Instead of hardwiring these controls to talk directly to the membership database, the ASP.NET team decided to introduce a provider model to abstract the data source from the controls that use it. You'll find this model in a lot of different places in ASP.NET,  but to make things more concrete, Figure 5-5 illustrates how the Membership provider works.
Figure 5-5. Provider architecture
This model is common in software design. It decouples the consumer of a service from the implementation of that service, or as the ASP.NET team refers to it, the
of the service. In this case, we're talking about
The ASP.NET infrastructure (such as the membership controls and the administration Web application)
Another nice benefit of this model is that you can write your own code that uses the Membership provider. We've included a tool called the ASP.NET Administration Console as an example with this book that does just this. You will find this code helpful in learning more about how the Membership and Role providers work. Besides finding it a helpful learning tool, you'll probably find that it
This abstract base class defines the interface for accessing a
Listing 5-5. MembershipProvider
The CreateUser method gives you a good idea of the type of user data tracked by membership. Each user has a name and password and an e-mail address. To allow users to reset their own passwords, there is also an optional question and answer that can be used as a secondary form of authentication for the user. Each user has a flag that indicates whether they are approved to log in. To ban a user, you can clear this flag and they will no longer be allowed to log in (the AD provider is serious about this; it disables the user account if you do this). If you ever call CreateUser directly, pass a null argument to
UpdateUser and DeleteUser are pretty self-explanatory, except perhaps for the deleteRelatedData argument. If you pass true, SqlMembershipProvider deletes all records
The presence of the UnlockUser method indicates that accounts can be locked out after a certain number of failed logon attempts. Account lockout policy depends on the provider. Under the AD provider, the number of failed password attempts is controlled by password policy in AD, while the SQL provider relies on the maxInvalidPasswordAttempts attribute specified in web.config.
ValidateUser is perhaps the most important method: this is how the login control (or even your own code) attempts to log on a user, supplying the user name and password. While the AD provider forwards this request on to a directory service, the SQL provider must actually perform the password validation itself against a password verifier stored in the membership database. We'll
Given that a user is much more likely to remember her e-mail address than to remember the user account name she chose to use at your Web site, the GetUserNameByEmail method can really come in handy. If the user can't figure out her user name, you can look it up for her based on her e-mail address. You could use this in conjunction with the events
You've now seen enough Membership plumbing to understand the login control, so let's digress for a moment to examine it in more detail.
The Login Control
The login control is very useful, as it allows you to crank out a login page with a single line of markup, as you saw at the beginning of this chapter, but you can use it other places as well. Imagine dropping a login control onto your master page that would let anonymous users browse your Web site and log in if they want more personalized service. Many Web sites work this way.
The login control is infinitely customizable. Using the designer, you can autoformat an instance of the control to give it a look you like, or you can centralize the look of all your login controls with a skin (see Chapter 2). You can also customize each bit of text shown,
Figure 5-6. Customizing the login control
Besides controlling the look and feel of this control, you'll need to make some important decisions about what features you want to expose to your users. The "Remember me
The login control will
The login control fires a sequence of events when the user presses the Login button. The first is the LoggingIn event, where you can preprocess the request and choose to allow or cancel the login before it's even attempted. The next event fired, Authenticate, is the
User Account Lockout: Blessing or Curse?
SqlMembershipProvider has a user account lockout feature that is designed to thwart password guessing attacks. Here's how it works: for a given user account, the first time ValidateUser is given an invalid password, a counter in the user's membership record (FailedPasswordAttemptCount) is bumped up, and the time of this first failed login is recorded (FailedPasswordAttemptWindowStart). If this happens five times in rapid succession (within ten minutes by default), the user's account will be locked out. You can modify these thresholds in web.config via the maxInvalidPasswordAttempts and passwordAttemptWindow attributes on the provider.
ActiveDirectoryMembershipProvider indirectly provides the same service, because directory services typically provide optional account lockout features. For example, if you're using AD as your user store, you can use the account lockout features in AD to lock user accounts after a certain number of failed login attempts. In both the SQL and AD provider cases, an administrator must intervene and unlock the user's account before she will be allowed to log in again. This is when the UnlockUser method comes in handy.
Before you get too excited about account lockout, consider the dark side. While it's hard for an attacker to guess a correct password for a user, it's trivial to guess an incorrect password. If I don't like Alice, and I want to make sure she can't log in to your Web site, all I have to do is use an incorrect password in several login attempts in rapid succession. And this attack can be automated. Imagine the pain you'd be in if an attacker learned the
You can effectively
A more useful feature might be a delay, as you see when you try to interactively log in to Windows. If you supply around five bad passwords to the interactive Windows login screen, you'll have to wait a little bit before you can try again. The system simply slows you down every four or five
Password Complexity Policy
Ultimately the best protection against password guessing attacks is to require strong passwords. There are several ways to enforce this password policy with the membership system. The first is through the configuration of your Membership provider. You can modify the following provider attributes to provide some general constraints on password complexity.
There's also a passwordStrengthRegularExpression attribute if you're comfortable using regular expressions. You can find an example of a regular expression that controls password length and requires the use of upper and lower
Each time a password is updated via the membership service, the ValidatingPassword event is fired. Don't let the name confuse you: this event isn't fired when a user is authenticated, only when his password is being changed. If you handle this event, you will be given the proposed new password, and you can decide whether or not it is acceptable. You could plug in code to do your own dictionary attack against the password, or add in a password history database to ensure that users aren't reusing old passwords.
If you are using the AD provider, your directory service's password policy will act as a yet another complexity check. Passwords will only be accepted if they pass both the membership password complexity constraints and the directory service's constraints.
The MembershipProvider class includes several