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

Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]

.pdf
Скачиваний:
107
Добавлен:
16.08.2013
Размер:
29.8 Mб
Скачать

768 C H A P T E R 2 2 W I N D O W S A U T H E N T I C AT I O N

Figure 22-14. Configuring delegation in Windows Server 2003 (functional level)

Figure 22-15. Trusted for delegation for Windows 2000 (functional level)

It’s important to keep in mind that both Figure 22-14 and Figure 22-15 show the same dialog box (the properties of the computer you want to configure through Active Directory Users and Computers). In the first case (Figure 22-14), Windows Server 2003 is configured to run the Active

C H A P T E R 2 2 W I N D O W S A U T H E N T I C AT I O N

769

Directory domain at a Windows Server 2003 functional level; in the second case (Figure 22-15), the domain is configured for Windows 2000 functional level (which is the default configuration). With these settings you basically specify the capabilities of the tickets issued by the KDC

(which is the domain controller). If tickets don’t have these capabilities, delegation is not granted and not possible. After you have configured this setting, you don’t need to perform any extra steps. Just configure IIS for integrated Windows authentication, and enable impersonation in your ASP.NET web application, as you will see in the next section. You can find more about configuring and troubleshooting delegation on the Microsoft website at http://www.microsoft.com/downloads/ details.aspx?FamilyID=7dfeb015-6043-47db-8238-dc7af89c93f1&displaylang=en.

Caution We suggest not using impersonation or delegation if it’s not really necessary. If you use impersonation or delegation, this includes flowing the original client user’s identity from the front-end to the backend. On the backend, all the ACLs and operating system security-related authorization settings must be configured properly for every single user. This configuration gets harder and harder with an increasing number of users. A simple configuration mistake can lead to either an application that doesn’t work or a (probably huge) security leak. And think about the additional security configurations necessary! Instead, you should group users to roles and perform any security configuration based on roles or groups. You will learn more about roles and groups in Chapter 23.

Caution Enabling delegation for a server is something you should do carefully. Thoroughly review applications running on such a server, because malicious applications can lead to repudiation attacks. Imagine a malicious application (or malicious part of an application that has not been reviewed) running on this server and performing some “illegal” actions under an impersonated/delegated user’s identity. Applications (and therefore servers) should be allowed only for performing delegation if it’s really necessary so that applications running on such servers cannot do any “illegal” things based on other, impersonated/delegated user identities.

Configured Impersonation

The simplest form of impersonation is configured impersonation, where you use the web.config file to define the impersonation behavior you want. You accomplish this by adding the <identity> element shown here:

<configuration>

<system.web>

<!-- Other settings omitted. -->

<identity impersonate="true" />

</system.web>

</configuration>

You can configure the identity element in more than one way, depending on the result you want. If you want to impersonate the IIS authenticated account, you should use this setting:

<identity impersonate="true">

Keep in mind that if you allow anonymous access, you can use the IUSR_[ComputerName] account. When using this approach, the impersonated account must have all the permissions required to run ASP.NET code, including read-write access to the c:\[WinDir]\_Microsoft.NET\ Framework\[Version]\Temporary ASP.NET Files directory where the compiled ASP.NET files are stored. Otherwise, an error will occur and the page will not be served.

770 C H A P T E R 2 2 W I N D O W S A U T H E N T I C AT I O N

ASP.NET also provides the option to specifically set an account that will be used for running code. This technique is useful if you want different ASP.NET applications to execute with different, but fixed, permissions. In this case, the user’s authenticated identity isn’t used by the ASP.NET code. It just sets a base level of permissions you want your application to have. Here’s an example:

<identity impersonate="true" userName="matthew" password="secret" >

This approach is more flexible than changing the machine.config account setting. The machine.config setting determines the default account that will be used for all web applications on the computer. The impersonation settings, on the other hand, override the machine.config setting for individual websites. Unfortunately, the password for the impersonated account cannot be encrypted in the web.config file by default. This constitutes a security risk if other users have access to the computer and can read the password. The risk is especially severe if you are using impersonation with a highly privileged account.

Fortunately, you can encrypt such settings with a tool provided by Microsoft called aspnet_setreg.exe. Because the following configuration sections cannot be encrypted with the aspnet_regiis.exe utility, you can use aspnet_setreg.exe to secure the following information in your web.config file:

The <processModel> user name and password in the machine.config file

The user name and password in the <identity> element

Session state connection strings in the <session> element if you are using SQL state

The aspnet_setreg.exe tool was originally created for .NET 1.0, but it can be used with .NET 1.1 and 2.0 as well. You can download the tool from Microsoft at http://support.microsoft.com/ default.aspx?scid=kb;en-us;329290.

The aspnet_setreg.exe tool queries the information and stores it encrypted in the registry. Of course, the worker process user has to have permissions for this registry key, as the first action it performs is to read the identity information from the registry key for impersonating this identity. You can use aspnet_setreg.exe as follows for encrypting a user name and password for the <identity> element:

aspnet_setreg -k:Software\ProAspNet\Identity -u:Developer -p:pass@word1

This encrypts the specified user name and password and stores the encrypted version in the registry key HKLM\Software\ProAspNet\Identity. Next you have to grant the worker process or application pool’s identity read access to this registry hive, as shown in Figure 22-16.

Now you have to configure your <identity> element in the web.config file as follows:

<identity impersonate="true" userName=

"registry:HKLM\Software\ProAspNet\Identity\ASPNET_SETREG,userName" password=

"registry:HKLM\Software\ProAspNet\Identity\ASPNET_SETREG,password"

/>

C H A P T E R 2 2 W I N D O W S A U T H E N T I C AT I O N

771

Figure 22-16. Granting access to the registry hive

When you now create a Default.aspx page as follows with the preceding <identity/> element configured, the result looks like Figure 22-17:

<%@ Page Language="C#" CodeFile="Default.aspx.cs" Inherits="_Default" %> <html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server"> <title>Untitled Page</title>

</head>

<body>

<form id="form1" runat="server"> <div>

<b>Authenticated:</b> <%= User.Identity.Name %> <b>Impersonated:</b>

<%= System.Security.Principal.WindowsIdentity.GetCurrent().Name %> </div>

</form>

</body>

</html>

772 C H A P T E R 2 2 W I N D O W S A U T H E N T I C AT I O N

Figure 22-17. Configured impersonation with fixed user name and password

Programmatic Impersonation

Configured impersonation allows you to impersonate a user for the entire duration of a request. If you want more control, such as the ability to impersonate a user for only part of the page request, you have to do the impersonation yourself in your code.

The key to impersonating a user programmatically is the WindowsIdentity.Impersonate() method. This method sets up impersonation for a specific account. You identify the account you want to impersonate by using its account token. Account tokens are what Windows uses to track users once their credentials are approved. If you have the token for a user, you can impersonate that user.

The general process is as follows:

1.Obtain an account token for the account you want to impersonate.

2.Use WindowsIdentity.Impersonate() to start impersonation. This method returns a WindowsImpersonationContext object.

3.Call the Undo() method of the WindowsImpersonationContext object to revert to the original identity.

Getting a Token

You can get an account token in two main ways. The most common approach is to retrieve the token for the currently authenticated user. You can access this token through the current security context, using the WindowsIdentity.Token property. Tokens are represented in .NET as IntPtr objects, which are representations of pointers to unmanaged memory locations. However, you never need to interact with this directly. Instead, you simply need to pass the token to the WindowsIdentity.Impersonate() method.

Here’s an example that extracts the token for the current user:

IntPtr token = ((WindowsIdentity)User.Identity).Token;

The only other way to get a user token is to programmatically log in with a specific user name and password. Unfortunately, .NET does not provide managed classes for logging a user in. Instead, you must use the LogonUser() function from the unmanaged Win32 security API.

C H A P T E R 2 2 W I N D O W S A U T H E N T I C AT I O N

773

To use the LogonUser() function, you must first declare it as shown here:

[DllImport(@"c:\Windows\System32\advapi32.dll")]

public static extern bool LogonUser(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out int phToken);

As you can see, the LogonUser() function exists in advapi32.dll. It takes a user name, domain, password, logon type, and logon provider input parameters, along with an output parameter that allows you to access the token following a successful logon. The parameter names aren’t important. In this example, the somewhat cryptic names from the Windows API reference are used. A Boolean result is returned to indicate whether the logon was successful.

Note Windows XP or later operating systems impose restrictions on the use of blank passwords to prevent network-based attacks. As a result of these restrictions, you won’t be able to use the LogonUser() function to impersonate an account with a blank password.

Once you have imported the LogonUser() function, you can use it in your code to log the user in, as shown here:

//Define required variables. string user = "matthew"; string password = "secret"; string machine = "FARIAMAT"; int returnedToken;

//Try to log on.

if (LogonUser(user, machine, password, 3, 0, out returnedToken))

{

// The attempt was successful. Get the token. IntPtr token = new IntPtr(returnedToken);

}

Note that you must convert the integer value returned by LogonUser() into an IntPtr in order to use it with the WindowsIdentity.Impersonate() method.

Performing the Impersonation

Once you have an account token, you can use the WindowsIdentity.Impersonate() method to start impersonating the corresponding identity. You can use the Impersonate() method in two ways. You can use the static version, which requires an account token. Alternatively, you can use the instance version, which impersonates the identity represented by the corresponding WindowsIdentity object. In either case, the Impersonate() method returns a WindowsImpersonationContext object that has a single function—it allows you to revert to the original identity by calling its Undo() method.

Here’s an example of programmatic impersonation at its simplest, using the static version of the Impersonate() method:

WindowsImpersonationContext impersonateContext; impersonateContext = WindowsIdentity.Impersonate(token);

//(Now perform tasks under the impersonated ID.

//This code will not be able to perform any task

//that the user would not be allowed to do.)

impersonateContext.Undo()

774 C H A P T E R 2 2 W I N D O W S A U T H E N T I C AT I O N

At any time, you can determine the identity that your code is currently executing under by calling the WindowsIdentity.GetCurrent() method. Here’s a function that uses this technique to determine the current identity and display the corresponding user name in a label on a web page:

private void DisplayIdentity()

{

// Get the identity under which the code is currently executing. WindowsIdentity identity = WindowsIdentity.GetCurrent(); lblInfo.Text += "Executing as: " + identity.Name + "<br>";

}

Using the method, you can create a simple test that impersonates the authenticated IIS identity and then reverts to the standard identity:

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

{

if (User is WindowsPrincipal)

{

DisplayIdentity();

//Impersonate the IIS identity. WindowsIdentity id;

id = (WindowsIdentity)User.Identity; WindowsImpersonationContext impersonateContext; impersonateContext = id.Impersonate(); DisplayIdentity();

//Revert to the original ID as shown here. impersonateContext.Undo(); DisplayIdentity();

}

else

{

//User isn't Windows authenticated.

//Throw an error or take other steps.

}

}

Figure 22-18 shows the result.

Figure 22-18. Impersonating a user programmatically

C H A P T E R 2 2 W I N D O W S A U T H E N T I C AT I O N

775

Summary

In this chapter, you learned how to use Windows authentication with ASP.NET to let IIS validate user identities. You also learned about the different types of authentication, how to retrieve user information, and how to impersonate users so your code runs under a different Windows account. In the next chapter, you’ll learn about using advanced authorization rules that apply to Windows authentication and forms authentication.

C H A P T E R 2 3

■ ■ ■

Authorization and Roles

So far, you’ve seen how to confirm that users are who they say they are and how to retrieve information about those authenticated identities. This gives your application the basic ability to distinguish between different users, but it’s only a starting point. To create a truly secure web application, you need to act upon that identity at various points using authorization.

Authorization is the process of determining whether an authenticated user has sufficient permissions to perform a given action. This action could be requesting a web page, accessing a resource controlled by the operating system (such as a file or database), or performing an applica- tion-specific task (such as placing an order or assigning a project). Windows performs some of these checks automatically, and you can code others declaratively using the web.config file. You’ll need to perform still others directly in your code using the IPrincipal object.

In this chapter, you’ll learn how ASP.NET authorization works, how to protect different resources, and how to implement your own role-based security.

URL Authorization

The most straightforward way to set security permissions is on individual web pages, web services, and subdirectories. Ideally, a web application framework should support resource-specific authorization without requiring you to change code and recompile the application. ASP.NET supports this requirement with declarative authorization rules, which you can define in the web.config file.

The rules you define are acted upon by the UrlAuthorizationModule, a specific HTTP module. This module examines these rules and checks each request to make sure users can’t access resources you’ve specifically restricted. This type of authorization is called URL authorization because it considers only two details—the security context of the user and the URL of the resource that the user is attempting to access. If the page is forbidden and you’re using forms authentication, the user will be redirected to the login page. If the page is forbidden and you’re using Windows authentication, the user will receive an “access denied” (HTTP 401) error page, as shown in Figure 23-1, or a more generic error message or custom error page, depending on the <customErrors> element.

777