ASP.NET performs a sequence of tasks - dubbed collectively
the ASP.NET process model - aimed at servicing a client request. Currently,
ASP.NET supports two models - the default model and the IIS 6 model - and their
use depends on your operating system and how IIS is configured. The default
model used on Windows 2000 and Windows XP with IIS 5.x is based on an ISAPI
extension and an ASP.NET-specific worker process (aspnet_wp.exe) that hosts the
Common Language Runtime (CLR). This model also is used on Windows Server 2003
when IIS 6.0 is configured to run in compatibility mode. But the normal mode of
operation of IIS 6.0 on Windows Server 2003 is known as the IIS 6 process
model, and it consists of an ISAPI extension and an IIS worker process
(w3wp.exe). Within this process, the request goes through an ISAPI extension,
which loads the CLR in the process and activates the HTTP runtime pipeline.
With IIS 6.0, IIS manages ASP.NET and no longer concerns itself with tasks such
as process recycling, gardening, and isolation from the Web server. Although
key differences exist between the two process models, they both leverage a
couple of components - an ISAPI extension, and a worker process that hosts the
CLR and runs managed code. The interaction of these two components determines
how the ASP.NET subsystem works as well as its security settings.

In this article, I'll focus on the security context in
which ASP.NET requests are served. The security context in which the ASP.NET
runtime operates is affected by privileges and permissions set on the
components of the ASP.NET process model. I'll refer mostly to the default IIS
5.x process model, and I'll signal any relevant differences in the IIS 6
process model.

Serve the Request

When a request hits IIS, the Web server first ensures that
the request comes from a trusted IP address. If not, the request is rejected
with the 403.6 HTTP error; if the requested resource belongs to a protected
type, such as .asax files, IIS returns an HTTP error 403.2. If the resource can
be accessed, IIS attempts to authenticate the caller using Windows' Integrated,
Digest, or Basic authentication methods. Which method is used depends on how
IIS is configured. To handle the request, IIS picks up a thread from its pool
and gives it the identity of the authenticated user; if anonymous access is
performed, the IIS thread has the anonymous identity. The thread now does its
job and dispatches the request to the ASP.NET ISAPI extension.

The ISAPI extension, named aspnet_isapi, is hosted by the
IIS process and therefore runs under the SYSTEM account. The ISAPI extension is
a piece of unmanaged code responsible for starting up the ASP.NET worker
process. The extension must guarantee that the worker process is available and
healthy, and it sends requests to the process for processing. The ISAPI
extension and the worker process communicate through a set of named pipes (see
Figure 1). In particular, synchronous pipes are used for callbacks from the
worker process into the ISAPI extension to retrieve some server variables and
obtain information from IIS. Asynchronous named pipes are used for transporting
requests and responses to and from the worker process.

Figure 1. The ASP.NET ISAPI extension hosted by IIS forwards the HTTP
request to the ASP.NET worker process using a set of asynchronous named pipes.
Then the request is processed through the ASP.NET managed HTTP pipeline.

The ISAPI extension has its own pool of threads, but it
uses them only for asynchronous calls to the worker process. The extension does
not process the HTTP request. The worker process receives the request to
process along with the security token of the authenticated, or anonymous, IIS
user. By default, the worker process runs under the ASPNET account.

The thread within the worker process that picks up the
request inherits the identity token of the IIS thread. If impersonation is
disabled, the thread runs under the account of the parent process - the ASPNET
account. In the IIS 6 process model, the account of the worker process
(w3wp.exe) is a built-in account named NetworkService.

Get to Know the ASPNET Account

The ASPNET account is a local account created when the
.NET Framework is installed (see Figure 2). Unlike SYSTEM, which is the default
account for classic ASP applications, ASPNET has few privileges: Access this
computer from the network, Deny logon locally, Deny logon through Terminal
Services, Log on as a batch job, and Log on as a service. ASPNET also has some
NTFS permissions to operate on certain folders and create temporary files and
assemblies, including access to the root of the .NET Framework installation
folder, the "Temporary ASP.NET Files" directory, the global assembly cache
(GAC) folder, and the Windows system directory.

Figure 2. Here is the Properties dialog box of the ASPNET user account.
The account is created when the .NET Framework is installed, and its password is
generated automatically and stored in the Windows local security authority.

Let's review some common scenarios that involve the
ASP.NET system security context as opposed to the ASP.NET application security
context, which is the context in which you use your own code to authenticate
and authorize users.

To create or edit a local file, the worker process thread
serving the current request requires access to the file or the containing
folder. The thread runs within the context of the aspnet_wp process (ASPNET
account) and impersonates an identity only if impersonation is on, which by
default is false. If impersonation is enabled, the thread might impersonate the
account propagated by IIS or IUSR_Machine in case of anonymous access; if the
thread has an identity token, the token must provide write access to the file.
By default, ASP.NET applications don't impersonate accounts and are accessed by
the anonymous user frequently. In this case, the worker process thread inherits
no identity and uses that of the host process - ASPNET. Because ASPNET could
have no write permissions on the Web space, an exception is thrown if you try
to create or edit a file.

A nearly identical situation occurs if your Web
application attempts to open a connection to a database. In this case, you need
a login defined for ASPNET or the identity being impersonated.

If a new process is to be created, it will inherit the
account of the parent process. The new process will run under the ASPNET
account whether or not impersonation is set.

The limited set of privileges granted to the ASPNET
account helps fend off revert-to-self attacks. This is a typical attack a
hacker can conduct once accessing your Web application (for example, exploiting
a buffer overrun) simply by reverting the security token of the impersonated
client to the token of the parent process. In ASP.NET, the most a hacker can
obtain is the ASPNET account - the "weak" account of the worker process. Bear
this in mind if you decide to change the default account for the ASP.NET worker
process.

Change the Process Account

The base identity for the ASP.NET ISAPI extension is set
to the account of IIS (SYSTEM) and is not configurable. The base identity for
the ASP.NET worker process by default is ASPNET, but you can change it to any
other system- or user-defined account (including SYSTEM). Information about the
process account is set in the section of machine.config.
Here are the default contents:

Setting the userName attribute to Machine indicates the
default account - ASPNET or NetworkService if the IIS 6 process model is
enabled. Setting the password attribute to AutoGenerate indicates that the
password is generated cryptographically during setup. The password is stored in
the Local Security Authority (LSA) of the operating system. You can configure
the worker process to run under the SYSTEM account, although this approach is
highly discouraged. Here's how you can give administrative privileges to your Web
application:

You also can set an arbitrary Windows account by
indicating the username and password:

Do not take lightly the decision to change to the default
account - you must review a few aspects carefully. In particular, the new
ASP.NET account shouldn't be less powerful than ASPNET; otherwise you seriously
risk tearing down some ASP.NET functionality. At a minimum, you should guarantee
that the new account is granted full access to the Temporary ASP.NET Files
folder. You can set the section only within the
machine.config file, and this affects all applications running on the machine.
To mitigate this problem, you can apply settings to a specific resource by
using a tag with an appropriate path attribute. You can use
the path attribute to identify a specific file or folder to which unique
configuration settings apply.

In addition, you should note that settings in the
section are read by aspnet_isapi.dll, not by managed code.
For this reason, changes to are not applied until IIS is
restarted. Also, be aware that when you use the IIS 6 process model, the
settings in the section are ignored. To configure the
process identity (or other process model values), use the Configuration Manager
and apply changes in the IIS metabase directly.

When you change the process account, you need to indicate
the password in a clear text file, which never is a safe practice. In ASP.NET
1.1, you can encrypt and store usernames and passwords in the registry and link
them to the .config files. This feature has been extended to ASP.NET 1.0 with
Service Pack 3. In the .config file, you can indicate usernames and passwords
like this:

userName="registry:HKLM\path\key, user"

password="registry:HKLM\path\key, pswd"

You indicate the path of the registry where the
information is stored. A new utility, aspnet_setreg.exe, is provided with SP3
and ASP.NET 1.1 to encrypt and store credentials in the registry. Note that you
can use this encrypting feature only for the ,
, and sections.

Enable Impersonation

Unlike classic ASP, ASP.NET works with impersonation
disabled by default. This setting is configurable, however, and you can enable
impersonation by editing this line in the web.config file:

Because the web.config file can be defined in each child
folder, you could configure the application so it assumes different identities
in different folders. This feature also depends on the fact that you can set
the section at all levels of the configuration hierarchy - not
only at the machine or application root level.

When impersonation is enabled, the thread processing the
request can have a different identity from the ASP.NET process. In particular,
the thread's security token is any token IIS uses to accept the request. The
identity is that of the currently logged-on Windows user. Note that editing the
section really is the only way to change the identity of
the worker process. This is different from impersonating the current, or a
fixed, user. If you change the identity of the process, all threads will use
the new identity and no extra work is needed on thread switches.

An alternative to default impersonation is to impersonate
a specific user by providing its username and password:

userName="..."
password="..." />

This case deserves a bit of attention. To impersonate a
user in Windows 2000, a process needs to call the LogonUser Win32 API. But this
function requires an administrator-level privilege - the SE_TCB_NAME privilege
- set on the calling process. In reality, the privilege does not need to be
enabled, but it must be granted; the LogonUser function enables it if
necessary. If the calling process does not have this privilege, the LogonUser
function simply fails. What does this mean to you? If you want to support
impersonation by name, you must grant the ASP.NET worker process a critical
privilege. The default ASPNET account doesn't have such a privilege; as a
result, you can't impersonate accounts in Windows 2000 unless you replace the
ASPNET account with a stronger one that boasts the SE_TCB_NAME privilege. For
this reason, impersonating by name in ASP.NET 1.0 might be dangerous. In
Windows XP and Windows Server 2003, the SE_TCB_NAME privilege is no longer
needed to log on as a particular user.

In ASP.NET 1.1, the situation is a bit different
regardless of the underlying operating system. Even if the privilege is not
granted to the worker process, impersonation by name still is supported by
virtue of a smart trick. In ASP.NET 1.1 on Windows 2000, in fact, the
aspnet_isapi, which runs under SYSTEM, creates the token and copies it back to
the memory of the worker process. As a result, the weak ASPNET account
impersonates any fixed identity.

ASP.NET counts three different security layers. One is
represented by the worker process, which by default runs under a weak account
such as ASPNET. The second layer is the security context of the thread within
the worker process that actually serves the request; this token is affected
directly by impersonation settings. Finally, the last layer of the ASP.NET
security is the application-specific infrastructure for authentication and
authorization. Interspersed between these three layers are many system features
that have been improved significantly in ASP.NET 1.1 to make the runtime more
secure.

Dino Esposito is a trainer and consultant who
specializes in ASP.NET, ADO.NET, and XML. Author of Building Web Solutions with ASP.NET and
ADO.NET and Programming
Microsoft ASP.NET, both from Microsoft Press, Dino also is a co-founder
of http://www.VB2TheMax.com.
E-mail him at mailto:[email protected].

Tell us what you think! Please send any comments about
this article to [email protected].
Please include the article title and author.