For that purpose, I have tried the following class code below (sourced from a web search some time ago). It creates a new basic table where IP, Username and a TimeStamp are entered.
The User IP is blocked correctly after X attempts and the block is automatically released (entries for that IP address deleted from the database) on reload of the login page after the set amount of time has expired.
It does work as it is - but it does also have significant limitations/issues and I'm hoping somebody might be able to help refine the process.
Problem 1: an "attempt" entry is made to the database for the IP address EVERY time the login page is LOADED. No need for form submit required to register an attempt. That creates a potential problem where there are multiple Users on the same IP address.
Problem 2: I'd like to use the 'username' variable from the login form input but it appears that $_POST variables are no longer available from the login page (since resources/lib/Authentication.php was introduced in V5.97?)? Tried print_r($_POST) on the login page results Array(). var_dump for the page also results in an empty string? Tried $_Request and (Request::val('username')) but still no luck?
Here's the code added to the top of the login page
Code: Select all
<?php
$currDir = dirname(__FILE__);
require_once("${currDir}/hooks/BruteForceClass.php");
$bf = new framework_BruteForce();
$conn = mysqli_connect("localhost", "my-username", "my-password", "my-database");
$bf->brute_force($conn,300,5,1200);
?>
Code: Select all
<?php
class framework_BruteForce {
// $conn - sql connection
// $interval - the duration we are checking (currently 5 minutes or 300 seconds)
// $count - how many interactions are we allowing in the interval (currently 5)
// $timeRelease - how long Username is blocked (currently 20 minutes or 1200 seconds)
public function brute_force($conn, $interval, $count, $timeRelease) {
// does the table exist? If not, create it
if (!mysqli_query($conn, 'select 1 from `brute_force`')) {
$sql = "CREATE TABLE `brute_force` (
`ip_address` VARCHAR(20) NOT NULL,
`username` VARCHAR(20) NOT NULL,
`timestamp` datetime)";
mysqli_query($conn,$sql);
}
// get username
$username = HOW TO GET USERNAME FROM LOGIN FORM HERE;
// insert a record for the current interaction
$sql = sprintf("INSERT INTO `brute_force` (ip_address,username,timestamp) VALUES('%s','$username',CURRENT_TIMESTAMP)",
mysqli_real_escape_string($conn,$_SERVER['REMOTE_ADDR']));
mysqli_query($conn,$sql);
// clean up any items older than our defined interval
mysqli_query($conn,"DELETE FROM `brute_force` where TIMESTAMPDIFF(SECOND,timestamp,CURRENT_TIMESTAMP) > $timeRelease");
// check if we have a number of items remaining
$sql = sprintf("SELECT COUNT(*) as COUNT from `brute_force` where ip_address = '%s'",
mysqli_real_escape_string($conn,$_SERVER['REMOTE_ADDR']));
$result = mysqli_query($conn,$sql);
$row = mysqli_fetch_assoc($result);
$ipAdd = $_SERVER['REMOTE_ADDR'];
// if we do, show the block message
if($row['COUNT'] > $count) {
echo "Your IP: $ipAdd has been temporarily blocked.<br>Too many failed login attempts detected.<br>Please try again in 20 minutes or<br>contact your Database System Admin if you require a password reset";
exit(0);
}
}
}
?>
1. Check for Form submission first (isset) to stop the code from executing immediately on page load
2. Check that a 'username' has been entered to the login form <input> and POST that 'username' to the /hooks/BruteForceClass.php
Even more ideally - as I use reCaptcha to limit actual brute force attacks, check if the submitted 'username' is in the membership_users table and if not - the block could be missed.
Any help or advice most appreciated.