First I just have to say there’s basically no way to stop a brute force attack and that all it does is penalize a legit users. A person who is trying to hack in a website isn’t going to be using one user account and/or computer to hack in. Anyways this is the best I could come up with :
[php]if (isset($submit) && $submit === ‘login’) {
$username = trim(filter_input(INPUT_POST, ‘username’, FILTER_SANITIZE_FULL_SPECIAL_CHARS));
$password = filter_input(INPUT_POST, ‘password’, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
if (preg_match('/^[ \w]+$/', $username) && (strlen($username) > 0)) {
$throttle_delay = throttle_failed_logins($username);
if ($throttle_delay > 0) {
$message = "Too many login attempts. ";
$message .= "You must wait " . $throttle_delay . " minutes before you can attempt another login.";
} else {
$result = $users->read($username, $password);
if ($result) {
clear_failed_logins($username);
after_successful_login();
} else {
record_failed_login($username);
}
}
} else {
unset($username);
}
}[/php]
here are my functions ->
[php]<?php
use Library\Database\Database as DB;
function find_username($username) {
$db = DB::getInstance();
$pdo = $db->getConnection();
$query = ‘SELECT id, username, count, last_time FROM users WHERE username=:username’;
$stmt = $pdo->prepare($query);
$stmt->execute([’:username’ => $username]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result;
}
function update_failed_logins_table(array $failed_login) {
$db = DB::getInstance();
$pdo = $db->getConnection();
$query = ‘UPDATE users SET username=:username, count=:count, last_time=:last_time WHERE id=:id’;
$stmt = $pdo->prepare($query);
$stmt->execute([’:username’ => $failed_login[‘username’], ‘:count’ => $failed_login[‘count’], ‘:last_time’ => $failed_login[‘last_time’], ‘:id’ => $failed_login[‘id’]]);
}
function record_failed_login($username) {
$failed_login = find_username($username);
if (!$failed_login) {
if (isset($_SESSION['username'])) {
$_SESSION['count'] = $_SESSION['count'] + 1;
$_SESSION['last_time'] = time();
} else {
$_SESSION['username'] = $username;
$_SESSION['count'] = 1;
$_SESSION['last_time'] = time();
}
} else {
// existing failed_login record
$failed_login['count'] = $failed_login['count'] + 1;
$failed_login['last_time'] = time();
update_failed_logins_table($failed_login);
}
return true;
}
function clear_failed_logins($username) {
$failed_login = find_username($username);
if ($failed_login) {
$failed_login['count'] = 0;
$failed_login['last_time'] = time();
update_failed_logins_table($failed_login);
} else {
unset($_SESSION['username']);
unset($_SESSION['count']);
unset($_SESSION['last_time']);
session_destroy();
}
return true;
}
// Returns the number of minutes to wait until logins
// are allowed again.
function throttle_failed_logins($username) {
$throttle_at = 500;
$delay_in_minutes = 5;
$delay = 60 * $delay_in_minutes;
$failed_login = find_username($username);
// Once failure count is over $throttle_at value,
// user must wait for the $delay period to pass.
if ($failed_login && $failed_login['count'] >= $throttle_at) {
$remaining_delay = ($failed_login['last_time'] + $delay) - time();
$remaining_delay_in_minutes = ceil($remaining_delay / 60);
return $remaining_delay_in_minutes;
} elseif (!$failed_login && isset($_SESSION['username']) && (int) $_SESSION['count'] > $throttle_at) {
$remaining_delay = ($_SESSION['last_time'] + $delay) - time();
$remaining_delay_in_minutes = ceil($remaining_delay / 60);
return $remaining_delay_in_minutes;
} else {
return 0;
}
}[/php]
If you notice I put my attempts at 500 basically disabling this feature for like I said it’s practically impossible stop a brute force attack. Here’s an online security scanner to validate in what I’m saying -> https://online.acunetix.com/#/dashboard/
Trust me I have tried and even research a solutions. The only thing I came up with was one reputable website basically saying don’t even bother in trying.
That is why I never try to write my own processing payment system and let the experts handle that. Whoever writes online banking applications have to really know their stuff.