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

Asp Net 2.0 Security Membership And Role Management

.pdf
Скачиваний:
48
Добавлен:
17.08.2013
Размер:
12.33 Mб
Скачать

Chapter 2

IIS Per-Request Security

In many ways, the security processing that occurs within IIS6 is something of a black box. You can choose the specific security that should be enforced for an application or for a virtual directory. Once configured, IIS6 performs the necessary work to set up security information for each request. From an ASP.NET perspective, the security choices in IIS boil down to the following:

Does the ASP.NET application require a WindowsPrincipal for each user that authenticates with the website?

Will ASP.NET handle authentication using forms-based authentication, or some other custom authentication strategy?

Will the ASP.NET site run from a remote file share (that is, a share defined with a Universal Naming Convention [UNC] name)? This question is orthogonal to the previous two considerations because using a UNC share is primarily a deployment decision, but one that does has ramifications for security.

From a technical perspective, IIS6 sets up security information for a request by initializing an Extension Control Block (ECB) structure and passing this structure to the ISAPI extension responsible for serving dynamic content. In the previous chapter, the difference between static and dynamic content handling was discussed. If static content is being served (as opposed to an ASP.NET page or a resource mapped to the ASP.NET ISAPI extension), IIS6 internally handles all of the security processing for static content.

Any ISAPI extension has the ability to use the ECB to call a support function within IIS that returns the impersonation token for the current request. Depending on whether anonymous access or authenticated access has been configured for an application in IIS, IIS returns an authenticated user token or a default anonymous access token from the support function. In IIS, the following directory security options are available:

Authenticated access using Integrated Security (either NTLMor Kerberos-based), Basic Authentication, Digest Authentication

Authenticated access using certificate mapping

Anonymous access

The first two security configurations result in a security token that represents a specific user from either the local machine’s security database or a domain. The token that is returned varies from request to request, depending on which user is currently making a request to IIS. The last option also results in a security token representing a specific user; however, on every request made to IIS, the same security token is returned because IIS uses a fixed identity to represent an anonymous user.

Keep in mind that IIS has determined the impersonation token for a request before ASP.NET is ever involved! A frequent (and understandable) request from customers is around configuring both Windows and forms authentication in ASP.NET for the same ASP.NET application. Although some complicated hacks get this scenario to work, ASP.NET (including ASP.NET 2.0) has, to date, never tackled the problem because doing so requires a complicated dance between the front-end request processing in IIS and the subsequent processing that occurs both in the ASP.NET ISAPI extension and the managed portion of the ASP.NET runtime. Because IIS has already set up an impersonation token before ASP.NET ever comes into the picture, solving this problem has always been deemed too awkward.

32

Security Processing for Each Request

Running Both Windows and Forms Authentication

One solution for attempting to allow some type of integrated authentication to a website as well as the option for forms authentication is to author a custom ISAPI filter (not an extension) that supports negotiating a secure connection with Internet Explorer as well as a fallback mode that redirects a user to a forms-based login. From the point of view of ASP.NET, though, a solution that included a custom ISAPI filter, login logic running in the managed world, and then additional logic to set up different IPrincipal-based user objects on an HttpContext gets complicated quickly. For example, how do you author an application where a person may either auto-magically authenticate against Active Directory, or explicitly log in with an account stored in a SQL-based Membership database? Technically, it is possible to accomplish this, but security-related code can be very awkward. With all that said though, extranet customers are especially interested in this type of solution and both third-party vendors Microsoft supply solutions to this problem today. Also, future versions of IIS and ASP.NET will eliminate the somewhat artificial division between IIS request processing and ASP.NET request processing. When this division is finally eliminated, it will become possible to more easily author sites that support mixed authentication modes.

For requests processed by the ASP.NET ISAPI extension, it is up to ASP.NET to decide what to do with the impersonation token from IIS. It is this interplay between IIS’s initial security processing and ASP.NET’s downstream security processing that leads to confusion over how to configure ASP.NET and IIS in such a way that you get the desired security context when an ASP.NET page executes.

In the previous chapter, you saw that at certain points in an application domain’s lifecycle ASP.NET may use the token that is passed to it from IIS, and may explicitly impersonate the token for certain tasks. Specifically, you saw that the security context for the Application_Start and Application_End events is one of the following: process identity, application impersonation identity, or explicit UNC credentials. However, an application developer also needs to know what security context will be available on each request. The following sections discuss what happens to the IIS impersonation token for each ASP.NET request.

ASP.NET Per-Request Security

When ASP.NET processes a request, it maintains a handle back to the IIS context for the request through a reference to an implementation of HttpWorkerRequest. In the case of ASP.NET running inside of IIS, the internal implementation of HttpWorkerRequest used includes various pieces of information passed to it by the ASP.NET ISAPI extension. Of course, part of this information includes the impersonation token.

However, just because an impersonation token is available to ASP.NET does not mean that the security credentials negotiated by IIS will be used by ASP.NET. Instead, the security context for each request is dependent on the following settings and information:

The identity of the operating system thread

The impersonation token from IIS

The value of the impersonate attribute in the <identity /> configuration element

33

Chapter 2

The value of the username and password attributes in the <identity /> configuration element

Whether the mode attribute of the <authentication /> configuration element has been set to

Windows

Before diving into how these settings interact with each other, a review of where security information can be stored is necessary.

Where Is the Security Identity for a Request?

In reality, no single location in ASP.NET defines the identity for a request. This is a case where the differences between the older Win32-oriented programming model and the managed world sort of collide.

Before the .NET Framework was implemented, the question of security identity always rested with the currently executing operating system thread. An operating system thread always has a security token associated with it representing either a local (potentially a built-in identity) or a domain account. Win32 programmers have always had the ability to create new security tokens and use these to change the security context of an operating system thread. This behavior includes reverting the identity of a thread and explicitly impersonating a security identity.

The impersonation token from IIS mentioned earlier is a piece of information that IIS creates based on the directory security settings for an application. ISAPI extensions, such as aspnet_isapi.dll, can get a handle to this token through the ISAPI support functions. The impersonation token can be passed to various Win32 APIs such as ImpersonateLoggedOnUser and SetThreadToken. For example, ASP.NET will call SetThreadToken in various places, while the application domain is initializing and during the processing of the very first request.

With the introduction of the .NET Framework, a managed representation of a thread is available from the

System.Threading.Thread class. The Thread class has a CurrentPrincipal property that represents the security identity of the managed thread. It is entirely possible for the security identity of the operating system thread (obtainable by calling System.Security.Principal.WindowsIdentity.GetCurrent()) to differ in type and in value from the managed IPrincipal reference available from instance of

Thread.CurrentPrincipal.

As if that weren’t complicated enough, ASP.NET introduced the concept of an HttpContext associated with each request flowing through ASP.NET. The HttpContext instance for a request has a User property that also contains a reference to an IPrincipal implementation. This additional reference to a security identity opened up the possibility of having a third set of security credentials available to a developer that differed from the information associated with the operating system thread and the managed thread.

Figure 2-1 highlights the differences between a managed and operating system thread as well as where the HttpContext fits into the picture.

To demonstrate, the following example is a simple application that displays three different identities. The sample code stores the operating system’s security identity and the managed thread identity as they exist during the Application_BeginRequest event, and when a page is running. The value for the User property on the HttpContext is also stored.

34

Security Processing for Each Request

Impersonation

 

 

,Thm

 

 

 

 

 

 

 

IPrincipal

 

IPrincipal

token available to

systemOpeaingr

eadr (Systeagednead)MaThr

tityn idenr witheadownnitsthnugr

 

 

ISAPI extensions

 

 

 

 

 

 

 

g,nTh

 

 

 

 

 

 

eadir

Figure 2-1

The initial identity information is collected in global.asax:

<%@ Import Namespace=”System.Security.Principal” %> <%@ Import Namespace=”System.Threading” %>

void Application_BeginRequest (Object sender, EventArgs e)

{

HttpContext current = HttpContext.Current;

current.Items[“OperatingSystem_ThreadIdentity_BeginRequest”] = WindowsIdentity.GetCurrent().Name;

if (String.IsNullOrEmpty(Thread.CurrentPrincipal.Identity.Name))

{

current.Items[“ManagedThread_ThreadIdentity_BeginRequest”] = “[null or empty]”;

current.Items[“ManagedThread_IsGenericPrincipal”] = (Thread.CurrentPrincipal is GenericPrincipal);

}

else

ntextCo (Syste

NET.ASP

Co Request

35

Chapter 2

current.Items[“ManagedThread_ThreadIdentity_BeginRequest”] = Thread.CurrentPrincipal.Identity.Name;

if (current.User == null) current.Items[“HttpContext_User_BeginRequest”] = “[null]”;

else

current.Items[“HttpContext_User_BeginRequest”] = current.User.Identity.Name;

}

This code contains checks for null or empty strings because Application_BeginRequest occurs as the first event that a developer can hook in ASP.NET’s processing pipeline. As a result, much of the security setup and synchronization that ASP.NET performs on your behalf has not occurred yet. Specifically, ASP.NET has not attempted to associate an IPrincipal with the current HttpContext. Additionally, ASP.NET has not synchronized user information on the HttpContext to the current managed thread. The managed thread principal is instead associated with an instance of a System.Security.Principal

.GenericPrincipal with a username set to the empty string. The value of the User property on the HttpContext though is not even initialized, and returns a null value instead.

The values for this information are displayed in a page load event using the following code:

using System.Security.Principal; using System.Threading;

...

protected void Page_Load(object sender, EventArgs e)

{

Response.Write(“The OS thread identity during BeginRequest is: “ + Context.Items[“OperatingSystem_ThreadIdentity_BeginRequest”] + “<br />”);

Response.Write(“The managed thread identity during BeginRequest is: “ + Context.Items[“ManagedThread_ThreadIdentity_BeginRequest”] + “<br />”);

Response.Write(“The managed thread identity during BeginRequest is “ + “a GenericPrincipal: “ + Context.Items[“ManagedThread_IsGenericPrincipal”] + “<br />”);

Response.Write(“The user on the HttpContext during BeginRequest is: “ + Context.Items[“HttpContext_User_BeginRequest”] + “<br />”);

Response.Write(“<hr />”);

Response.Write(“The OS thread identity when the page executes is: “ + WindowsIdentity.GetCurrent().Name + “<br />”);

if (String.IsNullOrEmpty(Thread.CurrentPrincipal.Identity.Name)) Response.Write(“The managed thread identity when the page executes is: “ +

“[null or empty]” + “<br />”);

else

Response.Write(“The managed thread identity when the page executes is: “ + Thread.CurrentPrincipal.Identity.Name + “<br />”);

Response.Write(“The managed thread identity is of type: “ +

Thread.CurrentPrincipal.ToString() + “<br />”);

if (String.IsNullOrEmpty(User.Identity.Name))

36

Security Processing for Each Request

Response.Write(“The user on the HttpContext when the page executes is: “ + “[null or empty]” + “<br />”);

else

Response.Write(“The user on the HttpContext when the page executes is: “ + User.Identity.Name + “<br />”);

Response.Write(“The user on the HttpContext is of type: “ +

User.ToString() + “<br />”);

Response.Write(“The user on the HttpContext and the “ + “thread principal point at the same object: “ +

(Thread.CurrentPrincipal == User) + “<br />”);

}

The information is displayed running on an ASP.NET 2.0 application with the following characteristics:

The site is running locally on the web server (that is, not on a UNC share).

IIS has Anonymous and Integrated Authentication enabled.

ASP.NET is using the default mode of Windows for authentication.

The <identity /> element’s impersonate attribute is set to false.

The page output is shown here:

The OS thread identity during BeginRequest is: NT AUTHORITY\NETWORK SERVICE The managed thread identity during BeginRequest is: [null or empty]

The managed thread identity during BeginRequest is a GenericPrincipal: True The user on the HttpContext during BeginRequest is: [null]

--------------------------------------------------------------------------------

The OS thread identity when the page executes is: NT AUTHORITY\NETWORK SERVICE The managed thread identity when the page executes is: [null or empty]

The managed thread identity is of type: System.Security.Principal.WindowsPrincipal The user on the HttpContext when the page executes is: [null or empty]

The user on the HttpContext is of type: System.Security.Principal.WindowsPrincipal The user on the HttpContext and the thread principal point at the same object: True

The operating system thread identity makes sense because this is the identity of the underlying IIS6 worker process. The ASP.NET runtime is not impersonating any identity, so the security context of the thread is not reset by ASP.NET. As mentioned earlier, during BeginRequest neither the HttpContext nor the Thread object have had any security information explicitly set by ASP.NET.

The security information during page execution is a bit more interesting. The operating system thread identity has not changed. However, the IPrincipal associated with the current thread, and the IPrincipal associated with HttpContext is a reference to a WindowsPrincipal. Furthermore, the managed thread and HttpContext are referencing the same object instance. Clearly something occurred after Application_BeginRequest that caused a WindowsPrincipal to come into the picture.

At this point, the important thing to keep in mind is that before the AuthenticateRequest event in the ASP.NET pipeline occurs, neither the thread principal nor the User property of HttpContext should be relied on for identifying the current. The operating system identity though has been established.

However, this identity can be affected by a number of factors, as you will see in the next section.

37

Chapter 2

Establishing the Operating System Thread Identity

Both ASP.NET and IIS have a “say” in the identity of the underlying operating system thread that is used for request processing. By default, the identity is set to that of the IIS6 worker process: NT AUTHORITY\NETWORK SERVICE. However, developers and administrators have the option to use the IIS6 MMC to change the identity of the IIS6 application pool (that is, the worker process) to a different domain or machine account.

In earlier versions of ASP.NET, determining the actual impersonation token passed to ASP.NET was difficult because the technique involved some rather esoteric code. However, it is easy to get a reference to the impersonation token that IIS passes to ASP.NET in ASP.NET 2.0. The following line of code gets a reference to the identity associated with the IIS impersonation token:

WindowsIdentity wi = Request.LogonUserIdentity;

With this information, it is much simpler to see the impersonation token without the sometimes confusing effects of other authentication and configuration settings. For example, with the sample application used in the previous section (anonymous access allowed in IIS, Windows authentication enabled in ASP.NET, no impersonation), some of the security information for a page request is:

The OS thread identity during BeginRequest is: NT AUTHORITY\NETWORK SERVICE The OS thread identity when the page executes is: NT AUTHORITY\NETWORK SERVICE The impersonation token from IIS is: DEMOTEST\IUSR_DEMOTEST

Getting confused yet? From this listing it appears that yet another security identity has appeared! In this case the output shows the default anonymous credentials for the IIS installation on my machine. The reason for this behavior is that the impersonation token that IIS hands off to ISAPI extensions is based on the security settings for the application in IIS.

If the IIS application is deployed on a UNC share with explicit UNC credentials, the security token that IIS makes available to the ASP.NET ISAPI extension corresponds to the explicit UNC credentials. Technically, IIS6 also supports UNC access whereby IIS6 can use the credentials of the browser user to access the UNC share (pass-through authentication to the UNC share). However, this mode of UNC access has not been tested with ASP.NET 2.0 and should not be used for ASP.NET applications.

The following table shows the various IIS security options and the resulting impersonation token that IIS will hand off to ASP.NET:

IIS Authentication Type

Impersonation Token Handed Off to ASP.NET

Integrated, Basic, Digest,

Token corresponding to the authenticated

or Certificate Mapping

(or mapped) browser user

Anonymous

The default identity configured in IIS for anony-

 

mous access. Usually an account of the form

 

IUSR_MACHINENAME

Running on a UNC share with explicit credentials

The configured UNC identity. This identity is passed regardless of the IIS authentication type.

38

Security Processing for Each Request

After the thread of execution enters the ASP.NET ISAPI extension and starts running the ASP.NET pipeline, the setting of the impersonate attribute on the <identity /> element will affect the operating system thread identity. Prior to starting execution of the HTTP pipeline, ASP.NET will initialize the identity of the operating system thread based on a combination of the settings in the <identity /> attribute and the impersonation token available from IIS.

If the impersonate attribute of the <identity /> element is set to true, then ASP.NET will change the operating system thread’s identity using the token that IIS passed to ASP.NET. However, if ASP.NET does not explicitly set the thread token, the operating system thread will run with the credentials configured for the worker process in IIS.

Continuing with previous sample, if the following configuration change is made to the application:

<identity impersonate=”true” />

Then ASP.NET explicitly impersonates using the supplied impersonation token. Now, the security information for the request changes to reflect the default anonymous user configured in IIS (at this point the sample application is not requiring IIS to authenticate the browser user):

The OS thread identity during BeginRequest is: DEMOTEST\IUSR_DEMOTEST The OS thread identity when the page executes is: DEMOTEST\IUSR_DEMOTEST The impersonation token from IIS is: DEMOTEST\IUSR_DEMOTEST

Changing the settings in IIS to instead allow only Integrated authentication causes IIS to hand off an impersonation token representing an authenticated user. Because ASP.NET impersonates this token, the thread identity will reflect the authenticated user identity:

The OS thread identity during BeginRequest is: CORSAIR\demouser

The OS thread identity when the page executes is: CORSAIR\demouser

The impersonation token from IIS is: CORSAIR\demouser

If the configuration for <identity /> includes an explicit value for the username and password attributes then ASP.NET ignores the impersonation token that is provided by IIS, and ASP.NET instead explicitly sets the operating system’s thread token based on the credentials in the <identity /> element. For example, if the sample application is switched back to allow Anonymous access in IIS and the configuration is changed to use the following:

<identity impersonate=”true” userName=”appimpersonation@corsair.com” password=”pass!word1”/>

Then the security information reflects the application impersonation identity:

The OS thread identity during BeginRequest is: CORSAIR\appimpersonation The OS thread identity when the page executes is: CORSAIR\appimpersonation The impersonation token from IIS is: DEMOTEST\IUSR_DEMOTEST

Another variation with application impersonation follows. This time the sample application in IIS is configured to require Integrated authentication. Notice how ASP.NET still sets the thread identity to the configured application impersonation account. The credentials negotiated with the browser are only available by looking at the impersonation token supplied by IIS.

39

Chapter 2

The OS thread identity during BeginRequest is: CORSAIR\appimpersonation The OS thread identity when the page executes is: CORSAIR\appimpersonation The impersonation token from IIS is: CORSAIR\demouser

Throughout the previous samples, the sample application was running locally on the web server. If instead the sample application is placed on a UNC share configured with explicit UNC credentials, the only security identities used for the operating system thread are either the UNC credentials or the application impersonation credentials. This is due in part because IIS always set the impersonation token to the explicit UNC identity, regardless of whether or not the application in IIS is configured to require some type of authentication with the browser.

When running the sample application on a UNC share without impersonation enabled, the security information looks like:

The OS thread identity during BeginRequest is: CORSAIR\uncidentity The OS thread identity when the page executes is: CORSAIR\uncidentity The impersonation token from IIS is: CORSAIR\uncidentity

This highlights an important piece of ASP.NET security behavior. ASP.NET always ignores the true/false state of the impersonate attribute when running on a UNC share. Instead, ASP.NET will impersonate the UNC identity. Running on a UNC share with client impersonation enabled (<identity impersonate =”true” />), the security information is exactly the same because of this behavior:

The OS thread identity during BeginRequest is: CORSAIR\uncidentity The OS thread identity when the page executes is: CORSAIR\uncidentity The impersonation token from IIS is: CORSAIR\uncidentity

However, if application impersonation is configured for an application (that is, the username and password attributes of the <identity /> element are set), then ASP.NET will ignore the impersonation token from IIS and will instead set the operating system thread identity to the values specified in the <identity /> element. Notice in the following output that the UNC identity is only available from the impersonation token passed by IIS:

The OS thread identity during BeginRequest is: CORSAIR\appimpersonation The OS thread identity when the page executes is: CORSAIR\appimpersonation The impersonation token from IIS is: CORSAIR\uncidentity

To summarize all this information (what? — you don’t have it memorized yet!), the following table lists the combinations of impersonation tokens from IIS and operating system thread identities based on various configuration settings when running on IIS6. Remember that client impersonation means <identity impersonate=”true”/>, whereas application impersonation means an explicit username and password were configured in the <identity /> element. In the following table, when running on a UNC share is yes, this means that the application in IIS has an explicit set of UNC credentials configured for accessing the share. I noted earlier that “officially” ASP.NET 2.0 is not supported running on a UNC share that uses pass-through authentication.

40

Security Processing for Each Request

On UNC

 

ASP.NET

Operating System

Impersonation

Share?

IIS Authentication

Impersonation

Thread Identity

Token

 

 

 

 

 

No

Anonymous

None

NETWORK

IUSR_

 

allowed

 

SERVICE

MACHINE

 

 

 

 

NAMENAME

No

Anonymous

Client

IUSR_

IUSR_

 

allowed

 

MACHINE

MACHINE

 

 

 

NAMENAME

NAMENAME

No

Anonymous

Application

The application

IUSR_

 

allowed

 

impersonation

MACHINE

 

 

 

credentials

NAMENAME

No

Authenticated

None

NETWORK

The credentials

 

access required

 

SERVICE

of the browser

 

 

 

 

user

No

Authenticated

Client

The credentials

The credentials

 

access required

 

of the browser user

of the browser

 

 

 

 

user

No

Authenticated

Application

The application

The credentials of

 

access required

 

impersonation

the browser user

 

 

 

credentials

 

Yes

Anonymous

 

 

 

 

allowed

None

The configured

The configured

 

 

 

UNC identity

UNC identity

Yes

Anonymous

Client

The configured

The configured

 

allowed

 

UNC identity

UNC identity

Yes

Anonymous

Application

The application

The configured

 

allowed

 

impersonation

UNC identity

 

 

 

credentials

 

Yes

Authenticated

None

The configured

The configured

 

access required

 

UNC identity

UNC identity

Yes

Authenticated

Client

The configured

The configured

 

access required

 

UNC identity

UNC identity

Yes

Authenticated

Application

The application

The configured

 

access required

 

impersonation

UNC identity

 

 

 

credentials

 

 

 

 

 

 

The ASP.NET Processing Pipeline

And now for a brief interlude to review the processing pipeline in ASP.NET 2.0: a basic understanding of the pipeline is useful for knowing when authentication and authorization occur within the lifecycle of an ASP.NET request and, thus, when other security credentials are established in ASP.NET and how these credentials are used later on in the ASP.NET pipeline.

41