views:

315

answers:

5

I have a login system requiring a username and a password. I want to display a captcha after a certain amount of failed login attempts. What is the proper way to implement this? I've read around on this site and some solutions suggest having a 'failed-attempts-count' added to the users table. However, I need the failed attempts to not be tied to a certain user - i.e. I'd like the captcha to be displayed regardless of whether or not the username that was entered exists in the system. Would storing this in a session variable be ok (I am using PHP)? If so, is there no downside to just throwing data as needed into session variables? I already have any a session id for every visitor on the site (either logged in or not) so I could create a table that relates login-attempts to this session id...any ideas on what the best / most secure approach is? Thanks.

Update: from the answers thus far, it seems like session ID is not the best idea since the hacker could simply clear his/her cache (but is this really an issue because wouldnt this slow down the brute force attack enough to render it useless?). The other option is by IP...but I am hesitant for users under an intranet or proxy since failed attempts will be shared....I can't really think of any other methods..can you?

+1  A: 

The session approach won't work if the hacker closes his browser and reopens it every try, so a table storing the number of failed attempts and the time of the last attempt (so you can check if say an hour passed so you can reset the counter) per user would be the safest way.

Blindy
right, but I figured the idea of the captcha in the first place is to hinder automated brute force attacks..so wouldn't closing the browser or clearing the cache after every attempt solve the problem in itself?
es11
and you say to store the failed attempts per user - does this imply that the failed attempts counter will only get incremented if a valid user id is entered (and will not if both the user id and password are invalid)?
es11
well automated brute force attacks could easily destroy the WebClient object and create a new one for every try. about the 2nd part, you could implement a 2nd fall back security layer inside a session object in case the user id is invalid and work off that. note however that storing hacking attempts on a per-user basis lets you do more than just ban an ip address, it lets you actually change the pw and email it to the user's email address (i saw blizzard do this before).
Blindy
+4  A: 

The danger of using a session ID is that someone who writes a brute force attack can just clear his cookies with each attempt and thus giving him a new session.

Keep in mind that an automated brute force attack can be written in a scripting language outside of a browser that could manipulate the cookies sent for each request.

Another way to do this would be to create a table with user source IP's and add the counter there. This will inconvenience users using a proxy server though. But at least you will catch those trying to repeatedly guess passwords from the same location.

UPDATE: Having to clear cookies during successive brute force attempts would not slow down the attack as this process would be automated and happen almost instantly. Cookie manipulation in these types of attacks is quite common. Modifying a cookie is not the same as clearing your browser's cache (which typically takes a while because it needs to delete a bunch of files). All the attacker needs to do is prevent a cookie from being sent.

Andre Miller
+1  A: 

You can log all failed attempts with a IP and Time. Old failed attempts get deleted after a certain time, and if there are more then a certain amount of failed attempts for a given IP display the captcha.

Moak
A: 

install APC http://www.php.net/apc or memcache(d) http://www.php.net/memcache or here http://www.php.net/memcached (memcache also needs a memcached server to be installed, see here http://www.danga.com/memcached/), then use the appropriate increment commands for bad login attempts from an IP address with a timeout of whatever suites you (5 minutes, 30 minutes, etc...). this will allow you to QUICKLY determine if a brute force attempt is happening (without worries of race conditions) and automatically expire the block after a determinate amount of time.

APC example:

$max_attempts = 5;  // max attempts before captcha
$attempts = apc_fetch('login_attempts_'.$ip));
if($attempts and $attempts>$max_attempts){
    // block code here or redirect, captcha etc... also suggest a short sleep time to delay answer, slow down bot
}else{
    // check login here, run next code if login fails
    if($login_failed){
        if(!$attempts){
            apc_store('login_attempts_'.$ip,1,$timeout);
        }else{
            // function NOT currently documented on php.net, increments number stored in key
            apc_inc('login_attempts_'.$ip);
        }
    }
}

of course it is a very rough example... but you get the idea

Jason
A: 

The most accurate information you can get is their IP address. DOn't use session cookies if you want it to be accurate, since the user may simply ignore your session cookie (ie. using curl). But if you are concerned about shared ips, you could try to include additional information, such as the browser agent or use ajax to pass back other information which you are unable to get otherwise. But, everything else other than the IP address can be faked (even then, you can use proxies).

Hans