In addition to the membership and role management APIs, ASP.NET 2.0 offers several server controls that make programming security-related aspects of a Web application easier than ever: Login , LoginName, LoginStatus, LoginView, PasswordRecovery, ChangePassword, and CreateUserWizard. These are composite controls, and they provide a rich, customizable user interface. They encapsulate a large part of the boilerplate code and markup you would otherwise have to write repeatedly for each Web application you developed. Figure 15-13 offers a comprehensive view of the membership platform and illustrates the role of the login controls.
Figure 15-13: The big picture of ASP.NET membership and login controls.
An application based on the Forms authentication model always needs a login page. Aside from the quality of the graphics, all login pages look alike. They contain a couple of text boxes (for user name and password), a button to validate credentials, plus perhaps a Remember Me check box, and possibly links to click if the user has forgotten his or her password or needs to create a new account. The Login control provides all this for free, including the ability to validate the user against the default membership provider.
The Login control is a composite control that provides all the common user interface elements of a login form. Figure 15-14 shows the default user interface of the control. To use it, you simply drop the control from the toolbox onto the Web form, or you just type the following code:
<asp:login runat="server" />
Figure 15-14: The Login control in action.
The Login control also has optional user interface elements for functions such as password reminder, new user registration, help link, error messages, and a custom action in case of a successful login. When you drop the control onto a Visual Studio .NET 2005 form, the Auto-Format verb lets you choose among a few predefined styles, as in Figure 15-15.
Figure 15-15: The predefined styles of the Login control.
The appearance of the control is fully customizable through templates and style settings. All user interface text messages are also customizable through properties of the class.
The control is modularized, and each constituent part can be individually customized. The parts include the Username and Password text boxes, the Submit button, the button to create a new user, the Remember Me check box, and instructions with guidance to the user.
If you don't like the standard user interface of the control, you can define your own template, too:
<asp:login runat="server" > <layouttemplate> ... </layouttemplate> </asp:login>
Your template can include new elements, and you can recycle default components. To do the latter, you should use the same ID for the controls as in the default template. To simplify this operation, right-click on the control in the Visual Studio designer, choose Convert To Template, and switch to the Source view. The markup you see is the default template of the control expressed as ASP.NET code. Use it as a starting point for creating your own template.
The Login control fires the server events listed in Table 15-15.
Event | Description |
---|---|
Authenticate | Fires when a user is authenticated. |
LoggedIn | Fires when the user logs in to the site after a successful authentication. |
LoggingIn | Fires when a user submits login information but before the authentication takes place. At this time, the operation can still be canceled. |
LoginError | Fires when a login error is detected. |
In most common cases, though, you don't need to handle any of these events, nor will you likely find it necessary to programmatically access any of the numerous properties of the control.
The most common use for the Login control is to use it as a single-control page to set up the user interface of the login page for use with Forms authentication. The control relies entirely on the membership API (and the selected provider) to execute standard operations such as validating credentials, displaying error messages, and redirecting to the originally requested page in case of successful login.
If you have a provider with custom capabilities that you want to be reflected by the Login control, you need to modify the layout to add new visual elements bound to a code-behind method. In the code-behind method, you invoke the custom method on the custom provider.
The LoginName control is an extremely simple but useful server control. It works like a sort of label control and displays the user's name on a Web page:
<asp:loginname runat="server" />
The control captures the name of the currently logged-in user from the User intrinsic object and outputs it using the current style. Internally, the control builds a dynamic instance of a Label control, sets fonts and color accordingly, and displays the text returned by the following expression:
string name = HttpContext.Current.User.Identity.Name;
The LoginName control has a pretty slim programming interface that consists of only one property FormatString, which defines the format of the text to display. It can contain only one placeholder, as shown here:
myLogin.FormatString = "Welcome, {0}";
If Dino is the name of the current user, the code generates a "Welcome, Dino" message.
The LoginStatus control indicates the state of the authentication for the current user. Its user interface consists of a link button to log in or log out, depending on the current user logon state. If the user is acting as an anonymous user that is, he or she never logged in the control displays a link button to invite the user to log in. Otherwise, if the user successfully passed through the authentication layer, the control displays the logout button.
The LoginStatus control is often used in conjunction with the LoginName control to display the name of the current user (if any), plus a button to let the user log in or out. The style, text, and action associated with the button changes are conveniently based on the authentication state of the user.
The following code creates a table showing the name of the current user and a button to log in or log out:
<table width="100%" border="0"><tr> <td> <asp:loginname runat="server" FormatString="Welcome, {0}" /> </td> <td align="right"> <asp:loginstatus runat="server" LogoutText="Log off" /> </td> </tr> </table>
Figure 15-16 shows the results. The first screen shot demonstrates a page that invites a user to log in, while the second shows the LoginName and LoginStatus controls working together in the case of a logged-in user. To detect whether the current user is authenticated and adapt the user interface, you can use the IsAuthenticated property of the User object:
void Page_Load(object sender, EventArgs e) { if (User.Identity.IsAuthenticated) // Adjust the UI by outputting some text to a label Msg.Text = "Enjoy more features"; else Msg.Text = "Login to enjoy more features."; }
Figure 15-16: The LoginStatus control invites a user who is not currently logged in to log in; next, it displays more features reserved to registered users.
Although the LoginStatus control is quite useful in its default form, it provides a bunch of properties and events that you can use to configure it. The properties are listed in Table 15-16.
Property | Description |
---|---|
LoginImageUrl | Gets or sets the URL of the image used for the login link. |
LoginText | Gets or sets the text used for the login link. |
LogoutAction | Determines the action taken when a user logs out of a Web site. Possible values are Refresh, Redirect, and RedirectToLoginPage. Refresh reloads the current page with the user logged out. The other two redirect the user to the logout page or the login page, respectively. |
LogoutImageUrl | Gets or sets the URL of the image used for the logout button. |
LogoutPageUrl | Gets or sets the URL of the logout page. |
LogoutText | Gets or sets the text used for the logout link. |
The control also features a couple events LoggingOut and LoggedOut. The former fires before the user clicks to log off. The latter is raised immediately after the logout process has completed.
The LoginView control allows you to aggregate the LoginStatus and LoginName controls to display a custom user interface that takes into account the authentication state of the user as well as the user's role or roles. The control, which is based on templates, simplifies creation of a user interface specific to the anonymous or connected state and particular roles to which they are assigned. In other words, you can create as many templates as you need, one per state or per role.
Table 15-17 lists the properties of the user interface of the LoginView control.
Property | Description |
---|---|
AnonymousTemplate | Gets or sets the template to display to users who are not logged in to the application. |
LoggedInTemplate | Gets or sets the template to display to users who are logged in to the application. |
RoleGroups | Returns the collection of templates defined for the supported roles. Templates can be declaratively specified through the <roleGroups> child tag. |
Note that the LoggedInTemplate template is displayed only to logged-in users who are not members of one of the role groups specified in the RoleGroups property. The template (if any) specified in the <roleGroups> tag always takes precedence.
The LoginView control also fires the ViewChanging and ViewChanged events. The former reaches the application when the control is going to change the view (such as when a user logs in). The latter event fires when the view has changed.
The LoginView control lets you define two distinct templates to show to anonymous and logged-in users. You can use the following markup to give your pages a common layout and manage the template to show when the user is logged in:
<asp:loginview runat="server"> <anonymoustemplate> <table width="100%" border="0"><tr><td> To enjoy more features, <asp:loginstatus runat="server"> </td></tr></table> </anonymoustemplate> <loggedintemplate> <table width="100%" border="0"><tr> <td><asp:loginname runat="server" /></td> <td align="right"><asp:loginstatus runat="server" /></td> </tr></table> </loggedintemplate> </asp:loginview>
Basically, the LoginView control provides a more flexible, template-based programming interface to distinguish between logged-in and anonymous scenarios, as we did in the previous example by combining LoginStatus and LoginName.
The LoginView control also allows you to define blocks of user interface to display to all logged-in users who belong to a particular role. As mentioned, these templates take precedence over the <loggedintemplate> template, if both apply:
<asp:loginview runat="server"> <rolegroups> <asp:rolegroup roles="Admin"> <contenttemplate> ... </contenttemplate> </asp:rolegroup> <asp:rolegroup roles="Guest"> <contenttemplate> ... </contenttemplate> </asp:rolegroup> </rolegroups> </asp:loginview>
The content of each <contenttemplate> block is displayed only to users whose role matches the value of the roles attribute. You can use this feature to create areas in a page whose contents are strictly role-specific. For the LoginView control to work fine, role management must be enabled, of course. The control uses the default provider.
The PasswordRecovery control is another server control that wraps a common piece of Web user interface into an out-of-the-box component. The control represents the form that enables a user to recover or reset a lost password. The user will receive the password through an e-mail message sent to the e-mail address associated with his or her account.
The control supports three views, depending on the user's password recovery stage, as follows. The first view is where the user provides the user name and forces the control to query the membership provider for a corresponding membership user object. The second view is where the user must provide the answer to a predetermined question to obtain or reset the password. Finally, the third view is where the user is informed of the success of the operation.
For the control to work properly, you must first ensure that the selected membership provider supports password retrieval. The password retrieval also requires that the provider defines a MembershipUser object and implements the GetUser methods. Remember that the membership provider decides how to store passwords: clear text, hashed, or encrypted.
If passwords are stored as hashed values, the control doesn't work. Hash algorithms are not two-way algorithms. In other words, the hash mechanism is great at encrypting and comparing passwords, but it doesn't retrieve the clear text. If you plan to use the PasswordRecovery control, you must ensure that the provider stores password as clear text or encrypted data.
The PasswordRecovery control supports a child element named MailDefinition:
<asp:passwordrecovery runat="server"> <maildefinition from="admin@contoso.com" /> </asp:passwordrecovery>
The <MailDefinition> element configures the e-mail message and indicates the sender as well as the format of the body (text or HTML), priority, subject, and carbon-copy (CC). For the same settings, you can also use a bunch of equivalent properties on the associated Framework class and set values programmatically.
If the user who has lost the password has a question/answer pair defined, the PasswordRecovery control changes its user interface to display the question and ask for the answer before the password is retrieved and sent back. Figure 15-17 demonstrates the behavior of the control.
Figure 15-17: The PasswordRecovery control in action.
The control first asks the user to provide the user name; next, it retrieves associated information and displays the security question, if any is defined for the user. Finally, if an e-mail address is known, the control sends a message with details, as shown in Figure 15-18.
Figure 15-18: The e-mail message with password information.
The ChangePassword control provides an out-of-the-box and virtually codeless solution that enables end users to change their password to the site. The control supplies a modifiable and customizable user interface and built-in behaviors to retrieve the old password and save a new one:
<asp:ChangePassword runat="server" />
The underlying API for password management is the same membership API we discussed earlier in this chapter.
The ChangePassword control will work in scenarios where a user might or might not be already authenticated. However, note that the User Name text box is optional. If you choose not to display the user name and still permit nonauthenticated users to change their passwords, the control will always fail.
If the user is not authenticated but the User Name text box is displayed, the user will be able to enter his or her user name, current password, and new password at the same time.
The change of the password is performed using the ChangePassword method on the MembershipUser object that represents the user making the attempt. Note that the provider might pose an upper limit to the invalid attempts to change or reset the password. If set, this limit affects the ChangePassword control. The control won't work any longer once the limit has been exceeded.
Once the password has been successfully changed, the control might send if properly configured a confirmation e-mail to the user, as shown in Figure 15-19.
Figure 15-19: The ChangePassword control in action.
The e-mail message is configured through the same <MailDefinition> element we saw earlier for the PasswordRecovery control.
The Continue button points the page with the control to a new destination URL to let users continue working. If you don't set the ContinuePageDestinationUrl property, clicking the button simply refreshes the current page.
The CreateUserWizard control is designed to provide a native functionality for creating and configuring a new user using the membership API. The control offers a basic behavior that the developer can extend to send a confirmation e-mail to the new user and add steps to the wizard to collect additional information such as address, phone number, or perhaps roles.
Customization is supported in two ways: by customizing one of the default steps, and by adding more user-defined steps. Figure 15-20 shows the control in action in the Add User page of the WSAT tool.
Figure 15-20: The CreateUserWizard control in action within WSAT.
The difference between this control and the CreateUser method on the membership provider is that the method just adds the user name and password. The wizard provides a user interface and lets you add more information in a single shot.
How can we design and code secure ASP.NET applications? First of all, security is strictly related to the application's usage, its popularity, and the type of users who connect to it and work with it. Paradoxically, a poorly secured application that isn't attractive to hackers can be perceived as being much more secure than a well-armored application with just one loophole or two. Successful attacks are possible through holes in the system-level and application-level security apparatus. When it comes to security, don't look for a magic wand to do the job for you. Security is a state of mind, and insecurity is often the result of loose coding styles, if not true programming laziness. Never blindly trust anything regarding Web and ASP.NET security. Always keep in mind that security for Web applications is mostly about raising the bar higher and higher to make it hard for bad guys to jump over.
The following Patterns & Practices links can help you find great information to fend off most common types of attacks and implement effective input validation in ASP.NET 1.x and 2.0 applications:
How To Protect from Injection Attacks in ASPNET http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/paght000003.asp
How To Use Regular Expressions to Constrain Input in ASP.NET http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/paght000001.asp
How To Protect from SQL Injection in ASP.NET http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/paght000002.asp
How To Prevent Cross-Site Scripting in ASP.NET http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/paght000004.asp