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 IdentityTo 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)
{
dlblUser.Text = Page.User.Identity.Name;
lblWindow.Text = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
lblThread.Text = System.Threading.Thread.CurrentPrincipal.Identity.Name;
}
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.
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;
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.
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" />
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" />
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.
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.
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 IdentityTo 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)
{
dlblUser.Text = Page.User.Identity.Name;
lblWindow.Text = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
lblThread.Text = System.Threading.Thread.CurrentPrincipal.Identity.Name;
}
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.
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;
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.
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" />
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" />
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.
No comments:
Post a Comment