Wednesday, 17 January 2007
Under ASP.NET 1.1, the ASP.NET engine is not (by default) responsible for the application's image, style sheet, and javascript files. These are served up directly by IIS. ASP.NET can be made responsible for these file types by playing with the ISAPI file mappings and maybe creating an HttpHandler or two.

This changes under ASP.NET 2.0.  The ASP.NET engine has responsibility for all these file types and is called when a request for one is made.

So what?  The files get served, the application works, who cares if IIS or ASP.NET does the work?

In many applications it wouldn't matter, but what happens if the application has been secured in web.config like so...

<authorization>
   <deny users="?" />
</authorization>

... and an anonymous user tries to access the site and gets redirected to the login page?

Under 1.1, since IIS was responsible for the css, javascript and image files, it wouldn't matter -- the page would be served as expected, showing all the images and properly referencing any stylesheets or script files.

Under 2.0, the anonymous user would see an unstyled page with blank image boxes. Any javascript calls to referenced files won't work either. Why? Because ASP.NET isn't going to serve up anything but the login page to an anonymous user -- because that is what it was told to do.

This is great for security, but can be a little frustrating when you KNOW you put the right path in for that image, and KNOW that the all h1 elements should be hot pink  and all you see is an unformatted mess.

Fortunately it is very easy to work around this so the public pages on a secured site show up properly.  All that needs to be done is to tell the application that anonymous users are allowed to access the content in certain folders. This can be done using a location element or two in the web.config file.  The following one allows anonymous users to access content in the unsecured_images folder...

<location path="unsecured_images">
    <system.web>
      <authorization>
        <allow users="*" />
      </authorization>
    </system.web>
  </location>

That's all there is to it.


Wednesday, 17 January 2007 22:29:44 (Eastern Standard Time, UTC-05:00)   #     Comments [1]  | 
Friday, 25 August 2006

Finding the Identity of the current user is one of those ASP.NET issues that causes confusion and consternation, especially given the different authentication schemes and application configurations available in ASP.NET.  In particular, HttpContext.Current.User, seems to be misunderstood.

Microsoft has provided a handy matrix outlining what can be expected under the different scenarios. Note that the last 3 tables are redundant -- they work exactly the same way no matter what form of Windows identification (Basic, Digest or Integrated) IIS is configured to use.  In any case, either this matrix is hard to find, or too confusing, so let's review the basics.

This post will cover ASP.NET 1.1.  Though the basics are the same in 2.0, there are some other things to discuss, which we'll tackle in a future post.

A freshly created web project in VS2003 (ASP.NET 1.1) has the following security settings in web.config...

<authentication mode="Windows" />
 
<authorization>
        <allow users="*" /> <!-- allow all users -->
</authorization>


The authorization element tells the app to let anyone in, making the authentication element pointless.

In this situation, the matrix tells us that we won't have any identity to look at since we'll have an anonymous user.  To test this, we build a web page that has a label that we will fill with the current user's name, using code like so....

CurrentUserIdentity.Text = HttpContext.Current.User.Identity.Name;

Run the app and sure enough,
HttpContext.Current.User.Identity.Name = ""

The user has no meaningful identity, so nothing is reported, we get the empty string.

Make a small change to the web.config file...

<authorization>
        <deny users="?" /> <!-- deny anonymous users -->
</authorization>

This tells the app to disallow ANONYMOUS users, it needs to authenitcate a user before it serves up the goodies.  Since our authentication mode is set for "windows", the user must have an account (domain or local) that can login to the server and has access to the web site.  Run the app again and now the app requests user credentials from the client's browser.  If the credentials are correct the page will be displayed, otherwise a prompt dialog box for the user id and password will be displayed.  Once correctly entered the page will be displayed, but this time it will show the user's domain and user name...

HttpContext.Current.User.Identity.Name = "somedomain\someuser"

Windows authentication is obviously useful for intranet sites where all the users are known to the server, but not so much for public sites. For public sites it makes sense to use Forms authentication. Under Forms Authentication the app will check a cookie (or a piece of the URL for a cookieless scheme) to see if the user has been authenticated.  This lets the developer implement a custom authentication scheme.  Putting the following in web.config tells the app to use Forms Authentication.

    <authentication mode="Forms">
        <forms name="FormsTicket"

          loginUrl="login.aspx"
          protection="All"
          timeout="20" path="/"
          requireSSL="false"
          slidingExpiration="true" />
    </authentication>

There are several ways user authentication can be implemented, the most common being looking up the user and a (hashed) password in a database.  But the key is setting the authentication cookie so the app knows that the user has been authenticated.  There are three methods in the FormsAuthentication class that will do this.  This one creates the cookie with the User's ID and redirects the user to the page they initially requested....

if(SomeMethodToAuthenticateUser(UserID, Password) == true)
{
FormsAuthentication.RedirectFromLoginPage(UserID, false);
}

This one sets the cookie and supplies the redirect URL, useful when redrection requires some more logic...

RedirectURL = FormsAuthentication.GetRedirectUrl(UserID, false);

This one just sets the cookie...

FormsAuthentication.SetAuthCookie(UserID, false);

All that matters is setting the cookie.  Without the cookie, the app thinks the user is anonymous and won't show itself.

So what shows up on our page?

HttpContext.Current.User.Identity.Name = UserID

Whatever the UserID set into the cookie was is what shows up as the User's name.  This is exactly what the matrix tells us it should be. Woo hoo.

So, there is one step to get the current user's identity no matter what authentication mode is being used.

1) Force authentication by denying access to anonymous users.

OK, three steps for Forms authentication:
2) Authenticate the user
3) Set the authentication cookie

So, if the user is not authenticated, the value of the HttpContext.Current.User.Identity.Name ="". For Authenticated users it just depends on the Authentication mode:
  • Windows =  domain\user (created and maintained by the app's server or its domain)
  • Forms = UserID (created and maintained by the app -- in DB, config file, etc. -- set in the Forms Authentication cookie)
  • Paspport = good question, does anybody outside of Redmond use this?
Now, another source of confusion is that each web app user has more than 1 identity, in fact, each user has 3 identities.  But even this isn't too horrid after a little investigation.

We've discussed the HttpContext Identity, which holds the user's identity as far as the application is concerned. There are also the
Windows Identity
and
Thread Identity

Odds are the Thread Identity is something that most developers will not have to concern themselves with. This is simply the identity tied to the currently executing .NET thread. The Windows Identity of the current user however, is something that every web developer has or will encounter at some point, because the Windows Identity is the actual Windows account that the current user is running under.  This is what gives the user the PERMISSIONS to access the file system and other resources on the server.  This is commonly referred to as the user's Security Context.

Let's go back to our previous examples and see what Windows accounts are being used in each situation:

HttpContext User Windows Account (XP, 2000) Windows Account (2003)
Anonymous MACHINE\ASPNET NT Authority\Network Service
Windows Authenticated MACHINE\ASPNET NT Authority\Network Service
Forms Authenticated MACHINE\ASPNET NT Authority\Network Service

Duh, that doesn't look complicated, all that matters is the OS, all the users use the same account! How can that cause confusion?

Well, like many mystery stories, we've left a character out until the end...

IMPERSONATE
 
ASP.NET applications have the ability to "impersonate" a windows account.  The above examples assumed the default of no impersonation.  Turn on impersonation, and it gets a little more complicated.

Impersonation is turned on like so...

<identity impersonate="true"><!-- default -->
OR

<identity impersonate="true" userName="SomeDomain\SomeUser" password="apassword"/><!-- specific account -->

Note that Impersonation can be set at any configuration level (machine, application, subdirectory).  If this is set in machine.config, it will affect all the web apps on a machine. Also note that putting a password and account in clear text isn't advised and there are ways to put encrypted versions in the registry. Finally note that allowing anonymous users to impersonate a real user account is a silly idea. In general, impersonation should only be used when necessary.


HttpContext User Windows Account (XP, 2000) Windows Account (2003)
Anonymous MACHINE\IUSR_MACHINE MACHINE\IUSR_MACHINE
Windows Authenticated SomeDomain\LoggedInUser SomeDomain\LoggedInUser
Forms Authenticated SomeDomain\SpecifiedUser SomeDomain\SpecifiedUser

 Let's put the info for our two identies of concern -- HttpContext and Windows -- into one table for easy reference.



Impersonation = False Impersonation = True
HttpContext User Name Win Acct (XP, 2000) Win Acct (2003) Name Win Acct
Anonymous   MACHINE\ASPNET Network Service   MACHINE\IUSR_MACHINE
Windows Authenticated Domain\LoggedInUser MACHINE\ASPNET Network Service Domain\LoggedInUser Domain\LoggedInUser
Forms Authenticated UserID
MACHINE\ASPNET Network Service UserID Domain\SpecifiedUser


As the table makes clear, the HttpContext Identity is only filled for authenticated users and the value it holds depends only on the authentication mode.  The name of the HttpContext user is controlled by Windows under Windows Authentication, the web application under Forms Authentication.  The HttpContext Identity is typically used by the application for personalization and application security.

The Windows Identity depends on the authentication mode and the impersonation settings. The Windows Identity the Windows Account the user is operating under, which  determines what files and resources the user can access on the SERVER. Without impersonation, a user will have access to the server resources via a very restricted account.  Impersonation can allow the user to have much broader access and should be used with care.

Hope this summary makes finding the current user's identities! easier. In the next post we'll look at what authentication wrinkles and new goodies there are in ASP.NET 2.0.  In the meantime, there's a new Yahoo Group to help decipher the more puzzling aspects of ASP.NET Security and Authentication.
Friday, 25 August 2006 13:42:57 (Eastern Standard Time, UTC-05:00)   #     Comments [0]  | 
Tuesday, 07 March 2006
One of those stupid things...

I opened up a 2.0 website I was working on and Visual Studio decided it needed to be converted. Not sure why, unfortunately I wasn't paying attention.  Fortunately, I did tell VS to back up the project.  Went to run the website, which was working yesterday.  I get an allowDefinition='MachineToApplication' error.  What the badword !?!

(BTW, I believe coding -- much like plumbing -- requires lots of swearing to let off steam to save passers-by and inanimate objects from more physical acts of aggression.)

My first, VERY ERRONEOUS, thought was that the stupid project was trying to run under IIS, which I didn't want it to do.  I had been running it under the very handy ASP.NET Development Web Server. But I thought, I'll play along.  I created a virtual directory under my local IIS and pointed it to my website.

Same friggin' error.  More swearing.  Some googling.  What's going on?

Finally, looking at the directory structure, I see the backup folder and decide to just open the old project.  I nuke the "converted" website and copy the old files into the directroy stucture, "wisely" leaving the backup folder intact, just in case.

Same friggin' error.  More swearing. 

Then it hits me.  The backup folder has a web.config file in it. I move the backup folder out of my website tree.  I run the website.

It works. I get the last of the swearing out of my system.

The moral of the story, if you are getting the allowDefinition='MachineToApplication' error, and you have done everything to ensure you have a virtual directory set up with an application, check the rest of the tree to make sure you don't have a friggin' backup web.config file screwing things up. 

Tuesday, 07 March 2006 15:50:17 (Eastern Standard Time, UTC-05:00)   #     Comments [7]  | 

Theme design by Dean Fiala

Pick a theme: