How can I throttle user login attempts in PHP

You cannot simply prevent DoS attacks by chaining throttling down to a single IP or username. You can't even really prevent rapid-fire login attempts using this method.

Why? Because the attack can span multiple IPs and user accounts for the sake of bypassing your throttling attempts.

I have seen posted elsewhere that ideally you should be tracking all failed login attempts across the site and associating them to a timestamp, perhaps:

CREATE TABLE failed_logins (
    id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(16) NOT NULL,
    ip_address INT(11) UNSIGNED NOT NULL,
    attempted DATETIME NOT NULL,
    INDEX `attempted_idx` (`attempted`)
) engine=InnoDB charset=UTF8;

A quick note on the ip_address field: You can store the data and retrieve the data, respectively, with INET_ATON() and INET_NTOA() which essentially equate to converting an ip address to and from an unsigned integer.

# example of insertion
INSERT INTO failed_logins SET username = 'example', ip_address = INET_ATON('192.168.0.1'), attempted = CURRENT_TIMESTAMP;
# example of selection
SELECT id, username, INET_NTOA(ip_address) AS ip_address, attempted;

Decide on certain delay thresholds based on the overall number of failed logins in a given amount of time (15 minutes in this example). You should base this on statistical data pulled from your failed_logins table as it will change over time based on the number of users and how many of them can recall (and type) their password.


> 10 failed attempts = 1 second
> 20 failed attempts = 2 seconds
> 30 failed attempts = reCaptcha

Query the table on every failed login attempt to find the number of failed logins for a given period of time, say 15 minutes:


SELECT COUNT(1) AS failed FROM failed_logins WHERE attempted > DATE_SUB(NOW(), INTERVAL 15 minute);

If the number of attempts over the given period of time is over your limit, either enforce throttling or force all users to use a captcha (i.e. reCaptcha) until the number of failed attempts over the given time period is less than the threshold.

// array of throttling
$throttle = array(10 => 1, 20 => 2, 30 => 'recaptcha');

// retrieve the latest failed login attempts
$sql = 'SELECT MAX(attempted) AS attempted FROM failed_logins';
$result = mysql_query($sql);
if (mysql_affected_rows($result) > 0) {
    $row = mysql_fetch_assoc($result);

    $latest_attempt = (int) date('U', strtotime($row['attempted']));

    // get the number of failed attempts
    $sql = 'SELECT COUNT(1) AS failed FROM failed_logins WHERE attempted > DATE_SUB(NOW(), INTERVAL 15 minute)';
    $result = mysql_query($sql);
    if (mysql_affected_rows($result) > 0) {
        // get the returned row
        $row = mysql_fetch_assoc($result);
        $failed_attempts = (int) $row['failed'];

        // assume the number of failed attempts was stored in $failed_attempts
        krsort($throttle);
        foreach ($throttle as $attempts => $delay) {
            if ($failed_attempts > $attempts) {
                // we need to throttle based on delay
                if (is_numeric($delay)) {
                    $remaining_delay = time() - $latest_attempt - $delay;
                    // output remaining delay
                    echo 'You must wait ' . $remaining_delay . ' seconds before your next login attempt';
                } else {
                    // code to display recaptcha on login form goes here
                }
                break;
            }
        }        
    }
}

Using reCaptcha at a certain threshold would ensure that an attack from multiple fronts would be stopped and normal site users would not experience a significant delay for legitimate failed login attempts.


You have three basic approaches: store session information, store cookie information or store IP information.

If you use session information the end user (attacker) could forcibly invoke new sessions, bypass your tactic, and then login again with no delay. Sessions are pretty simple to implement, simply store the last known login time of the user in a session variable, match it against the current time, and make sure the delay has been long enough.

If you use cookies, the attacker can simply reject the cookies, all in all, this really isn't something viable.

If you track IP addresses you'll need to store login attempts from an IP address somehow, preferably in a database. When a user attempts to log on, simply update your recorded list of IPs. You should purge this table at a reasonable interval, dumping IP addresses that haven't been active in some time. The pitfall (there's always a pitfall), is that some users may end up sharing an IP address, and in boundary conditions your delays may affect users inadvertantly. Since you're tracking failed logins, and only failed logins, this shouldn't cause too much pain.


The short answer is: Do not do this. You will not protect yourself from brute forcing, you could even make your situation worse.

None of the proposed solutions would work. If you use the IP as any parameter for throttling, the attacker will just span the attack across a huge number of IPs. If you use the session(cookie), the attacker will just drop any cookies. The sum of all you can think of is, that there is absolutely nothing a brute forcing attacker could not overcome.

There is one thing, though - you just rely on the username that tried to log in. So, not looking at all the other parameters you track how often a user tried to log in and throttle. But an attacker wants to harm you. If he recognizes this, he will just also brute force user names.

This will result in almost all of your users being throttled to your maximum value when they try to log in. Your website will be useless. Attacker: success.

You could delay the password check in general for around 200ms - the website user will almost not notice that. But a brute-forcer will. (Again he could span across IPs) However, nothing of all this will protect you from brute forcing or DDoS - as you can not programatically.

The only way to do this is using the infrastructure.

You should use bcrypt instead of MD5 or SHA-x to hash your passwords, this will make decrypting your passwords a LOT harder if someone steals your database (because I guess you are on a shared or managed host)

Sorry for disappointing you, but all the solutions here have a weakness and there is no way to overcome them inside the back-end logic.


The login process needs reduce its speed for both successful and unsuccessful login. The login attempt itself should never be faster than about 1 second. If it is, brute force uses the delay to know that the attempt failed because success is shorter than failure. Then, more combinations can be evaluated per second.

The number of simultaneous login attempts per machine needs to be limited by the load balancer. Finally, you just need to track if the same user or password is re-used by more than one user/password login attempt. Humans cannot type faster than about 200 words per minite. So, successive or simultaneous login attempts faster than 200 words per minite are from a set of machines. These can thus be piped to a black list safely as it is not your customer. Black list times per host do not need to be greater than about 1 second. This will never inconvenience a human, but plays havoc with a brute force attempt whether in serial or parallel.

2 * 10^19 combinations at one combination per second, run in parallel on 4 billion separate IP addresses, will take 158 years to exhaust as a search space. To last one day per user against 4 billion attackers, you need a fully random alphanumeric password 9 places long at a minimum. Consider training users in pass phrases at least 13 places long, 1.7 * 10^20 combinations.

This delay, will motivate the attacker to steal your password hash file rather than brute force your site. Use approved, named, hashing techniques. Banning the entire population of Internet IP for one second, will limit the effect of parallel attacks without a dealy a human would appreciate. Finally, if your system allows more than 1000 failed logon attempts in one second without some response to ban systems, then your security plans have bigger problems to work on. Fix that automated response first of all.