With ASP.NET applications it’s 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 you’re not that familiar with IIS or you’re running on an ISP box and they failed to provide you this information… You can stick:
<%= Environment.UserName %>
<%= Environment.UserName %>
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 it’s self contained so it’s easy to copy between projects (although I usually add a few admin functions). The following comes from my West Wind Web Store app:
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 we’re 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.
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 it’s 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 it’s a low rights account which by default has access to next to nothing on your machine.
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 – doesn’t 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 you’re 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 doesn’t change the underlying security context.
This is a good thing and something that wasn’t 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:
<identity impersonate="true" />
<!-- WS: Allow only Authenticated users -->
<!-- allow users="*"/ -->
<deny users="?" />
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.
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 you’ve 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 don’t want Web site users to see the content) and then add read/write access for NETWORK SERVICE.
Here’s 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.
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.