views:

420

answers:

3

I am trying to write a script to prevent brute-force login attempts in a website I'm building. The logic goes something like this:

  1. User sends login information.
  2. Check if username and password is correct
    • If Yes, let them in.
    • If No, record a failed attempt in the database. Check if there's too many fails within a given timeframe (eg: 5 in 5 minutes):
      • If Yes, then pause execution for 10 seconds: sleep(10), then report a login failure to the user.
      • Report a login failure to the user immediately

Explaining this to a co-worker, I was asked how this would help if a hacker sent, say, 1000 requests in one second. Would the first 5 would return immediately, and then the remaining 995 all take only 10 seconds?

I have a sneaking suspicion that I don't fully understand how HTTP works - is that situation above even possible, or is there a limit to the number of concurrent requests that a server will handle from one client?

Would a better solution be to have an increasing sleep time?

sleep($numRequestsInLast5Minutes - 5)

So the first 5 would be fast, and then every subsequent one would increase the sleep.

A: 

I am not sure what the best practice is, but when dealing with DOS attacks, a better strategy is to actually divert traffic away from your server. Setting timeouts actually wont help you because your still processing the request and running your php.

Have you considered setting up another web server running a simpler stripped down version of your login page. When the user tries too many times (like thousands of times), send a message to configure your router and redirect this user to the second web server.

Its like when websites get hit with the slashdot effect, many of them just redirect traffic away until the time the traffic reduces.

Andrew Keith
+2  A: 

I would suggest if the user has tried unsuccessfully, say more than five times and five minutes, you start returning a 503 Service Unavailable immediately, for that IP address. When a login fails, you could use memcache to get the current bad attempts for an IP, and then increment the amount, and save it back to memcache with a 5 minute expiry.

You don't want to put a sleep in your PHP code, as that will allow a single user to create lots of connections to your web server, and potentially bring down other users.

Since the user hasn't logged in, you don't have a session cookie, and if the user is trying to brute force their way into an account, they may not present a cookie at all.

brianegge
sleep "...will allow a single user to create lots of connections". Do you have any sources for that? It's not that I don't believe you, I just would like to read some more about it.
nickf
@nickf: well, you don't really need to read a research paper to understand the predicament of using sleep() in this situation. Using sleep() basically pauses the execution of the script, meaning the connection is still open until it continues running. There are limits on how many concurrent connections your server can allow so with sleep() you technically make it easier for hackers to DoS your server ...
Lukman
A: 

I have used something like this

  • Check username and password
    • If no match then, record last failed login time for that combo and number of failed logins. Each fail makes the wait between being able to login something like failsCount * 30 seconds.

INFO

  • This means a brute force attack will exponentially take longer and longer.
  • It could lock a user out - but it will not count a failed login whilst trying to login during the lockout period. This should minimise it.

I've developed this but not released it into the wild yet, so any feedback would be appreciated.

alex
do the failed logins expire? that is, if 2 months ago I forgot my password and tried 6 different things, and now I typed the wrong password, would I be forced to wait 3 minutes before trying again?
nickf
Umm I think I've done something like `if (timeNow - timeFailed) > 3600` then reset count.
alex