Thursday, November 29, 2007

User Lockout Pattern in the Membership Provider of ASP.NET 2.0+

With the Provider Framework of ASP.NET 2.0, the Membership Provider was born. All MembershipProviders contain a facility for locking out users when an account login has failed for a certain number of times. You can even specify over what period of time that number is counted up - all in the name of locking! From the article:

"Once locked out, a user may not log in again until his or her account is unlocked."

Why lock user accounts?

The mechanism for locking user accounts is present so that multiple, repeated attempts at a user's account cannot be made by an automated system using a brute-force "attack" to gain access to that user's account within that system.

When does locking user accounts make for problems?

When, you say? Often enough, if you as a web site administrator are at a distance from your users (ie. they're somewhere out on the Internet and not in your office), or if you're that same administrator and are at a distance with your web site (it's not part of your day-to-day), and don't log into it everyday and don't want to deal with these repeated login problems.

The first problem, from what I can see, is that there is no built-in (easy) way to detect/inform the web site user that their account has been locked. And although you might argue this is a security feature (to hide the information from the white hats and the black hats and also all the rest of us), the user is not aware they need to do anything special, and they may not be aware of the login lockout policy that you as an administrator are purveying. So you could end up with frustrated users! And also with frustrated administrators because they (likely) cannot see the users' locked-out status.

What Microsoft doesn't exactly tell us flat-out is that once a user is locked, they can't reset their password using the public reset password form. If you've left all the defaults, your users will simply receive the "We were unable to access your information. Please try again." error message when their account is locked. This is another security "feature" that hides the root cause of the problem from the user.

The second problem is that by having this user lockout feature, you actually leave your web site open to a denial-of-service (DoS) attack. If someone knows a username, they can just punch in random passwords and lock up an account. (This is actually how I tested the unlock feature I was developing when I needed to lock an account.) Not good!

Any Solutions?

Well, the first solution is obviously to configure away the user lockout feature, and set the maxInvalidPasswordAttempts to some really high number, or the passwordAttemptWindow to some really small number, if not both.

I've run into some solutions that implement a custom membership provider that automatically unlocks user accounts. That's not too bad, but the optics are still a bit lacking for the users of the web site. To the user, there is still some sort of magical reason why they can't log in, even if they are using the right password. Hmmph.

Where I was going next with my solution that I was developing was to inform the user of their new locked-out status via email just as the lockout happened. I didn't explore this enough to implement it, but I suspect that the OnLoginError event would be a good place to check the IsLockedOut status of that user. Even then - now you've sent them an email, now what! Tell them to call some administrator? We're making work for ourselves so that we can be on the phone all day with our password-forgetting users? Hmmm!

What is the Ideal Solution?

Where I would really like to go with this is to configure away the lockout feature (as described above) and then to show the user a CAPTCHA image to be verified with each login. This would solve the problem of the automated attack, and it would also prevent the DoS attack that currently exists with the MembershipProviders.

CAPTCHA is however a bit of a nuissance for everyone, and in the case of accessibility, it can be as much of a bummer as being downright illegal. My approach to this would be... Give the user two or three attempts without CAPTCHA, and if they haven't figured it out by then, throw it onto the form so that the user now needs to prove that they are human. This article from Microsoft is on my screen but has yet to be read, looks interesting... An ASP.NET Framework for Human Interactive Proofs

Further than this, it would be nice to have it all wrapped up into a MembershipProvider so that the CAPTCHA component came bundled with and was configurable from within the MembershipProvider framework. If I code it (or find it) I'll post up the solution!

2 comments:

Pete Hurst said...

Hi, I found this article searching for information on the lockout feature, since I have just been locked out of my own account through password reset testing!

The solution you suggest sounds like a far neater and more user-friendly way. In terms of accessibility, many CAPTCHAs provide an audio test in addition to the visual one.

I'm wondering, have you implemented or found a membership provider like this yet?

Andy said...

I don't know, I don't like CAPTCHA that much - it might block machines, but often they block me too, and I'm normal sighted.

Personally, I'd just redesign my login forms to tell the user that they've been locked out, how long they have to wait to login again, and possibly how to get assistance. The first two parts of that you can find out from the MembershipUser and Membership classes respectively and output to the page, and the final part can just be a fixed page - heck, it might even just say 'wait 5 minutes and try again'.