I don't like writing open-ended questions but I have a quick security question. I have a PHP login script that shows a CAPTCHA on the third failed login attempts. The way I'm counting failed logins is by using session data. Is this secure? Is there a better way to do this? Maybe store the IP in a database table? I'm not really sure how bots and programs that try to use word list actually work.


  • 1
    captcha has long since been broken, it's now just an annoyance to users but no problem at all to bots. – jwenting May 30 '11 at 6:18

Every time you call session_start() php either creates or reads (if already exists) a cookie named PHPSESSID. This cookie stores a unique identifier for the browser session, its php's way of telling one session from the other. A bot can easily refresh this cookie with a new identifier on each request and mislead php into recognizing each failed login as a first time try, so no its not secure.

Exactly the same can be done with the IP, all $_SERVER variables can be manipulated. A clever bot can send different IP on each request. So, if you are battling a malicious bot, requests are unidentifiable.

Two solutions, an easy one and a paranoid one:

Easy solution

Add CAPTCHA by default on your login page.

Paranoid solution

Create a table in your database to log the timestamp of each and every request, regardless of IP or any other identifier. Whenever you get a request to your login page decide if you are going to show the CAPTCHA based on the elapsed time from the immediately previous request. Make sure to have a cron job (or manually) delete old rows from the table, so it won't get very big very soon.

You will have to chose a sane interval, based on the traffic of your site. Make sure the interval is configurable, cause hopefully your traffic will rise over time. The success or failure of the solution depends on choosing a balanced interval.

For extra paranoid points log and compare microtime().

Ordinarily a login script does not need a CAPTCHA, as the strain on the database to check two indexed fields against a table of a few thousand users shouldn't be much. But if your site does have more than a few thousand users, or you have valid reasons to suspect that a bot is targeting you the paranoid solution should be sufficient.


As Dean notes, faking the IP is not trivial, that's what I meant by a clever bot, didn't really thought it was valuable to elaborate further. There is a related question at serverfault.stackexchange with a detailed accepted answer on the matter. (Related articles at Symantec and Linux Security)

Of course it would be somewhat secure to go with an IP based solution, especially if your site is not likely to be an intended target (but how can you know that?). Extra things you have to factor in an IP based solution would be dynamic IPs and how to distinguish between firewalled users.

Personally, I always go with the paranoid approach, and never absolutely trust $_SESSION, $_COOKIE and $_SERVER variables.

  • 1
    The IP address is not provided by the client, it's extracted from the TCP connection, and cannot be (easily) forged. Unless you're talking about a botnet and the client is actually running on different hosts. – Dean Harding May 30 '11 at 8:33
  • @Dean You're absolutely right, I've added some info on ip spoofing in my answer. – yannis May 30 '11 at 8:56
  • Thanks, A Valid Session ID is required to login but I'm sure the bot could just refresh until they get a valid login. Really the best solution here is to enforce strick passwords and logins right? – Brooke. May 30 '11 at 15:54

What sort of abuse is most likely to occur: someone trying to break into a specific account, or someone trying to find a random login (trying various user names, probably obtained from a list of registered users.)

For the first, you could store login-attempts per user name. This also prevents login attempts through proxies (where IP wouldn't be the same all the time.) Session probably doesn't work - it probably will only affect the people that just made mistakes, anyone deliberately trying to enter will probably clear cookies anyway. To prevent this type, you could require to reactivate the account through a link send in email (note, I would set the limit a bit higher than three, since it's annoying. You could still combine it with a captcha - it will not stop someone very deliberate but it might help as a first line of defense and demotivate some script kiddies.)

For the second, you can't do much better than IP I'm afraid. (Do log requests, a pattern may emerge.) To prevent this set a minimal time between attempts (on IP or also on user name), initially like 1 request per second - nothing a normal user would notice, and after 3 attempts set it to 10s, after 10 attempts increment it to half a minute (and you can keep on incrementing.) (Finetune as needed - and consider the possibility that users may come from a closed network, so multiple users will have the same IP legitimately.)

I would advise against using a captcha every time, that just annoys the user with no benefit.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.