Beruflich Dokumente
Kultur Dokumente
com/en-us/library/Aa292118
ASP.NET Impersonation
Visual Studio .NET 2003 Another important security feature is the ability to control the identity under which code is executed. Impersonation is when ASP.NET executes code in the context of an authenticated and authorized client. By default, ASP.NET does not use impersonation and instead executes all code using the same user account as the ASP.NET process, which is typically the ASPNET account. This is contrary to the default behavior of ASP, which uses impersonation by default. In Internet Information Services (IIS) 6, the default identity is the NetworkService account. Note Impersonation can significantly affect performance and scaling. It is generally more expensive to impersonate a client on a call than to make the call directly. Using impersonation, ASP.NET applications can optionally execute the processing thread using the identity of the client on whose behalf they are operating. You usually use impersonation for resource access control. Delegation is a more powerful form of impersonation and makes it possible for the server process to access remote resources while acting as the client. For more information, see ASP.NET Delegation. Note Impersonation is local to a particular thread. When code changes threads, such as when using thread pooling, the new thread executes using the process identity by default. When impersonation is required on the new thread, your application should save the security token (WindowsIdentity.Token Property) from the original thread as part of the state for the completion thread. If you enable impersonation, ASP.NET can either impersonate the authenticated identity received from IIS or one specified in the application's Web.config file. You have the following three options when configuring impersonation:
Impersonation is disabled. This is the default setting. For backward compatibility with ASP, you must enable impersonation and change the ASP.NET process identity to use the Local System account. In this instance, the ASP.NET thread runs using the process token of the application worker process regardless of which combination of IIS and ASP.NET authentication is used. By default, the process identity of the application worker process is the ASPNET account. For more information, see ASP.NET Process Identity.
If the application resides on a UNC share, ASP.NET always impersonates the IIS UNC token to access that share unless a configured account is used. If you provide an explicitly configured account, ASP.NET uses that account in preference to the IIS UNC token. You should exercise care when using impersonation because it makes it possible for an application to potentially process code using permissions not anticipated by the application designer. For example, if your application impersonates an authenticated intranet user, that application possesses administrative privileges when impersonating a user with those privileges. Likewise, if the impersonated user possesses more restrictive permissions than anticipated, the user may not be able to use the application.
ASP.NET Impersonation
.NET Framework 1.1 Other Versions
When using impersonation, ASP.NET applications can optionally execute with the identity of the client on whose behalf they are operating. The usual reason for doing this is to avoid dealing with authentication and authorization issues in the ASP.NET application code. Instead, you rely on Microsoft Internet Information Services (IIS) to authenticate the user and either pass an authenticated token to the ASP.NET application or, if unable to authenticate the user, pass an unauthenticated token. In either case, the ASP.NET application impersonates whichever token is received if impersonation is enabled. The ASP.NET application, now
impersonating the client, then relies on the settings in the NTFS directories and files to allow it to gain access, or not. Be sure to format the server file space as NTFS, so that access permissions can be set. Impersonation is disabled by default. For ASP compatibility, the user must explicitly enable impersonation. If impersonation is enabled for a given application, ASP.NET always impersonates the access token that IIS provides to ISAPI extensions. That token can be either an authenticated user token, or the token for the anonymous user (such as IUSR_MACHINENAME). The impersonation occurs regardless of the type of authentication being used in the application. Only application code is impersonated; compilation and configuration are read as the process token. The result of the compilation is put in the "Temporary ASP.NET files" directory. The account that is being impersonated needs to have read/write access to this directory. If an application is on a universal naming convention (UNC) share, ASP.NET will always impersonate the token provided to IIS to access that share unless a configured account is used. If an explicit configured account is provided, ASP.NET will use that account in preference to the IIS UNC token. Applications that do want per-request impersonation can simply be configured to impersonate the user making the request. Impersonation is disabled at the computer level by default and, unless overridden, all the application domains inherit this setting. You can enable impersonation by putting a configuration file in the application root directory. For more information about the ASP.NET configuration system, see ASP.NET Configuration. As is the case with other configuration directives, this directive applies hierarchically. It is respected by nested applications in the hierarchy, unless explicitly overridden. The default value for this setting is as follows.
<impersonation enable="false"/>
A minimal configuration file to enable impersonation for an application might look similar to the following example.
userName="registry:HKLM\Software\AspNetIdentity,Name" password="registry:HKLM\Software\AspNetIdentity,Password"
The portion of the string after the keyword registry and before the comma indicates the name of the registry key that ASP.NET opens. The portion after the comma contains a single string value name from which ASP.NET will read the credentials. The comma is required, and the credentials must be stored in the HKLM hive. If the configuration format is incorrect, ASP.NET will not launch the worker process and the current account creation failure code path will be followed. The credentials must be in REG_BINARY format, containing the output of a call to the Windows API function CryptProtectData. You can create the encrypted credentials and store them in the registry with the ASP.NET Set Registry console application(Aspnet_setreg.exe), which uses CryptProtectData to accomplish the encryption. To download Aspnet_setreg.exe, along with the Visual C++ source code and documentation, visit the Web site www.asp.net and search for "aspnet_setreg". You should configure access to the key storing the encrypted credentials so that access is provided only to Administrators and SYSTEM. Because the key will be read by the ASP.NET process running as SYSTEM, you should set the following permissions: Administrators:F
SYSTEM:F CREATOR OWNER:F ProcessAccount:R This provides two lines of defense to protect the data:
The ACL permissions require the identity accessing the data to be an Administrator. An attacker must run code on the server (CryptUnprotectData) to recover the credentials for the account.
Most Web sites need to selectively restrict access to some portions of the site. You can think of a Web site as somewhat analogous to an art gallery. The gallery is open for the public to come in and browse, but there are certain parts of the facility, such as the business offices, that are accessible only to people with certain credentials, such as employees. When a Web site stores its customers' credit card information in a database, for example, access to the database must be restricted. ASP.NET security features help you address this and many other security issues. ASP.NET, in conjunction with Microsoft Internet Information Services (IIS), can authenticate user credentials such as names and passwords using any of the following authentication methods:
Windows: Basic, digest, or Integrated Windows Authentication (NTLM or Kerberos). Microsoft Passport authentication Forms authentication Client Certificate authentication
ASP.NET controls access to site information by comparing authenticated credentials, or representations of them, to NTFS file system permissions or to an XML file that lists authorized users, authorized roles (groups), or authorized HTTP verbs. This section and the following sections describe the specifics of ASP.NET security. For more information about the types of security attacks Web sites experience and how you can help protect your site from attack, see Security Considerations for ASP.NET Web Applications. In This Section How ASP.NET Security Works Provides an overview of ASP.NET security. ASP.NET Architecture Provides an overview of ASP.NET infrastructure and subsystem relationships, as related to security. ASP.NET Data Flow Describes the security data flow for two common scenarios. ASP.NET Authentication Describes ASP.NET authentication providers. ASP.NET Authorization Describes two fundamental ways to authorize access to a resource. ASP.NET Impersonation Describes how and when to use ASP.NET Impersonation. Designing Secure ASP.NET Applications Describes how to create ASP.NET applications with incorporated security. ASP.NET Application Security in Hosted Environments Describes ASP.NET security features for multi-application Web servers.
On This Page
Expand all | Collapse all SUMMARY This article describes different ways to implement impersonation in an ASP.NET application.
Back to the top MORE INFORMATION If you want to impersonate a user on a thread in ASP.NET, you can use one of the following methods, based on your requirments:
Impersonate the IIS authenticated account or user Impersonate a specific user for all the requests of an ASP.NET application Impersonate the authenticating user in code Impersonate a specific user in code
Note You can use the following code to determine what user the thread is executing as:
System.Security.Principal.WindowsIdentity.GetCurrent().Name
Note The identity of the process that impersonates a specific user on a thread must have the "Act as part of the operating system" privilege. By default, the Aspnet_wp.exe process runs under a computer account named ASPNET. However, this account does not have the required privileges to impersonate a specific user. You receive an error message if you try to impersonate a specific user. This information applies only to the .NET Framework 1.0. This privilege is not required for the .NET Framework 1.1.
Grant the "Act as part of the operating system" privilege to the ASPNET account (the least privileged account).
Note Although you can use this method to work around the problem, Microsoft does not recommend this method.
Change the account that the Aspnet_wp.exe process runs under to the System account in the <processModel> configuration section of the Machine.config file.
'Insert your code that runs under the security context of the authenticating user here.
impersonationContext.Undo()
Visual C# .NET
((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
//Insert your code that runs under the security context of the authenticating user here.
impersonationContext.Undo();
Visual J# .NET
((System.Security.Principal.WindowsIdentity)get_User().get_Identity()).Impersonate();
//Insert your code that runs under the security context of the authenticating user here.
impersonationContext.Undo();
<%@ Page Language="VB" %> <%@ Import Namespace = "System.Web" %> <%@ Import Namespace = "System.Web.Security" %> <%@ Import Namespace = "System.Security.Principal" %> <%@ Import Namespace = "System.Runtime.InteropServices" %>
Declare Function LogonUserA Lib "advapi32.dll" (ByVal lpszUsername As String, _ ByVal lpszDomain As String, _ ByVal lpszPassword As String, _ ByVal dwLogonType As Integer, _ ByVal dwLogonProvider As Integer, _ ByRef phToken As IntPtr) As Integer
Declare Auto Function DuplicateToken Lib "advapi32.dll" ( _ ByVal ExistingTokenHandle As IntPtr, _ ByVal ImpersonationLevel As Integer, _ ByRef DuplicateTokenHandle As IntPtr) As Integer
Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long
Public Sub Page_Load(ByVal s As Object, ByVal e As EventArgs) If impersonateValidUser("username", "domain", "password") Then 'Insert your code that runs under the security context of a specific user here. undoImpersonation() Else 'Your impersonation failed. Therefore, include a fail-safe mechanism here. End If End Sub
Private Function impersonateValidUser(ByVal userName As String, _ ByVal domain As String, ByVal password As String) As Boolean
Dim tempWindowsIdentity As WindowsIdentity Dim token As IntPtr = IntPtr.Zero Dim tokenDuplicate As IntPtr = IntPtr.Zero impersonateValidUser = False
If RevertToSelf() Then
If LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, token) <> 0 Then If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then tempWindowsIdentity = New WindowsIdentity(tokenDuplicate) impersonationContext = tempWindowsIdentity.Impersonate() If Not impersonationContext Is Nothing Then impersonateValidUser = True End If End If End If End If If Not tokenDuplicate.Equals(IntPtr.Zero) Then CloseHandle(tokenDuplicate) End If If Not token.Equals(IntPtr.Zero) Then CloseHandle(token) End If End Function
<%@ Page Language="C#"%> <%@ Import Namespace = "System.Web" %> <%@ Import Namespace = "System.Web.Security" %> <%@ Import Namespace = "System.Security.Principal" %> <%@ Import Namespace = "System.Runtime.InteropServices" %>
<script runat=server> public const int LOGON32_LOGON_INTERACTIVE = 2; public const int LOGON32_PROVIDER_DEFAULT = 0;
WindowsImpersonationContext impersonationContext;
[DllImport("advapi32.dll")] public static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);
public void Page_Load(Object s, EventArgs e) { if(impersonateValidUser("username", "domain", "password")) { //Insert your code that runs under the security context of a specific user here. undoImpersonation(); } else { //Your impersonation failed. Therefore, include a fail-safe mechanism here. } }
if(RevertToSelf()) { if(LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0) { if(DuplicateToken(token, 2, ref tokenDuplicate) != 0) { tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); impersonationContext = tempWindowsIdentity.Impersonate(); if (impersonationContext != null) { CloseHandle(token); CloseHandle(tokenDuplicate); return true; } } } } if(token!= IntPtr.Zero) CloseHandle(token); if(tokenDuplicate!=IntPtr.Zero) CloseHandle(tokenDuplicate); return false; }
Visual J# .NET
<%@ Page language="VJ#" %> <%@ Import Namespace="System.Web" %> <%@ Import Namespace="System.Web.Security" %> <%@ Import Namespace="System.Security.Principal" %> <%@ Import Namespace="System.Runtime.InteropServices" %>
<script runat=server> public static int LOGON32_LOGON_INTERACTIVE = 2; public static int LOGON32_PROVIDER_DEFAULT = 0;
WindowsImpersonationContext impersonationContext;
/** @attribute DllImport("advapi32.dll") */ public static native int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, System.IntPtr[] phToken);
/** @attribute DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true) */ public static native int DuplicateToken(System.IntPtr hToken, int impersonationLevel, System.IntPtr[] hNewToken);
public void Page_Load(Object s, System.EventArgs e) { if(impersonateValidUser("username", "domain", " password")) { //Insert your code that runs under the security context of a specific user here. undoImpersonation(); } else { //Your impersonation failed. Therefore, include a fail-safe mechanism here. } }
private boolean impersonateValidUser(String userName, String domain, String password) { WindowsIdentity tempWindowsIdentity; System.IntPtr[] token = new System.IntPtr[1]; System.IntPtr[] tokenDuplicate = new System.IntPtr[1];
if(RevertToSelf()) { if(LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, token) != 0) { if(DuplicateToken(token[0], 2, tokenDuplicate) != 0) { tempWindowsIdentity = new WindowsIdentity(tokenDuplicate[0]); impersonationContext = tempWindowsIdentity.Impersonate(); if (impersonationContext != null) { CloseHandle(tokenDuplicate); CloseHandle(token);
Note The identity of the process that impersonates a specific user on a thread must have the "Act as part of the operating system" privilege if the Aspnet_wp.exe process is running on a Microsoft Windows 2000-based computer. The "Act as part of the operating system" privilege is not required if the Aspnet_wp.exe process is running on a Windows XP-based computer or on a Windows Server 2003-based computer. By default, the Aspnet_wp.exe process runs under a computer account named ASPNET. However, this account does not have the required privileges to impersonate a specific user. You receive an error message if you try to impersonate a specific user. .
Grant the "Act as part of the operating system" privilege to the ASPNET account.
Change the account that the Aspnet_wp.exe process runs under to the System account in the <processModel> configuration section of the Machine.config file.
http://www.codeproject.com/Articles/107940/Back-to-Basic-ASP-NET-Runtime-Impersonation
Today I got a question from one of the developers at my main customer. The question was how to move an uploaded file from an ASP.NET server to a file server on the network. The answer is of course by impersonating. In this post Ill explain how you can make ASP.NET impersonation and in more details how to make runtime impersonation.
Impersonation in ASP.NET
When we are doing I/O operations, the operation system makes security checks to understand if the user is authorized to do the operation. The same thing happens when you try to do operations on another machine in your network. Impersonation in ASP.NET occurs when ASP.NET executes code in the context of an authenticated and authorized user. By default, ASP.NET run in the ASPNET account. By using impersonation we can impersonate the ASPNET account to another account that has access to resources which arent granted in the internet security permission. One way to impersonate a user is by using the identity element in the web.config. When you use the following code in your web.config, ASP.NET impersonates to the authenticated user or to an anonymous internet user account:
Collapse | Copy Code
If you want to impersonate to a specific user you can use the following configuration:
Collapse | Copy Code
<identity impersonate="true"
Runtime Impersonation
At my customer the previous configuration examples werent an option. The second way to impose impersonation is by runtime. This option can be achieved by using the System.Security.Principal and the WindowsIdentity class. TheWindowsIdentity class has a method that makes impersonation and returns a WindowsImpersonationContext. The problem with this class is that you need to supply to it an IntPtr which is a security access token of the user that you wish to impersonate to. The solution is to use P/Invoke and call the LogonUser Win32 API. After you get the impersonation context you can run the network operations that you seek to perform. After you finish to do your operations you need to undo the impersonation. The following code shows an example of an impersonation service class:
Collapse | Copy Code
using System.Security.Principal; namespace WebApplication1 { public class ImpersonationService { #region Consts public const int LOGON32_LOGON_INTERACTIVE = 2; public const int LOGON32_PROVIDER_DEFAULT = 0; #endregion #region External API [DllImport("advapi32.dll", SetLastError = true)] public static extern int LogonUser( string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken ); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool RevertToSelf(); [DllImport("kernel32.dll", SetLastError = true)] public static extern int CloseHandle(IntPtr hObject); #endregion #region Methods public void PerformImpersonatedTask(string username, string domain, string password, int logonType, int logonProvider, Action methodToPerform) { IntPtr token = IntPtr.Zero; if (RevertToSelf()) { if (LogonUser(username, domain, password, logonType, logonProvider, out token) != 0) { var identity = new WindowsIdentity(token); var impersonationContext = identity.Impersonate(); if (impersonationContext != null) { methodToPerform.Invoke(); impersonationContext.Undo(); } } else { // do logging } } if (token != IntPtr.Zero) { CloseHandle(token); } } #endregion } }
using System; using System.IO; namespace WebApplication1 { public partial class _Default : System.Web.UI.Page { #region Page Events private void Page_Load(object sender, System.EventArgs e) { var service = new ImpersonationService(); service.PerformImpersonatedTask("username", "domain", "password", ImpersonationService.LOGON32_LOGON_INTERACTIVE, ImpersonationService.LOGON32_PROVIDER_DEFAULT, new Action(MethodToPerform)); } #endregion #region Methods public void MethodToPerform() { var serverPath = @"\\ServerName\test"; var dirInfo = new DirectoryInfo(serverPath); Response.Write(dirInfo.Exists); } #endregion } }
Summary
In the post I showed a simple way to implement a class that impersonate to a relevant account in order to achieve some functionality that internet security permissions dont allow. You should consider to use the web.config instead since it does all the communication with Win32 API instead of the supplied code. The impersonation isnt limited only to ASP.NET and can be used also in other frameworks.
http://www.west-wind.com/weblog/posts/2005/May/18/Understanding-ASPNET-ImpersonationSecurity
Understanding ASP.NET Impersonation Security
64 comments
With ASP.NET applications its usually recommended that you run under the ASP.NET user account without impersonation. The idea is that this account is a low rights account that can do little damage if for some reason the machine is compromised.
I get a lot of questions that are related to access rights for ASP.NET (and classic ASP) applications and 90% of the time the issue is related to the user underlying system account that IIS is running under. There tends to be some confusion in understanding just what account your application runs under and what rights need to be set to access this or that resource on the server. Usually the scenario involves a file on disk that needs to be accessed in read/write format.
First, to find out exactly which account your ASP.NET application runs under you can simply echo back Environment.UserName which is useful if youre not that familiar with IIS or youre running on an ISP box and they failed to provide you this information You can stick:
into an ASPX page will tell you exactly which account the page is running under.
In all of my applications I tend to add one page in my admin directory that displays a bunch of information about the running environment. This page is fairly generic and its self contained so its easy to copy between projects (although I usually add a few admin functions). The following comes from my West Wind Web Storeapp:
Notice that there are two accounts that are listed: The underlying system account and the logged on user. These are two different things and the underlying system account is what Environment.UserName returns. The Logged on User in turn gets returned by User.Identity.Name which is a much higher level informational abstract that can be used by your application to figure out which user authenticated.
So, Environment.UserName returns to you the current Impersonation of the ASP.NET thread that you are running under. User.Identity.Name only returns you the authentication that ASP.NET has figured out based on the current request. So above I accessed a page with a Windows Login, hence it shows my username rstrahl. If I displayed this on a standard non-logged in Web page it would show the IUSR_ account the Internet Guest account.
What were interested in here is the underlying account which really determines what rights your ASP.NET application has to access system resources. Now, the account that ASP.NET runs under depends on the Windows version, how you have configured your Web server and what settings are configured in your Web.Config file.
Web Server If you are running IIS 5, the default account that IIS runs ASP.NET under is the ASPNET account. The actual account is configurable in machine.config. The ASPNET account is an account that ASP.NET installs and has fairly low rights. One big drawback in IIS 5 is that this account cannot be customized for each application the ProcessModel key that sets this account lives in machine.config and cannot be overridden in web.config, so you basically end up with having the same account run all your ASP.NET applications.
On IIS 6 things are much more configurable. The default account used is NETWORK SERVICE but its actually configurable via a new feature called an Application Pool. With IIS 6 all processing no longer occurs in the INETINFO.EXE process, but rather is offloaded into one or more daemon process (w3wp.exe). You can configure one or more of these processes by adding Application Pools in the IIS management Console. You can then add virtual directories to specific Application Pools. Application Pools are quite configurable and one of the key options is the ability to specify an Identity that this process runs under.
By default this is Network Service, but you can change this to any account you see fit. Network Service is used because like the ASPNET account on IIS 5 its a low rights account which by default has access to next to nothing on your machine.
Web.Config The next level of configuration for your ASP.NET application is web.config which allows you to specify the type of Impersonation is used with your ASP.NET application. By default ASP.NET unlike classic ASP doesnt use any impersonation. Impersonation is the concept of the application pretending to be a different account than the underlying account running the application ie. ASPNET or NETWORK SERVICE. In classic ASP IIS impersonated the logged on user which usually was the Internet Guest Account (IUSR_) or if you were logged in via Windows or Basic or Digest Authentication the user that was actually logged on.
ASP.NET works differently. It runs under the account that the process was started under originally. This means ASPNET or NETWORK SERVICE or if youre using IIS 6 the account you have specifically configured for your Application Pool. So while ASP.NET provides different Authentication models to let you see who logged on to secure areas of your Web site via Forms or Windows Authentication, the underlying account in this scenario never changes. The Authentication is merely a tool for your application, but it doesnt change the underlying security context.
This is a good thing and something that wasnt really easily possible with classic ASP. You can however revert to the Impersonation model that works just like classic ASP by turning Impersonation on in web.config:
<system.web> <location path="admin"> <system.web> <identity impersonate="true" /> <!-- WS: Allow only Authenticated users --> <authorization> <!-- allow users="*"/ --> <deny users="?" /> </authorization> </system.web> </location> </system.web>
This option goes back to setting the underlying system account to the specific account that is logged on: The anonymous Internet Guest account or who ever is logged on. In this mode ASP.NET impersonates the current Web user. It gets this token from IIS which passes this account token on the ISAPI thread that originally fires off this request.
While this is not recommended generally, it can be a useful. I like to use this sort of setup for my Admin directories of an application. This makes it possible for my main application to run under a low privilege account, but allows my Admin paths to elevate the rights for users that have authentication to access a specifc directory of the application. Once the impersonation is in effect I can run under a specific admin account which will have rights for example to write to data in my application directory.
Directory Rights So now we know which account is actually driving the application. The next step is to dole out permissions for your application in such a way that you can access the data you might need without compromising security. Which is not always easy and often means a compromise between security and flexibility.
The most common scenario I see is people needing to write some sort of output to the application directory. Usually people have problems when they need to write or access data in the Web Directory. Once youve figured out which account your application is actually running under you can start adjusting permissions as needed. So if you need to write to a file in your Web directory you need to give NETWORK SERVICE/ASPNET or whatever your app runs under rights to access this file. As always be very careful with this task and think ahead of how any security changes affect your security environment beyond just what you need to accomplish to access the file.
The easy way is to go in and give the required account the appropriate rights give NETWORK SERVICE WRITE access for example in the directory. But again be careful here. General security guidelines would say giving the operating account WRITE access might permit a hacker to upload ASPX pages and execute them. True, but you have to weigh that risk against the need to do what you need to do which might be to write an XML log file or write to web.config etc.
If you do need to open up security try to minimize your surface area. If you need access to a specific file give access only to that file. Even better move any files you are accessing out of the script directories and into a separate folder. Remove anonymous access rights (assuming you dont want Web site users to see the content) and then add read/write access for NETWORK SERVICE.
Heres another little trick in this regard: One thing all my Web applications do is allow configuration settings to be stored in Web.Config using a custom Configuration class that allows reading and writing and serializing configuration data to various output formats. Web.Config lives in the application root of course along with most of the ASPX files. So I need WRITE access for my NETWORK SERVICE account right?
Well, no. <g> I can actually cheat by designing my in such a way that configuration settings are only updated from an Admin directory. I can set up impersonation on the Admin directory, disable access for anonymous users and voila I can now log in and gain elevated rights that do allow me write to the web.config file without changing the permissions (beyond making sure that my user account or group has WRITE access) on the default files. There's more detail about this scenario in this previous entry.
Note that you can restrict access to a directory either declaratively in web.config as a I showed in the previous section, or you can do it explicitly by setting directory permissions on a directory. If you have an Admin directory you can simply remove the Internet Guest account from the file permissions which will disallow all anonymous access to the directory without changing anything in you Web.config. In fact this may be desirable anyway if you store any sensitive data in the directory. The <location> tag only applies to files that ASP.NET processes (ASPX, ASMX etc.) but not static files like HTML, XML etc. So in some cases you may need directory rights anyway.
Programmatic Impersonation? Note that it's also possible to affect the application's security environment programmatically from within your ASP.NET application by using some Windows API calls. There's more info on this topic in this previous entry about Using Programmatic Impersonation from an ASP.NET Page.
http://www.west-wind.com/weblog/posts/2005/Feb/24/Using-programmatic-Impersonation-from-anASPNET-Page
Using programmatic Impersonation from an ASP.NET Page
123 comments
Several times I've heard the following question asked: I have an ASP.NET application and I need access to network resources that my account running ASP.NET does not have. How can I change the permissions at runtime without setting up Impersonation or using a high privilige account for my ASP.NET user account? In short, how can you raise permissions of an ASP.NET request at runtime to perform some task that requires rights that the standard account ASP.NET runs under cannot handle? To accomplish this you can use various system API calls (LogonUser, ImpersonateLoggedOnUser and RevertToSelf) to change the currently active account ASP.NET runs under. This would be Environment.UserName as opposed to Page.User. Environment.UserName returns the threads currently logged on user. Page.User returns the name that ASP.NET verifies through Authentication and this user in most cases is independent of the Windows logon that is running the current thread. For anonymous requests Page.User is blank, while Environment.User will be NETWORK SERVICE (or ASPNET on IIS5). The only time when Page.User reflects Environment.User is when Impersonation is enabled in which case the ASP.NET automatically changes the impersonation on the underlying ASP.NET thread to match of who's logged on. For anonymous users this will be the IUSR_ account or if logged on the user who logged on. With the API calls mentioned above you can change the thread's security environment. The idea is, you can log on as a user, change the impersonation to that environment, do your thing then revert back. Let's look at how to do this first before going over some caveats. Given that you have rights to use PInvoke calls to make these API calls (NETWORK SERVICE generally does have these rights) the following code can be used to accomplish this:
using using using using using using using using using using
System; System.Collections; System.ComponentModel; System.Data; System.Drawing; System.Web; System.Web.SessionState; System.Web.UI; System.Web.UI.WebControls; System.Web.UI.HtmlControls;
using System.Runtime.InteropServices; using System.Text; namespace Westwind.WebStore.Demos { /// <summary> /// Summary description for Test. /// </summary> public class Test : System.Web.UI.Page { const const const const const int int int int int LOGON32_LOGON_INTERACTIVE LOGON32_LOGON_NETWORK LOGON32_LOGON_BATCH LOGON32_LOGON_SERVICE LOGON32_LOGON_UNLOCK = = = = = 2; 3; 4; 5; 7;
const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8; const int LOGON32_LOGON_NEW_CREDENTIALS = 9; const int LOGON32_PROVIDER_DEFAULT = 0; [DllImport("advapi32.dll", SetLastError=true)] public static extern int LogonUser( string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken ); [DllImport("advapi32.dll", SetLastError=true)] public static extern int ImpersonateLoggedOnUser( IntPtr hToken ); [DllImport("advapi32.dll", SetLastError=true)] static extern int RevertToSelf(); [DllImport("kernel32.dll", SetLastError=true)] static extern int CloseHandle(IntPtr hObject); private void Page_Load(object sender, System.EventArgs e) { Response.Write( Environment.UserName + "<hr>"); IntPtr lnToken; int TResult = LogonUser("ricks",".","supersecret", LOGON32_LOGON_NETWORK,LOGON32_PROVIDER_DEFAULT, out lnToken); if ( TResult > 0 ) { ImpersonateLoggedOnUser(lnToken); StringBuilder sb = new StringBuilder(80,80); uint Size = 79; Response.Write( Environment.UserName + " - " + this.User.Identity.Name + "<hr>"); RevertToSelf(); Response.Write("<hr>" + Environment.UserName); CloseHandle(lnToken); } else { Response.Write("Not logged on: " + Environment.UserName); }
return; } }
To use this code change the username and password in the call to LogonUser to a valid local or domain account (see MSDN Docs for exact syntax to use for domain accounts and machine names). When you run this code you should see: NETWORK SERVICE ricks NETWORK SERVICE which corresponds to the original account the page is running under, the Impersonation that I applied, and then after I reverted back to the original account. Note that you should make sure to close the Token handle returned after you are done with your request, preferrably in a Finally section so you don't leak handles. Actually you don't have to revert back - ASP.NET assigns security to the ASP.NET thread before your request starts, so Revert is not really required.
Caveats
Now is this a good idea to do this in your code? Not really, because it's obviously a potential security risk. There are two things that are a problem here. First you need to run under an account that has rights to make PInvoke calls which is by no means guaranteed. Many multi-hosted ISPs will set up very low right accounts for their sites so that you may not be able to actually perform these tasks. OTOH, it's unlikely that in an ISP scenario you would actually need access to additional resources that require impersonation. Second and maybe more importantly using LogonUser requires that you use a password and that password has to come from somewhere. This means somewhere in your system you have to store this password and retrieve that password which can be compromised and then potentially be used to further penetrate security. A fairly far fetched hack scenario that would require somebody pretty damn familiar with your architecture, but still a threat.
You can also impersonation system accounts like SYSTEM and NETWORK SERVICE which don't require passwords (pass "" for the password), but most likely these accounts are not what you need to get your job done - for example access another machine on the network.
Alternatives
Ultimately the solution to this problem is to set up your ASP.NET application with the right account with the exact rights it needs. While I think security is important it's always been my feeling that if someone can penetrate your network and compromise ASP.NET pages - they're in too far already to not be able to do the rest (like changing web.config and stepping down security), so I am usually not opposed to running ASP.NET in slightly elevated security modes to match my needs. Another option for those one off requests is to use Impersonation and Windows Security in a separate directory. I do this in several of my applications where several admin requests require elevated rights. These pages sit off in a seperate directory with anonymous access off and Impersonation enabled in a separate web.config (or a Location section in the main web.config). An example of this is my Configuration class manager which has the capability to write Config changes back into Web.Config. I don't want to give NETWORK SERVICE rights to write any files in my main application directory, so I have my Configuration page off in an Admin directory. Admin requires Windows Security and then uses Impersonation, which means it runs under my Admin account on the server once I've logged in with my username and password. <location path="admin"> <system.web> <identity impersonate="true" /> <!-- WS: Allow only Authenticated users --> <authorization> <!-- allow users="*"/ --> <deny users="?" /> </authorization> </system.web> </location> I can set this up in a couple of ways. Here I do it all through ASP.NET's impersonation and security settings which deny access to non-authenticated users to these Admin pages. I could also set this up with directory security on the Admin directory and simply remove IUSR_ to achieve the same effect although I would still need the impersonate setting in web.config. This works well as long as the requests in question can be easily isolated and users are that are accessing these requests indeed can authenticate, which is not always the case. Use with care... and don't impersonate GrandMa - it ain't nice!
http://odetocode.com/resources/110.aspx
Impersonation in ASP.Net
By default, ASP.NET executes in the security context of a restricted user account on the local machine. Sometimes you need to access network resources such as a file on a shared drive, which requires additional permissions. One way to overcome this restriction is to use impersonation. With impersonation, ASP.NET can execute the request using the identity of the client who is making the request, or ASP.NET can impersonate a specific account you specify in web.config.
Implementing Impersonation in an ASP.NET Application Choosing Resource Access Identities - Identity Impersonation In ASP.NET MSDN: ASP.NET Impersonation HOW TO: Secure an ASP.NET Application by Using Windows Security On being someone else
http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q306158
http://www.dnzone.com/go?504
Authorization
In ASP.NET, it is possible to allow authorization to the application when you make additional settings available within the Web.config file. You can allow certain users or certain groups access to these additional settings. The following examples describe this capability. To allow access to all users found in the Windows NT Group that is called "Managers," use the following code:
<configuration> <system.web> <authorization> <allow roles="domainname\Managers" /> <deny users="*" /> </authorization> </system.web> </configuration>
To allow access to only certain users, use the following code:
<configuration> <system.web> <authorization> <allow users="domainname\user1,domainname\user2,domainname\user3" /> <deny users="*" /> </authorization> </system.web> </configuration>
Note You can reference multiple roles or users when you use a comma-separated list.
1.
2.
In Visual Studio, view the Web.config file for the WindowsSite project. Add the following element after the authentication element:
4. 5.
Save Web.config. Run the project. Confirm that the page is displayed with the following message (note that the ASP.NET execution engine will use your Windows credentials to access resources on your behalf):
You are: Your Windows user name This page runs as: Your Windows user name
6.
http://www.dotnet-guide.com/impersonation.html
authenticated user. In either case, permissions for the account are checked in the Windows ACL for the resource(s) that a user requests, and a resource is only available if the account they are running under is valid for that resource.
http://www.4guysfromrolla.com/articles/031204-1.aspx
- continued -
Whenever a user logs on to an application, the user is first authenticated and then authorized. With ASP.NET Web applications, the users requesting a page are, by default, anonymous. There are different techniques available for determining the identity of an anonymous user, which we'll examine in this article. Realize, however, that, by default, Web applications allow for anonymous access.
This article provides a high-level overview of the authentication and authorization models available in an ASP.NET Web application.
The following shows the sequence of authentication and authorization actions performed by IIS and ASP.NET on an incoming request.
1. 2. 3. 4.
The incoming request is first checked by IIS. If the IP address from where the request is sought is not allowed access to the domain, IIS denies the request. IIS allows anonymous access by default and hence requests are automatically authenticated. However, this can be overridden for each application within IIS. Next in the sequence IIS performs this authentication, if it has been configured to do so. The authenticated user request is passed to ASP.NET. ASP.NET checks whether Impersonation is enabled or not. By default impersonation is not enabled in ASP .NET. Generally, some applications require impersonation for ASP compatibility and Windows server authentication. (By default, the ASP.NET engine operates under the
ASPNET user account. Impersonation is a means by which you can have the ASP.NET engine operates under the authenticated user's o o
If impersonation is enabled, ASP.NET executes with the identity of the entity on behalf of which it is performing executing the task. If impersonation is not enabled, the application runs with the privileges of the
user account. For more information refer to INFO: Implementing Impersonation in an ASP.NET Application.)
5.
Finally, the identity that has been authenticated and checked for in the previous steps is used to request resources from the OS. ASP.NET uses two forms of authorization:
o o
FileAuthorization - relies on NTFS file permissions for granting access. UrlAuthorization - in the the
Web.config file you can specify the authorization rules for various directories or files using <authorization> element.
6.
If access is granted (successful authorization), ASP .NET returns the user's request through IIS.
Authentication Providers
ASP.NET provides three ways to authenticate a user:
It is the job of the authentication provider to verify the credentials of the user and decide whether a particular request should be considered authenticated or not. The authentication scheme an ASP.NET Web application uses can be configured in its refer to ASP.NET Authentication.
There are four different kinds of Windows authentication options available that can be configured in IIS:
Anonymous Authentication: IIS doesn't perform any authentication check. IIS allows any user to access the ASP .NET application. Basic Authentication: For this kind of authentication, a Windows user name and password have to be provided to connect. However, this information is sent over the network in plain text and hence this is an insecure kind of authentication. Basic Authentication is the only mode of authentication older, non-Internet Explorer browsers support. Digest Authentication: It is same as Basic Authentication but for the fact that the password is hashed before it is sent across the network. However, to be using Digest Authentication, we must use IE 5.0 or above. Integrated Windows Authentication: In this kind of authentication technique, passwords are not sent across the network. The application here uses either the kerberos or challenge/response protocols to authenticate users. Kerberos, a network authentication protocol, is designed to provide strong authentication for client-server applications. It provides the tools of authentication and strong cryptography over the network to help to secure information in systems across entire enterprise.
For more information on these four different types of IIS authentication consult IIS Authentication Methods Available for Windows 2000
Passport uses an encrypted cookie mechanism to identify and indicate authenticated users. If the users have already been signed into passport when they visit the application page, ASP.NET will consider them as authenticated. Otherwise, the users will be redirected to Passport servers to login. Upon successful login, the user is redirected back to the ASP.NET Web page that they initially tried to access. If you use Hotmail you already have a Passport account and are familiar with the sign-in process from an end-user's perspective.
Web.config file:
<!-- For Windows Authentication... --> <authentication mode="windows"> <!-- For Passport Authentication... --> <authentication mode="passport"> <!-- For Forms Authentication... --> <authentication mode="forms">
ASP .NET also supports custom authentication providers. Setting the authentication
mode for the application to "none" and then writing our own
code to perform authentication can achieve this. For example, we might install an ISAPI filter in IIS that compares incoming requests' IP address with a list of source IP addresses and considers the request to be authenticated only if the IP address is found in the source list. In this example, we can set the authentication mode to
"none" in Web.config file. This will prevent any of the default authentication providers from being triggered.
Configuring Authorization
There are two forms of authorization available in ASP.NET:
FileAuthorization - relies on NTFS file permissions for granting access. UrlAuthorization - in the the
Web.config file you can specify the authorization rules for various directories or files using <authorization> element.
FileAuthorization works by checking the access rights of the user account that the ASP.NET engine is operating under against the NTFS file permissions. That is, if a user is requesting the file the ASP.NET engine is operating under has read permissions to that file. Recall that by default the ASP.NET engine operates under the account; however, this can be changed through impersonation.
C:\Inetpub\wwwroot\SomeFile.aspx, a check is made to ensure that the user account ASPNET user
Impersonation is a technique that allows the ASP.NET process to act as the authenticated user, or as an arbitrary specified user. ASP.NET impersonation is controlled by entries in the applications
ASPNET. You can, however, configure the account that ASP.NET uses by changing userName attribute of the processModel section in the machine.config file. That is, you could modify this setting so that
the ASP.NET engine always operated under, say, the Administrator account (NOT A GOOD IDEA!!!!, but it can be done).
Using impersonation, the ASP.NET engine will operate under the identity IIS passes to it. If anonymous access is allowed in IIS, ASP.NET will run under the
IUSR_ComputerName account that IIS uses. If anonymous access is not allowed, ASP.NET will take on the identity of the authenticated Web.config file for impersonation, refer to ASP.NET Impersonation.
user. Impersonation can also be configured so that a single, static user account is used for all requests. For more information on using impersonation, including how to configure the
http://msdn.microsoft.com/en-us/library/wce3kxhd(vs.71).aspx
ASP.NET Authorization
.NET Framework 1.1 Other Versions
The purpose of authorization is to determine whether an identity should be granted the requested type of access to a given resource. There are two fundamental ways to authorize access to a given resource:
File authorization File authorization is performed by the FileAuthorizationModule, and is active when you use Windows authentication. It does an access control list (ACL) check of the .aspx or .asmx handler file to determine if a user should have access. Applications can further use impersonation to get resource checks on resources that they are accessing. For more information about impersonation, see ASP.NET Impersonation.
URL authorization URL authorization is performed by the URLAuthorizationModule, which maps users and roles to pieces of the URL namespace. This module implements both positive and negative authorization assertions. That is, the module can be used to selectively allow or deny access to arbitrary parts of the URL namespace for certain sets, users, or roles.
The URLAuthorizationModule is available for use at any time. You only need to place a list of users and/or roles in the <allow> or <deny> elements of the <authorization>section of a configuration file.
To establish the conditions for access to a particular directory, you must place a configuration file that contains an <authorization> section in that directory. The conditions set for that directory also apply to its subdirectories, unless configuration files in a subdirectory override them. The general syntax for this section is as follows.
Attribute
roles
Description
Identifies a targeted role for this element. The associated IPrincipal object for the request determines the role membership. You can attach arbitraryIPrincipal objects to the context for a given request and they can determine role membership in whatever way you like. For example, the defaultWindowsPrincipal class uses Microsoft Windows NT groups to determine role membership. Identifies the targeted identities for this element. Defines the HTTP verbs to which the action applies, such as GET, HEAD, and POST.
users verbs
Anonymous users are also denied. The following example grants access to Kim and members of the Admins role, while denying it to John and all anonymous users:
<authorization> <allow users="Kim"/> <allow roles="Admins"/> <deny users="John"/> <deny users="?"/> </authorization>
Both users and roles can refer to multiple entities by using a comma-separated list, as shown in the following example.
Identity
* ?
Description
Refers to all identities Refers to the anonymous identity
To allow John and deny everyone else, one might construct the following configuration section.
<authorization>
<authorization> <allow verb="GET" users="*"/> <allow verb="POST" users="Kim"/> <deny verb="POST" users="*"/> </authorization>
Rules are applied using the following heuristics:
Rules contained in configuration files at lower directory levels take precedence over rules at higher directory levels. The system determines which rule takes precedence by constructing a merged list of all rules for a URL, with the most recent (nearest in the hierarchy) rules at the head of the list. Given a set of merged rules for a URL, the system starts at the head of the list and checks rules until the first match is found. Note that the default configuration for ASP.NET contains an <allow users="*"> element, which authorizes all users. If no rules match, the request is allowed unless otherwise denied. If a match is found and the match is a <deny> element, it returns the 401 status code. Applications or sites can easily configure a <deny users="*"> element at the top level of their site or application to prevent this behavior. If an <allow> matches, the module does nothing and lets the request be processed further.
There is also a <location> tag that you can use to specify a particular file or directory to which settings wrapped by that tag (between <location> and </location> tags) should apply.
http://msdn.microsoft.com/en-us/library/xh507fc5(vs.71).aspx
ASP.NET Impersonation
.NET Framework 1.1 Other Versions
When using impersonation, ASP.NET applications can optionally execute with the identity of the client on whose behalf they are operating. The usual reason for doing this is to avoid dealing with authentication and authorization issues in the ASP.NET application code. Instead, you rely on Microsoft Internet Information Services (IIS) to authenticate the user and either pass an authenticated token to the ASP.NET application or, if unable to authenticate the user, pass an unauthenticated token. In either case, the ASP.NET application impersonates whichever token is received if impersonation is enabled. The ASP.NET application, now impersonating the client, then relies on the settings in the NTFS directories and files to allow it to gain access, or not. Be sure to format the server file space as NTFS, so that access permissions can be set. Impersonation is disabled by default. For ASP compatibility, the user must explicitly enable impersonation. If impersonation is enabled for a given application, ASP.NET always impersonates the access token that IIS provides to ISAPI extensions. That token can be either an authenticated user token, or the token for the anonymous user (such as IUSR_MACHINENAME). The impersonation occurs regardless of the type of authentication being used in the application. Only application code is impersonated; compilation and configuration are read as the process token. The result of the compilation is put in the "Temporary ASP.NET files" directory. The account that is being impersonated needs to have read/write access to this directory. If an application is on a universal naming convention (UNC) share, ASP.NET will always impersonate the token provided to IIS to access that share unless a configured account is used. If an explicit configured account is provided, ASP.NET will use that account in preference to the IIS UNC token. Applications that do want per-request impersonation can simply be configured to impersonate the user making the request. Impersonation is disabled at the computer level by default and, unless overridden, all the application domains inherit this setting. You can enable impersonation by putting a configuration file in the application root directory. For more information about the ASP.NET configuration system, see ASP.NET Configuration.
As is the case with other configuration directives, this directive applies hierarchically. It is respected by nested applications in the hierarchy, unless explicitly overridden. The default value for this setting is as follows.
<impersonation enable="false"/>
A minimal configuration file to enable impersonation for an application might look similar to the following example.
userName="registry:HKLM\Software\AspNetIdentity,Name" password="registry:HKLM\Software\AspNetIdentity,Password"
The portion of the string after the keyword registry and before the comma indicates the name of the registry key that ASP.NET opens. The portion after the comma contains a single string value name from which ASP.NET will read the credentials. The comma is required, and the credentials must be stored in the HKLM hive. If the configuration format is incorrect, ASP.NET will not launch the worker process and the current account creation failure code path will be followed. The credentials must be in REG_BINARY format, containing the output of a call to the Windows API function CryptProtectData. You can create the encrypted credentials and store them in the registry with the ASP.NET Set Registry console application(Aspnet_setreg.exe), which uses CryptProtectData to accomplish the encryption. To download Aspnet_setreg.exe, along with the Visual C++ source code and documentation, visit the Web site www.asp.net and search for "aspnet_setreg". You should configure access to the key storing the encrypted credentials so that access is provided only to Administrators and SYSTEM. Because the key will be read by the ASP.NET process running as SYSTEM, you should set the following permissions: Administrators:F SYSTEM:F CREATOR OWNER:F ProcessAccount:R This provides two lines of defense to protect the data:
The ACL permissions require the identity accessing the data to be an Administrator. An attacker must run code on the server (CryptUnprotectData) to recover the credentials for the account.