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

Professional ASP.NET Security - Jeff Ferguson

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

We can now switch a DatabaseCredentialsStore object into the login, aspx in the same way as we did for the XmlCredentialsStore. This time, we need to create the connection to pass to the DataBaseCredentialsStore constructor:

private void LoginButton_Click(object sender, System.EventArgs e) {

//define the connection string string connectionString =

@"Provider=SQLOLEDB;Data Source=syzygy\VSDOTNET;Initial"* "Catalog=UserCredentials;Integrated Security=SSPI;";

//create a connection

OleDbConnection connection = new OleDbConnection(connectionString);

//create the connection ICredentialsStore credentialsStore = new DatabaseCredentialsStore(connection);

//check the credentials if(credentialsStore.Authenticate(UsernameTextBox.Text, PasswordTextBox.Value))

{

//set up the authentication cookie and redirect

FormsAuthentication. RedirectFromLoginPage (UsernameTextBox. Text .false) ,-} else {

//make the error message visible ErrorMessageLabel.Visible = true;

Using Windows Credentials

The simplest way to use Windows credentials is to activate Windows authentication in the web. conf ig. We saw how to do this in Chapter 7. However, we may want to have the same degree of control over the authentication process as we do with forms authentication but use the credentials store of Windows. We can do this by using a Platform Invoke (often called a P/Invoke) to access the Windows security API.

Note: Think carefully before tying forms authentication into Windows credentials. It is usually a good idea to keep the two separate, so that Windows credentials are used as little as possible. The more we use the Windows credentials, the higher the risk of them being compromised.

We will create an implementation of ICredentialsStore, as we did for XML and database credentials stores. Here is our class:

[assembly:SecurityPermissionAttribute(SecurityAction.RequestMinimum,

UnmanagedCode=true)]

198

Forms Authentication

namespace credentialsStores

public class WindowsCredentialsStore : ICredentialsStore

//import the LogonUser function from the windows API [Dlllmport ("C: \\Windows\\System32\\advapi32 .dll") ] public static extern bool LogonUser(String IpszUsername, String IpszDomain, String IpszPassword, int dwLogonType, int dwLogonProvider, out int phToken);

public bool Authenticate(string username, string password) int

token;

//try to log the user on with windows

if(LogonUser(username,"syzygy",password,3,0,out token)) return true;

else

return false; }

As you can see, the class is very simple. Before the class, we specify that this assembly needs permission to use unmanaged code. This is because we will be accessing the Windows API:

[assembly:SecurityPermissionAttribute(SecurityAction.RequestMinimum,

UnmanagedCode=true)]

The line ensures that anyone running our code will be aware that this permission is required.

Inside the WindowsCredentialsStore class but before our Authenticate method, we import the LogonUser function from the Windows API:

[Dlllmport("C:\\Windows\\System32\\advapi32.dll")]

public static extern bool LogonUser(String IpszUsername, String IpszDomain, String IpszPassword, int dwLogonType, int dwLogonProvider, out int phToken);

We now have the LogonUser method available to us. This will take a username, password, machine name, numerical logon type, numerical logon provider, and integer token to populate (remember from Chapter 6 that the token represents the logon session) and return true if the username and password match a user correctly.

We use the LogonUser method in the Authenticate method:

int token;

//try to log the user on with windows

1OQ

if(LogonUser(username,"syzygy",password,3,0,out token)) return true;

else

return false;

We pass the username, machine name ("syzygy" in this case) and password, along with the correct values for logon type and logon provider. If the logon was successful, we return true from the method. If not, we return false.

The WindowsCredentialsStore class can be used in the same way as our other credentials stores:

private void LoginButton_Click(object sender, System.EventArgs e)

ICredentialsStore credentialsStore = new WindowsCredentialsStore( ) ;

//check the credentials if(credentialsStore.Authenticate(UsernameTextBox.Text,

PasswordTextBox.Value))

//set up the authentication cookie and redirect FormsAuthentication.RedirectFromLoginPage(UsernameTextBox.Text, false);

else

//make the error message visible ErrorMessageLabel .Visible = true;

Other Credentials Stores

The simple examples shown here should be enough to show that we can use forms authentication with a variety of credentials stores. For example, we might want to tie forms authentication to credentials stored in an LDAP directory or to some proprietary credentials store. Forms authentication doesn't make any assumptions about the credentials store we use - as long as we provide code to check the credentials we can use whatever store we like.

Summary

In this chapter, we have looked at how we can use forms authentication to implement authentication systems that give us a lot of control over both the user interface and the functionality.

We have seen that:

Q Forms authentication has advantages over Windows authentication and Passport authentication in some scenarios.

Q It is usually better to implement forms authentication rather than creating our own cookie-based authentication scheme.

200

Forms Authentication

Q The forms authentication API provides a variety of useful functionality - a toolkit for implementing forms authentication.

Q To implement forms authentication we need to configure authentication to use forms and create a login page.

Q It is easy to use alternative credentials stores rather than the web. conf ig file.

In the next chapter, we will look at some of the ways that the flexibility of forms authentication allows us the freedom to customize it for different scenarios.

201

Extending Forms Authentication

In the last chapter, we saw how we can implement forms authentication to identify users of our application. In this chapter, we will show how the flexibility of forms authentication API allows us to modify the behavior of our authentication system to suit a wide variety of scenarios.

We will be looking at some of the most common problems that we need to solve, along with solutions to them. Hopefully these examples will also inspire you with ideas for achieving other results.

We'll be investigating the following topics:

Q Implementing forms authentication in a web farm environment

Q Using forms authentication without cookies

Q Protecting content other than . aspx pages

Q Adding our own information to the authentication ticket

Q Setting up forms authentication to support role-based security

LI Maintaining a list of logged on users

G Providing multiple logon pages

Q Adding additional protection against cookie theft

Using Forms Authentication across Multiple Servers

If you expect to be involved in a project of sufficient scale to require a web farm of servers, you will be pleased to hear that forms authentication can be used across multiple servers. A little extra work is required to configure ASP.NET correctly on each server but the end result is that each server in the farm will accept authentication tickets issued by any of the other servers. This means that, once a user has had their credentials verified by one server in the farm, they will be authenticated by all the others through their authentication cookie (of course, the users' credentials must be synchronized between the servers or based on a shared resource in order for the servers to have the same behavior).

The key thing we have to do to is synchronize the cryptographic keys that are used for encrypting, decrypting and validating the authentication ticket. By default, these keys are automatically and randomly generated by ASP.NET. This will not work across a web farm - each machine will have its own randomly generated keys. The servers will not accept the authentication tickets issued by the other servers because they will not validate correctly. Even if they did accept them, they would not be able to read them as their decryption key would not match the key used to encrypt the ticket.

These keys are also important in allowing servers to validate viewstate values issued by other servers - another important issue when using ASP.NET with a web farm.

If we want to enable the servers to validate and decrypt tickets issued by the other machines, we must set the keys for the servers to matching values. This is done through the <machineRey> configuration element.

We can set the <machineKey> element at for a whole machine (in machine. conf ig), at web site level (in a web. conf ig), or in the root folder of a particular web application. In order to prevent ASP.NET from generating its own values, we must populate the validationKey and decryptionKey attributes of <machineKey> with our own keys. These key strings should consist of hexadecimal characters (0-9 and A - F).

We can set either attribute to "autogenerate" if we want ASP.NET to automatically generate a random value for that key, but normally if we need to manually specify one key then we also need to specify the other.

Here is an example of a <machineKey> element with the two key attributes defined:

<machineKey

validationKey="61EA54E005915332011232149A2EEB317586824B265326CCDB3AD9ABDBE9D6F24BO

625547769E835539AD3882D3DA88896EA531CC7AFE664866ED5242FC2B05D" decryptionKey = "61EA54E005915332011232149A2EEB317586824B265337AF" validation="SHAl" />

The validationKey value can be between 40 and 128 characters long. It is strongly recommended that we use the maximum length key available.

The decryptionKey value can be either 16 or 48 characters long. If 16 characters are defined, standard DES (Data Encryption Standard) encryption is used. If 48 characters are defined, Triple DES (or 3DES) will be used. This means that DES is applied three times consecutively. 3DES is much more difficult to break than DES so it is recommended that 48 characters are defined for decryptionKey, unless the overhead of triple DES becomes a performance issue.

204

Extending Forms Authentication

Note: Some versions of the .NET Framework and Visual Studio .NET documentation incorrectly state that the decryptionKey can have a length of between 40 and 128 characters. This does not make sense, as it is a DES or 3DES key and thus needs to have either 16 or 48 characters.

The last attribute defines which algorithm will be used for validating authentication tickets and viewstate values. We can set it to "SHA1", "MD5", and "3DES". In the. case of triple DES (3DES), authentication ticket validation will use SHA1 because it does not support 3DES.

If either the length of either of the keys is outside the allowed values, ASP.NET will return an error message when requests are made to the application:

Fte

Edit

View

Favorites

Tods

Hefp

Search

Favorites

trip Media

f*

|j^ http:J/l

Server Error in '/security/forms/MultipleAppsPublic' Application.

Configuration Error

w the specific error details below and

Description: An error occurred during the processing of a configuration tile required to service this request. Please modify your configuration file appropriately.

Parser Error Message: Machine validation key is of invalid size It is '129' Hex chars long, ft must be between 40 and 128 Hex chars long.

Source Error:

Line 45:

</authentications .ine 46:

*j Local intranet

When triple DES is in use (that is, the decryptionKey has 48 characters), ASP.NET also checks that the decryptionKey does not use a value that is very insecure. For example, if you enter:

61EA54E00591533261EA54E00591533261EA54E005915332

for the decryptionKey, you will get the following error message:

favorites Tools Help

drws j^j Ktpj/tocafrost/secunty/Forms/f'VftoieAppspublic/

Server Error in Vsecurity/forms/MultipleAppsPublic1 Application.

Specified key is a known weak key for TripfeDES and cannot be used.

Description: An unhandred exception occurred during the execution of the current web request Please review the stack trace fw more information aboU the error and where 1 originated in the code.

Exception Details: System.Securly.Cryptography.CryptographicException; Specified key is a known weak key for TripteDES and cannot be used Source Error:

regarding the origin and location of th* exception can be

identified using the exception stack

trace b«lo».

 

0CXxx>

* j Local intranet

2O5

Can you see what was wrong with the key? It was the same sequence of 16 characters repeated three times. Using the same key three times with DES is insecure so there is a check to ensure that our 3DES key does not contain repetition.

Using These Techniques Without a Web Farm

It is possible to use these techniques for forms authentication without setting up a web farm. In fact, the technique of synchronizing <machineKey> elements can be useful in some situations where we do not wish to use a web farm or even where we are not dealing with multiple servers.

Imagine we have a server that hosts a number of web applications. Most of these applications need to be kept separate, so each application has its own keys in its <machineKey> element. However, we want two of the web applications to share authentication - a user whose credentials have been accepted by one of the applications should be automatically logged into the other without having to enter their credentials again. We can achieve this by synchronizing the <machineKey> elements for these applications. We will also have to synchronize the <authentication> elements and ensure that the two applications are using the same set of credentials. Once we have done this, the applications will each accept tickets issued by the other.

This approach also works across servers. For example, if we have an application running on a server that is accessible over the Internet and another application running on a server inside our firewall, and we want to enable our staff to log in once for both applications, we can synchronize the <machineKey> elements and each application will accept the tickets issued by the other. We could even set up the system so that the internet-facing application has no means of issuing new tickets - users who do not want to be anonymous must enter their credentials in the application behind the firewall before using the application on the internet-accessible server.

Let's create a simple example to show how this works.

We'll create two web applications, one that requires users to log in to use it (that is, it does not allow anonymous users) and one that does allow anonymous users. We can create these applications on different machines or as separate applications on the same machine.

The first application represents an internal application that is only used by employees of a hypothetical company. The second site represents a public e-commerce site.

Our first application, MultipleAppsPrivate, is very similar to the basic authentication example we covered earlier in the chapter. We set up the web. conf ig for forms authentication and to deny requests by anonymous clients:

authentication mode="Forms"> <forms name="MultipleApps"

loginUrl="login.aspx"

timeout="30"

path="/"

protection="All">

<credentials passwordFormat="Clear"> <user name="dan" password="dnvj45sc" />

206

Extending Forms Authentication

<user name="jenny" password="2dfIq499" /> </credentials>

</forms>

</authentication>

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

</authorization>

<machineKey validation="SHAl" validationKey="61EA54E005915332011232149A2EEB317586824B265326CCDB3AD9ABDBE9D6F24BO 625547769E835539AD3882D3DA88896EA531CC7AFE664866BD5242FC2B05D"

decryptionKey="61EA54E005915332011232149A2EEB317586824B265337AF"

/>

You can see that we have also specified some user credentials, and values for the attributes of the <machineKey> element.

MultipleAppsPrivate has a login form identical to the simple example we used earlier. Its default page (default. aspx) has some text on it to represent the private site:

http:«locatrosysecucity/forms,'MullipleAppsPrivate/delault.aspK

Welcome to the administration site.

If this application were fully developed, you would be able to do all sorts of exciting things

At present, all we can offer you is a link to the public site

HBBJ

* Local intrane?

The second application is very simple indeed. We add the <authentication> and <machineKey> elements to the web. conf ig in the same way, although we do not add a <credentials> element as we will not be allowing users to log in on this application. We also leave the <authorization> element out as we want this application to be open to anonymous users as well as authenticated users.

<authentication mode="Forms"> <forms name="MultipleApps"

loginUrl="login.aspx"

timeout="30"

path="/"

protection="All">

</forms>

</authentication

<machineKey validation="SHAl" validationKey="61EA54E005915332011232149A2EEB317586824B265326CCDB3AD9ABDBE9D6F24BO 625547769E835539AD3882D3DA88896EA531CC7AFE664866BD5242FC2B05D"

decryptionKey="61EA54E005915332011232149A2EEB317586824B265337AF"

207

Note that we use exactly the same values for the validationKey and decryptionKey attributes as we did in the private application.

For the purposes of this example, the public site consists of a single page. It includes a server-side <div> element that begins with its visible property set to false:

<div id="AdminOptionsDiv" runat="server" visible="false">

( <a href="http://localhost/security/forms/multipleappsprivate/">

Go to private site </a> | Add A Product | Delete A Product | Add A Category Delete A Category )

We will display this <div> only to users who are authenticated. Since there is no way for the user to log in to this application, they will have to do so in the private application.

We also add a button control for authenticated users to log out. This also begins as invisible.

In the code behind, we add some simple code to display the <div> and the button if the user is authenticated:

private void Page_Load(object sender, System.EventArgs

e) if(Request.IsAuthenticated)

AdminOptionsDiv.Visible = true; SignOutButton.Visible = true;

We also add a simple method to log the user out when they click the log out button.

We can test the applications now by first visiting the pubic site. We are not authenticated, so we do not see the administration options:

httpV/localhost/secuf ity/forms/M uftipleApp

208

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