views:

1259

answers:

5

(This is in principal a language-agnostic question, though in my case I am using ASP.NET 3.5)

I am using the standard ASP.NET login control and would like to implement the following failed login attempt throttling logic.

  • Handle the OnLoginError event and maintain, in Session, a count of failed login attempts
  • When this count gets to [some configurable value] block further login attempts from the originating IP address or for that user / those users for 1 hour

Does this sound like a sensible approach? Am I missing an obvious means by which such checks could be bypassed?

Note: ASP.NET Session is associated with the user's browser using a cookie

Edit

This is for an administration site that is only going to be used from the UK and India

+3  A: 

This could possibly effect your genuine users too. For ex. in countries like Singapore there are limited number of ISPs and a smaller set of IPs which are available for home users.

Alternatively , you could possibly insert a captcha after x failed attempts to thwart script kiddies.

Learning
captcha is a good idea, but in reality how many people try more than 30 times to login to an account before doing a password reset :)
Dead account
+2  A: 

I think you'll need to keep the count outside the session - otherwise the trivial attack is to clear cookies before each login attempt.

Otherwise a count and lock-out is reasonable - although an easier solution might be to have a doubling-timeout between each login failure. i.e. 2 seconds after first login attempt, 4 seconds after next, 8 etc.

You implement the timeout by refusing logins in the timeout period - even if the user gives the correct password - just reply with human readable text saying that the account is locked-out.

Also monitor for same ip/different user and same user/different ip.

Douglas Leeder
Vunerable to botnet attack?
Dead account
+3  A: 

Jeff Atwood mentioned another approach: Rather than locking an account after a number of attempts, increase the time until another login attempt is allowed:

1st failed login    no delay
2nd failed login    2 sec delay
3rd failed login    4 sec delay
4th failed login    8 sec delay
5th failed login    16 sec delay

That would reduce the risk that this protection measure can be abused for denial of service attacks.

See http://www.codinghorror.com/blog/archives/001206.html

Gumbo
If I have a list of 1000 usernames, I'll try user1+password1, user2+pasword1, user3+password1... user1+password2, user2+password2
Dead account
In ASP.NET, how and where would you implement the delay? Presumably it would have to be done server-side in Page_Load using a Thread.Sleep(x).
Richard Ev
a link for Jeff Atwood? :)
Learning
I’m not familiar with ASP.NET. But I would store this information in a database (failedLoginAttemptsInARow, lastLoginAttempt) and could then calculate the time. When the login was successful, I just reset the counter.
Gumbo
No worries, thanks again Gumbo
Richard Ev
I will use this approach, and store the number of failed logins per user in the database. Then I insert a delay of 2^(n-1) seconds at failed logins.
Christian Davén
+5  A: 

The last thing you want to do is storing all unsuccessful login attempts in a database, that'll work well enough but also makes it extremely trivial for DDOS attacks to bring your database server down.

You are probably using some type of server-side cache on your webserver, memcached or similar. Those are perfect systems to use for keeping track of failed attempts by IP address and/or username.  If a certain threshold for failed login attempts is exceeded you can then decide to deactivate the account in the database, but you'll be saving a bunch of reads and writes to your persisted storage for the failed login counters that you don't need to persist.

If you're trying to stop people from brute-forcing authentication, a throttling system like Gumbo suggested probably works best.  It will make brute-force attacks uninteresting to the attacker while minimizing impact for legitimate users under normal circumstances or even while an attack is going on.  I'd suggest just counting unsuccessful attempts by IP in memcached or similar, and if you ever become the target of an extremely distributed brute-force attack, you can always elect to also start keeping track of attempts per username, assuming that the attackers are actually trying the same username often.  As long as the attempt is not extremely distributed, as in still coming from a countable amount of IP addresses, the initial by-IP code should keep attackers out pretty adequately.

The key to preventing issues with visitors from countries with a limited number of IP addresses is to not make your thresholds too strict; if you don't receive multiple attempts in a couple of seconds, you probably don't have much to worry about re. scripted brute-forcing.  If you're more concerned with people trying to unravel other user's passwords manually, you can set wider boundaries for subsequent failed login attempts by username.

One other suggestion, that doesn't answer your question but is somewhat related, is to enforce a certain level of password security on your end-users.  I wouldn't go overboard with requiring a mixed-case, at least x characters, non-dictionary, etc. etc. password, because you don't want to bug people to much when they haven't even signed up yet, but simply stopping people from using their username as their password should go a very long way to protect your service and users against the most unsophisticated – guess why they call them brute-force ;) – of attacks.

Dirk Stoop
+3  A: 

The accepted answer, which inserts increasing delays into successive login attempts, may perform very poorly in ASP.NET depending on how it is implemented. ASP.NET uses a thread pool to service requests. Once this thread pool is exhausted, incoming requests will be queued until a thread becomes available.

If you insert the delay using Thread.Sleep(n), you will tie up an ASP.NET thread pool thread for the duration of the delay. This thread will no longer be available to execute other requests. In this scenario a simple DOS style attack would be to keep submitting your login form. Eventually every thread available to execute requests will be sleeping (and for increasing periods of time).

The only way I can think of to properly implement this delay mechanism is to use an asynchronous HTTP handler. See Walkthrough: Creating an Asynchronous HTTP Handler. The implementation would likely need to:

  1. Attempt authentication during BeginProcessRequest and determine the delay upon failure
  2. Return an IAsyncResult exposing a WaitHandle that will be triggered after the delay
  3. Make sure the WaitHandle has been triggered (or block until it has been) in EndProcessRequest
Michael Petito
Couldn't you store somewhere the last login attempt time, and check to make sure the new attempt's time is `> [time]` since the last attempt?
goldenratio
Yes you could return immediately with a failure message based on the timing. It depends on the behavior you want.When I first read Gumbo's response regarding delays, I thought that the intended approach was to insert a delay before returning the login result to the user. Artificial delays like this are commonly used to reduce the impact of brute force login attempts.My only point was to warn users about sleeping thread pool threads to accomplish "delay" behavior.
Michael Petito