Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to delay login attempts after too many tries (PHP)

I'm working on a fairly large web site built in PHP that will potentially have a lot of users. I'm looking into a way to protect the login screen from automated attempts. I have already included a CAPTCHA check on the registration form, yet want to harden the site more.

There have been similar questions on StackOverflow that I know of, and I know I'm capable of implementing this myself from scratch (storing login attempts and their time in the db), yet I dislike that path:

  • Conceptually, I think this kind of logic belongs at the web server/infrastructure level, not the application level. I dislike having this logic and complexity in my application
  • I worry about performance, particularly at the database level.
  • I'm lazy, in a good way, by not wanting to build a common utility like this from scratch

Any advise is appreciated, I think that I'm particularly looking for some kind of Apache module that can do this. My platform is PHP5 (using CodeIgniter), Apache2, MySQL 5.

like image 709
Fer Avatar asked May 20 '09 07:05

Fer


People also ask

How do I limit the number of login attempts in PHP?

First, we will create an HTML form with two fields i.e username and password. We will store the IP address in a variable and also create a variable for time. $time=time()-30; // Here you can chnage the attempt lock time. We using 30 here because after 3 failed login attempt, user can't login for 30 second.

Why is it necessary to limit the number of login attempts within a fixed period of time?

Sometimes the hacker might think they know your password, or they might develop a script to guess your password. In that case what you need to do is limit the login attempts. Limiting the failed login attempts will lock a user out if they entered the wrong password more than the specified time.

How do I unlock too many failed login attempts WordPress?

You need to access your site using your FTP client or file manager, and then go to the /wp-content/plugins/ folder. Once you're there, you can simply delete the limit-login-attempts-reloaded plugin folder. You can now log in to your WordPress admin area.


2 Answers

update: do not use sleep() for rate limiting! this doesn't make sense at all. i don't have a better solution on hand.


a good start would be to just sleep(1); after a failed login attempt - easy to implement, almost bug-free.

1 second isn't much for a human (especially because login attempts by humans don't fail to often), but 1sec/try brute-force ... sloooow! dictionary attacks may be another problem, but it's in the same domain.

if the attacker starts too may connections to circumvent this, you deal with a kind of DOS-attack. problem solved (but now you've got another problem).

some stuff you should consider:

  • if you lock accounts soley on a per IP basis, there may be problems with private networks.
  • if you lock accounts soley on a username basis, denial-of-service attacks agains known usernames would be possible
  • locking on a IP/username basis (where username is the one attacked) could work better

my suggestion: complete locking is not desireable (DOS), so a better alternative would be: count the login attempts for a certain username from a unique IP. you could do this with a simple table failed_logins: IP/username/failed_attempts

if the login fails, wait(failed_attempts); seconds. every xx minutes, run a cron script that decreases failed_logins:failed_attempts by one.

sorry, i can't provide a premade solution, but this should be trivial to implement.

okay, okay. here's the pseudocode:

<?php
$login_success = tryToLogIn($username, $password);

if (!$login_success) {
    // some kind of unique hash
    $ipusr = getUserIP() . $username;

    DB:update('INSERT INTO failed_logins (ip_usr, failed_attempts) VALUES (:ipusr, 1) ON DUPLICATE KEY UPDATE failed_logins SET failed_attempts = failed_attempts+1 WHERE ip_usr=:ipusr', array((':ipusr' => $ipusr));

    $failed_attempts = DB:selectCell('SELECT failed_attempts WHERE ip_usr=:ipusr', array(':ipusr' => $ipusr));

    sleep($failed_attempts);
    redirect('/login', array('errorMessage' => 'login-fail! ur doin it rong!'));
}
?>

disclaimer: this may not work in certain regions. last thing i heard was that in asia there's a whole country NATed (also, they all know kung-fu).

like image 114
stefs Avatar answered Oct 12 '22 22:10

stefs


A very dummy untested example, but I think, you will find here the main idea ).

if ($unlockTime && (time() > $unlockTime))
{
    query("UPDATE users SET login_attempts = 0, unlocktime = 0 ... ");
}
else
{
   die ('Your account is temporary locked. Reason: too much wrong login attempts.');
}
if (!$logged_in)
{
    $loginAttempts++;
    $unlocktime = 0;
    if ($loginAttempts > MAX_LOGIN_ATTEMPTS) 
    {
        $unlockTime = time() + LOCK_TIMEOUT;
    }
    query("UPDATE users SET login_attempts = $loginAttempts, unlocktime = $unlocktime ... ");
}

Sorry for the mistakes - I wrote it in some seconds ad didn't test... The same you can do by IP, by nickname, by session_id etc...

like image 29
Jet Avatar answered Oct 12 '22 23:10

Jet