| Several questions have come
up about accessing resources while executing an ASP.Net web
request. I decided to create a review of the different
configuration options and how that affects the identity ASP.Net
uses to process a request. Choosing the correct security model
to use is extremely important in making a website secure and the
implications of those decisions needs to be understood to avoid
common programming pitfalls. For setting the appropriate
permissions it is essential to know which account to set.
Hopefully this will clear up some of the confusion about account
information.
|
| ASP.Net supports the
following authentication modes, None, Windows and Forms. None as
it's name implies does not perform any type of authentication
and runs under the default identity. Windows supports both basic
and integrated authentication. Forms supports the ASP.Net forms
authentication. I will not be covering Forms authentication as
that falls into the realm of custom authentication. I'm also not
going to cover Passport as it is rarely used. |
| As part of the ASP.Net
request life cycle, we can capture the AuthenticateRequest event
when a request is received. This event always fires and is
raised by the Application. It can be handled in the Global.asax.
This event is also handy for capturing the actual user's
identity and replacing the users principal with our own. This
will be covered. |
| The areas we will be
looking at are the AuthenticateRequest event, the web.config
setting for setting up the identity and how IIS interacts with
the settings. I will not be reviewing how to secure a website. I
will also not be focusing on how to change the identity the
process runs under. There are many excellent references on
these topics already. |
| Determining Identity | |
| To determine the identity
used for the request I set up a single aspx page that writes out
the three most common ways of getting the current identity from
a web application. These are the Page.User.Identity,
System.Security.Pricipal.WindowsIdentity.GetCurrent() and
system.Threading.Thread.CurrentPrincipal.Identity. The code for
the Page_Load event is displayed in Listing 1. The IIS Security
Settings dialog can be seen in Figure 1. This displays the
options available for configuring web security with IIS. |
| Listing 1. |
private void Page_Load(object sender, System.EventArgs e)
{
lblUser.Text = Page.User.Identity.Name;
lblWindow.Text = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
lblThread.Text = System.Threading.Thread.CurrentPrincipal.Identity.Name;
}
|
| Figure 1. |
|
|
| Anonymous Access |
| If we are setting a public
informational website, we may not need the users to be
identified. This is known as anonymous access. In this case IIS
will authenticate using a pre-determined account and the request
will use the default ASP.Net account. Anonymous is set in IIS by
selecting the Anonymous Access. Once this is set, no
authentication will be made against the user regardless of any
other selected authentication modes. The only mode available in
the web.config with this authentication is Forms. The identities
can be seen in Figure 2. As you can see the only item populated
is the Windows identity running as ASPNET. This is the
default account for IIS 5.x. |
| Figure 2. |
|
|
| So what does this mean?
This indicates that if you are using anonymous access then the ASPNET account will have to have the appropriate permissions to
access the files that are needed on the server. It also means
that if you are planning on using Integrated Security on SQL
Server, this account must have the appropriate permission in SQL
Server. |
| Now, if you wanted to
perform your own custom security, including Forms
authentication, you could create a GenericIdentity and
GenericPricipal to use in place of the default Page.User. this
allows you to send the user's information to your middle tier
without depending on using the HttpContext. The
code to perform this is shown in Listing 2. You can find many
good examples of how to implement this in your own application.
The results of this change can be seen in Figure 3. As you can
see we are still making the actual resource requests using the
ASPNET account. |
| Listing 2. |
System.Security.Principal.GenericIdentity id = new System.Security.Principal.GenericIdentity("Darth Vader");
System.Security.Principal.GenericPrincipal p = new System.Security.Principal.GenericPrincipal(id, new string[]{"Dark Side", "Sith"});
HttpContext.Current.User = p;
|
| Figure 3. |
|
|
| Authenticated Requests |
| If we want authenticated
request to obtain the user's actual identity, we need to
deselect the Allow Anonymous with IIS. We then have to select
one of the authentication methods. the two most commonly used
selection are Basic and Windows Integrated. The Basic
authentication will prompt the user to enter a user name and
password which are sent in the http header. Windows integrated
security only works with Internet Explorer. It does not prompt
the user but takes the current user's credentials they used to
log onto their machine and tries to authenticate. If that fails,
then it will ask for a user name and password. Unlike Basic
authentication, it does not send the password in plain text, it
send the password hash. This is more secure then Basic but
typically should be limited to intranet scenarios. Both of these
methods can be used over the internet using https. |
| If we set the mode
attribute of the authentication element in the web .config to
None while requiring authentication in IIS you can see in Figure
4 we have the same result as allowing anonymous in IIS. If we
change the mode to Windows we have a different result as we can
see in Figure 5. Now, even though we are actually capturing the
authentication, our request are still being made under the
ASPNET account. |
| Figure 4. |
|
|
| Figure 5. |
|
|
| To get the request to run
under our identity, we need to add an additional element to our
web.config file to tell the .Net runtime to impersonate the
actual requester. This is the identity element. We set the
impersonate attribute to true to set up the impersonation as in
Listing 3. The effect of this change on our identity can be seen
in Figure 6. We are now impersonating the actual requestor. This
does deserve some additional discussion as it can lead to some
permissions errors. This account will be used to access the
appropriate resources on the host machine. This works regardless
of whether we are using Basic or Windows Integrated
authentication as we are authenticated against the server. If we
try to access remote resource we see an important difference.
Since Windows integrated authentication only passes the hash of
the password, the server cannot authenticate us against any
other servers, the delegation ends at the web server. If we try
and connect to any remote resources we will see a failure in the
authentication. This is known as the "double-hop" issue. This is
not an issue with Basic authentication since we have been passed
the password in plain text. This allows the server to further
authenticate against any other server on our behalf. |
| An interesting
anomaly is when using Windows Integrated authentication with
impersonation on an application and running from the local
machine. This avoids the double hop since it can obtain your
user credentials from the Windows logon. It will then forward
those credentials and be able to connect if you have permission.
This sometimes confuses developers and leads to those famous
words when the application is moved to production, "It works on
my machine." |
| Listing 3. |
<identity impersonate="true" />
|
| Figure 6. |
|
|
| Now if we want to run under
a fixed account for accessing resources on the server and
connecting to say SQL Server using Windows Integrated
authentication. You could use Basic authentication but this is
considered very insecure even on an intranet. We could also use
Basic over https but this could lead to performance issues. I
have experienced increases in the request time of up to 30% with
https. ASP.Net allows the application to run under a separate
account by adding the userName and password attributes of the
identity element in the web.config. The new identity element is
shown in Listing 4 with the results shown in Figure 7. For this
to be successful, the user account must be granted full control
of the ASP.Net Temporary Files folder in order to create the
shadow copy. Also note that we have the actual user that made
the request while any resource requests will be made using the
application account. We can also run with these settings under
Allow Anonymous within IIS. We would still be making the
requests under our application account but we would lose the
identity of the actual requestor. This is still useful for
allowing Integrated authentication against remote resources and
SQL Server. |
| Listing 4. |
<identity impersonate="true" userName="joe" password="joeuser" />
|
| Figure 7. |
|
|
| Security Issues |
| It is essential
in any security configuration that care be taken. Too often we
grant excessive permission to an account because "it's easier to
work with". One of the keys to good security is understanding
what access is needed and only providing the minimal amount. If
an account is compromised, then we need to limit the amount of
damage an attacker can do. You can follow this link to determine what
permissions are required for an ASP.Net application account,
Process and request identity in ASP.Net
|
| A concern
with using application accounts is the fact that you have to
place the user name and password in the web.config file. Even
though .config files are prevented from being served by the
runtime this does not mean the file is secure. A misconfigured
server could allow someone to see the web.config and the account being used. This
can create an unacceptable risk. Microsoft has incorporated a
means of encrypting the user name and password and placing them
in the registry. The details of the procedure can be found at
How to use the ASP.Net utility to encrypt credentials and session state connection strings. |
| Differences in IIS 6.0 |
| IIS runs under
what is know as application pools. Each pool can use a different
user name and password for processing the request. The name that
is specified in the pool will be the default account for your
application. All the other information presented here behaves
the same. |
| |