Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional ASP.NET Security - Jeff Ferguson

.pdf
Скачиваний:
28
Добавлен:
24.05.2014
Размер:
13.26 Mб
Скачать

Id |^/L

We will now have a look at each of these classes in turn.

The FormsAuthenticationModule HTTP Module

The forms authentication module is an HTTP module (a class that implements IHttpModule). It handles the Application_AuthenticateRequest event that fires with each page request. If a cookie that contains a forms authentication ticket exists, the forms authentication module decrypts the information and populates Context .User with a GenericPrincipal (creating an instance of Formsldentity for the Identity property).

We do not use FormsAuthenticationModule directly when we are implementing forms authentication - if our application is configured to use forms authentication, the module will be activated and will do its work with no prompting from us. It is, however, useful to be aware of what is going on with each page request.

The FormsAuthentication Class

Because FormsAuthenticationModule and FormsAuthentication have similar names, it is easy to get them confused. It is important to remember that the FormsAuthenticationModule is an HTTP module that works in the background while FormsAuthentication is a class that exposes a variety of useful methods and properties that we will be using to implement forms authentication.

We will not need to use all of these methods and properties when we implement simple forms authentication: many of them only come into play when we want to customize the behavior of forms authentication. We will be using them extensively in the next chapter. For now, what we will do at this stage is take an overview of what properties and methods are provided and why they are useful.

The members of FormsAuthentication that we will be using are static (shared in Visual Basic .NET) so we do not need to create an instance of FormsAuthentication to use them.

The Authenticate method enables us to check a username and password against those stored in the web. conf ig file. If we want to store credentials in a place different from the web. conf ig, we must use our own code to check them rather than using Authenticate. We will look later at storing user credentials in different places: in a separate XML file, a database, and in Active Directory.

The method of FormsAuthentication that we will find ourselves using most often is RedirectFromLoginPage. We use this when we have validated a user's credentials on the login page. It performs a number of actions:

1 . It creates an authentication ticket for the user.

2. It encrypts the information from the authentication ticket.

3 . It creates a cookie to persist the encrypted ticket information.

4 . It adds the cookie to the HTTP response, sending it to the client.

5. It redirects the user back to the page they requested before coming to the login page.

168

Forms Authentication

M ny of the remaining methods of FormsAuthentication enable us to perform parts of this sequence Hividually, giving us more control over the process.

Th two properties of FormsAuthentication are concerned with the cookie that it uses to persist the authentication ticket on the client:

Q FormsCookieName - gets or sets the name that should be used for the cookie

Q FormsCookiePath - gets or sets the URL path that the cookie should be valid for

One thing worth noting about the FormsCookiePath property is that most browsers use the case of cookie paths when deciding whether to send a cookie with a request. If we set a path other than "/" (the whole site), and users use a URL with characters in a different case to the path we specify, their authentication cookie will not be sent with their request and our application will treat them as unauthenticated.

FormsAuthentication contains a set of methods for dealing with authentication tickets and the cookies that persists them:

Q Decrypt - extracts the encrypted information that was in an authentication cookie and creates a FormsAuthenticationTicket

Q Encrypt - takes the information from an FormsAuthenticationTicket and encrypts it ready for writing to an authentication cookie

Q GetAuthCookie - creates an authentication cookie but does not immediately add it to the HTTP response

Q SetAuthCookie - creates an authentication cookie and also adds it to the Response. Cookies collection - sending it back to the client with the response

Q RenewTicketlfOld - provides us with a way of extending the lifetime of the authentication ticket cookie

If we want to implement our own functionality for redirecting the user back to their originally requested page, we can use the GetRedirectUrl method to obtain the URL they originally requested.

We will see later in this chapter that password hashing provides a good way to protect passwords. The HashPasswordForStoringlnConf igFile method (possibly winning the prize for the most explanatory method name in the .NET Framework Class Library) performs this hashing for us. Something to note about this method is that it is not, as its name implies, only for use when we are storing the passwords in the web. conf ig. It can just as well be used for hashing passwords for storage in a database. "HashPasswordForStorage" might have been a better name for it.

The final method of FormsAuthentication is SignOut. This method expires the client's authentication cookie immediately with the current HTTP response - logging them out of forms authentication, meaning that their next request will be as an anonymous user.

The Formsldentity Class

Formsldentity is an implementation of Ildentity (see Chapter 6). The important difference between Formsldentity and Genericldentity is that it exposes the authentication ticket through its Ticket property. This allows us to access the ticket when we customize forms authentication.

1RQ

The FormsAuthenticationTicket Class

As we have discussed, the authentication ticket is the information that is encrypted and persisted as an HTTP cookie between requests. The ticket is what allows the forms authentication module to verify that the user has been authenticated.

It is important to clarify the difference between an instance of FormsAuthenticationTicket and the authentication cookie. An instance of FormsAuthenticationTicket contains the information we want to persist about the user in an unencrypted format. We can access this information through a set of properties. When we create the authentication cookie, the information in the authentication ticket is encrypted and stored in a string representation. When an authentication cookie returns to the server, we decrypt the information into a new FormsAuthenticationTicket so that we can access it.

The Username property of the FormsAuthenticationTicket contains, logically enough, the username of the user that the ticket is for. It is this property that allows us to identify the user when they make their requests.

Since the authentication cookie is what controls users' access to our application, the expiry of cookies is a big concern. If they expire too often, users will have to log in often, and the usability of our application may suffer. If they expire too seldom, we run a greater risk of cookies being stolen and misused. We will look at this issue in more detail later in the chapter. For now, it is enough to know that the expiry details of the authentication cookie are represented in the FormsAuthenticationTicket by a set of properties:

Q Expiration gets a DateTime object that represents the expiry date and time of the cookie Q Expired is a quick way to see whether the expiry date and time has already passed

Q IsPersistent tells us whether the cookie is set up to persist after the user closes their browser (by default, authentication cookies expire when the browser is closed)

Q IssueDate returns a DateTime object that shows when the cookie was originally issued

The CookiePath property tells us which path the cookie was issued for. As we said before, it is usually best for this to be "/" to prevent case problems with some browsers.

The main purpose of an authentication ticket is to identify the user. We can, however, use the UserData property of the FormsAuthenticationTicket to add additional information to the ticket that will be persisted in the authentication cookie between requests. As we will see later in this chapter, this can be a really useful way of securely persisting information in a cookie.

The FormsAuthenticationTicket class also includes a Version property, presumably in case of changes to the forms authentication process in the future. At present, this property returns the value 1. In future versions of ASP.NET, this property may be used to ensure backwards-compatibility with older versions in terms of the format of the authentication ticket.

Implementing Forms Authentication

Now that we have reviewed the tools that we are provided with for implementing forms authentication, let's move on to discuss what work we have to do in order to get forms authentication up and running.

170

Forms Authentication

For simple forms authentication, the steps we must take are as follows:

Q Configure forms authentication in the web.config file

3 Create a login form to enable users to enter their credentials

\Ve will discuss each of these steps in turn shortly, but first we need to discuss an important point that is critical to implementing forms authentication in a secure way.

An Important Note About SSL

Because forms authentication uses standard HTML forms for the entry of credentials, the username and password are sent over the network as plain text. This is an obvious security risk as anyone who intercepts the network traffic will be able to read the usernames and passwords that are entered into the login form. For this reason, it is strongly recommended that the traffic between the browser and the server is encrypted, at least while the user is using the login page.

The method for encrypting network traffic that is supported by a wide range of browsers is Secure Sockets Layer (SSL). SSL encrypts all traffic in both directions between the browser and the server using cryptographically sound algorithms.

SSL provides the means for a client and a server to negotiate which cryptographic algorithm will be used to encrypt the information that passes between them. Through SSL negotiation, the client and server will find an algorithm that they both support and use that.

SSL operates at a higher level than TCP/IP but at a lower level than protocols such as HTTP. Because it operates underneath HTTP, using SSL does not change the way we deal with HTTP requests - all the encryption and decryption work is taken care of by the SSL capabilities of the web server. The only difference is that the URL for addresses protected by SSL begins HTTPS:// rather than HTTP://.

Before SSL can be used, a Server Certificate must be obtained and installed on the web server. The certificate contains the cryptographic keys that will be used in encrypting traffic during exchanges with browsers along with identification information that proves the identity of the server. Server certificates are issued by certificate authorities. These authorities exist to act as trusted third parties who confirm that the possessor of a server certificate is who they say they are (identity must be confirmed with evidence when the certificate is issued).

The two biggest certificate authorities are:

Q Thawte — www. thawte . com

Q

Verisign-www.verisign.com

If we do not need the identity validation function of certificate authorities (for example, if our certificates will only be used on our intranet), it is possible to install a certificate server ourselves (effectively acting as our own certificate authority). The overhead involved in doing this usually means it is easier to buy certificates from a certificate authority.

Something worth noting here is that the Request . IsSecureConnection property shows whether the current request is using SSL. This can be useful in ensuring that no sensitive data is sent down an insecure channel.

171

Chapter 9

Configuring Forms Authentication

In order to use forms authentication, we have to set up the web. conf ig correctly. The first thing we must do is set up the <authentication> element to activate forms authentication:

authentication mode=" forms">

This tells ASP.NET that we want the forms authentication module to be active. Underneath the covers, the FormsAuthenticationModule HTTP module is activated so that it will handle the Application_AuthenticateRequest event.

NOTE: The <authentication> element can only be used in the web. conf ig that is in the root folder of an application. Attempting to use it in subfolders will cause an error. This means that only one authentication type can be defined for each application. If we want to use a different authentication type in a subfolder, we will have to define it as a separate application.

Since forms authentication does the work of authentication rather than handing it off to another system (as Windows and Passport authentication do), we have to do some additional configuration. We therefore add the <forms> element, inside the <authentication> element:

authentication mode=" forms "> <forms name="DansApplication" loginUrl="login.aspx" timeout="30" path="/" protection="all">

</forms>

</authentication

Let's have a look at each of the attributes of <f orms> in turn, to see what we are doing here.

The name attribute defines the name that the authentication cookie will have. If more than one ASP.NET application is sharing the same web server and using forms authentication, it is important that each has a unique name for its authentication cookie.

The loginUrl attribute allows us to define which page the user should be redirected to in order to log in to the application. This could be a page in the root folder of the application or it could be in a subfolder. For example, in the last section we discussed why it is important that the login page, at least, is protected by SSL encryption. If we do not want to apply SSL to our entire site, we may apply it to a secure subfolder and use loginUrl = " secure/ login, aspx" in our configuration of forms authentication.

The timeout attribute sets the length of time that the authentication cookies should last for. This is a 'rolling timeout': each time a user is authenticated (that is each time they make a request that is serviced by ASP.NET) the expiry time of their cookie is extended by the time set in the timeout attribute. So, using the configuration we have used above, a user would have to make no requests for 30 minutes for their cookie to time out and a new login be required.

±72

Forms Authentication

AS we mentioned earlier when discussing the FormsAuthentication class, the path for the authentication cookie is critical. The problems that some browsers have with case in the cookie path mean that it is usually a good idea to use "/" (whole site) for the path. If we do want to use a different path, we can set it in the path attribute.

In Chapter 2, we discussed the fact that we should not trust any data that is sent by the client machine unless we have provided additional protection. The protection attribute allows us to configure two types of protection for our authentication cookies:

Q Encryption - The contents of the cookie are encrypted

Q Validation - A Message Authentication Code (MAC) is added to the contents of the cookie to enable the server to tell if the cookie has been tampered with

We can select either of these protections individually (by setting the Protection element to Encryption or Validation), turn them both off (Protection= "none"), or use both (Protection= "All ").

The default approach is clearly the most secure - the data is encrypted to prevent it being read and validated upon its return to the server to ensure that the data has not changed. This being the case, why would we want to turn either or both of the protection methods off?

Sometimes, we are not authenticating users for security reasons, but rather we want to identify users for personalization purposes. In these cases, where it does not really matter if a user impersonates another user, we might decide that the overhead of encrypting, decrypting, and validating the authentication cookies could impact performance while bringing no benefits. Think very carefully before doing this though - it should only be done in situations where it really does not matter if the authentication system is subverted.

Adding Credentials in the web.config

Forms authentication gives us a lot of flexibility in how we store users' credentials. The default is to store them in the web . conf ig. We will look at other credentials stores later - for now, using the web . conf ig is a convenient way of setting up forms authentication quickly.

To store credentials in the web.config, we insert a <credentials> element inside the <forms> element:

outhentication mode= " Forms "> <forms name="DansApplication" loginUrl=" login. aspx" timeout="30" path= " / " protection="All ">

-credentials passwordFormat=" Clear ">

<user name="dan" password="dnvj45sc" /> <user name="jenny" password="2df Iq499"

/> </credentials>

</forms> </ authentication

173

The passwordFormat attribute is used to tell ASP.NET whether we have used a hashing algorithm to protect our passwords (we will look at how we can use hashing later in this chapter). In this case, we have used "Clear", so that our passwords will not be hashed - "dnvj45sc" is the actual password that Dan enters to use the system. The other possible options are "MD5" and "SHA1": two popular hashing algorithms.

Denying Access to Anonymous Users

As we mentioned earlier, we do not have to restrict access to pages in order to use authentication. It is perfectly possible to use authentication purely for personalization, so that anonymous users view the same pages as authenticated users (but see different results). However, in order to demonstrate the redirection functionality of forms authentication, it is useful for us to deny access to anonymous users -this will then force such users to be redirected to the login page so that they can become authenticated.

We will be looking at authorization, the process of granting and denying access to specific resources, in Chapter 12. For now, what we need to do is deny access to unauthenticated users (also known as anonymous users) so that users will be sent to our login page to enter their credentials.

To do this we use the <authorization> element of the web.config:

<authorization> <deny users="?" />

</authorization>

The authorization element is not limited to the top level web. conf ig - it can be used at any level in the application folder structure. As we will see in Chapter 12, this allows us to have different authorization settings for different folders.

The <deny> tag simply tells ASP.NET that we want to deny access to anonymous users (represented by " ? "). This will affect our entire site. We will look at more sophisticated authorization scenarios in Chapter 12 but this setting will allow us to bring forms authentication into play.

Setting Up The Login Page

We have configured forms authentication and set up a simple authorization rule to require users to log in, all that we require now is the means for them to log in - the login form.

The two essential aspects of a login form are:

Q Elements for the entry of user credentials (usually username and password).

Q Code to check the credentials. If the credentials are correct, this code should create an authentication ticket and persist it in an authentication cookie. It should then redirect the user back to the URL that sent them to the login form.

As we saw in the section on the forms authentication API, we have functionality available to us for dealing with the authentication ticket, authentication cookie, and redirection, so a basic login page is very easy to create.

Here is the presentation code for a basic login form:

174

Forms Authentication

<%@ Page language="c#" Codebehind="login.aspx.es" AutoEventWireup="false" Inherits="basic.login" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <HTML> <HEAD>

<title>login</title> </HEAD> <body>

<form id="login" raethod="post" runat="server"> <p>Username:

<asp:TextBox id="UsernameTextBox" runat="server" /> <asp:RequiredFieldValidator id="UsernameRequiredValidator" runat="server" ErrorMessage="You must enter a username" ControlToValidate="UserNameTextBox">

You must enter your username here </asp:ReguiredFieldValidator>

<asp:RegularExpressionValidator id="UserNameRegExValidator" runat="server" ErrorMessage="Username contains invalid characters" ControlToValidate="UsernameTextBox" ValidationExpression="[a-z A-Z 0-9]*"> </asp:RegularExpressionValidator> </p> <p>Password:

<INPUT id="PasswordTextBox" type="password" runat="server"> <asp:RequiredFieldValidator id="PasswordRequiredValidator" runat="server" ErrorMessage="You must enter a password"

ControlToValidate="PasswordTextbox"> You must enter your password here

</asp:RequiredFieldValidator> <asp:RegularExpressionValidator id="PasswordRegExValidator" runat="server" ControlToValidate="PasswordTextBox" ErrorMessage="Password contains invalid characters" ValidationExpression="[a-z|A-Z|0-9|!£$%&*§?]*"> </asp:RegularExpressionValidator> </p>

<asp:Label id="ErrorMessageLabel" runat="server" Visible="False"> <p>Your username and password did not match. Please try again. </p>

</asp:Label> <asp:Button id="LoginButton" runat="server" Text="Log In" />

   </form> </body> </HTML>

We have created two textboxes, one using the TextBox web form control for the username, and one using a HTML control for the password. We have to use the HTML control for the password because there is no web forms control that offers the masking on the input that the HTML password control provides.

Here is the completed login form:

17K

http: //localhost/secunty/lorms/basic/login. aspx

We have used required field validation controls to ensure that both a username and a password are entered. We also added an invisible label that will become visible if the user's credentials are not correct. We used an invisible label like this rather than setting text in the code for the button click because this way, we keep all the text that will be displayed on the page in the . aspx file and all the functionality code in the . aspx. cs file - a good separation of presentation and business logic. Finally, we have to create a button to enable the user to log in.

We have also used a regular expression validator for each input to ensure that the inputted values only contain characters we consider safe. As we saw in Chapter 2, it is important that we validate all user input that comes into our application. The username field is set up to accept only upper and lowercase letters and digits:

ValidationExpression="[a-z| A-Z

0-9]*"> While

the password field accepts some additional characters:

ValidationExpression="[a-z|A-Z 0-9 !£$%&*@?]*">

Note that we have used & in the regular expression to specify that we allow the ampersand character.

Here is what the login form displays when we enter an invalid character (!) in the username:

176

l-orms Authentication

We now need to add some code for when the login button is clicked. As we want to separate presentation and business logic, this goes in a class in login.aspx.es:

using System;

using System.Collections; using Systern.ComponentModel; using System.Data;

using System.Drawing; using System.Web;

using System.Web.SessionState; using System.Web.UI;

using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; using System.Web.Security;

namespace basic

public class login : System.Web.UI.Page {

protected System.Web.UI.WebControls.TextBox UsernameTextBox; protected Systern.Web.UI.WebControls.RequiredFieldValidator

UsernameRequiredValidator; protected Systern.Web.UI.WebControls.RequiredFieldValidator

PasswordRequiredValidator;

protected System.Web.UI.WebControls.Label ErrorMessageLabel; protected System.Web.UI.WebControls.Button LoginButton; protected System.Web.UI.WebControls.RegularExpressionValidator

UserNameRegExValidator; protected Systern.Web.UI.WebControls.RegularExpressionValidator

PasswordRegExValidator; protected Systern.Web.UI.HtmlControls.HtmlInputText PasswordTextBox;

private void Page_Load(object sender, Systern.EventArgs e)

//we don't need to do anything in Page_Load but we must include it }

177

Соседние файлы в предмете Программирование