Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Non ASPX Extensions and Authentication in the IIS 7 Integrated Pipeline


:P
On this page:

Last week, I ran into an interesting problem that I didn't immediately find a solution for. I was cleaning up one of my DevConnections samples, which is a photo album application. The idea is that any folder that contains images, multi-media content or anything else can be turned into a dynamic photoalbum by copying a couple of files into this folder that can then be accessed from the Web. What I needed to do is have a dynamic URL that when executed in this directory automatically creates the photo album related files in the current folder.

These administrative functions are blocked off via Authentication - either Forms or Windows Authentication - so that the admin features are not accessible to all.  So I require that the user is logged in - otherwise a 401 status is thrown which SHOULD in theory redirect the user to the login page in the case of Forms Auth or the Windows Logon with Windows Auth.

So for example, if I have the application set up in localhost/photoalbum and I add a new folder underneath it called Maui2007 I should be able to create a new album like this:

http://localhost/photoalbum/maui2007/CreateAlbum.axd

This is what would amount to a virtual URL - a URL that has no backing file on disk. ASP.NET makes it pretty easy to create virtual URLs. In IIS 7's integrated pipeline  ALL files are routed through the managed pipeline and an HttpModule (or Application_ handler) can look at all files with any extension, from ASPX pages to images and html files inside of a module. In IIS 5/6 you can only look at URLs that are routed to the ASP.NET ISAPI extension which requires some configuration.

I chose AXD as the extension because AXD is a built in and already a virtual extension. By that I mean it's is mapped in IIS as not requiring a mapping file. AXD is used extensively by ASP.NET internally - WebResource.axd, Trace.axd, ScriptResource.axd are a few of the more common ones you might have seen before.

So I created a Module that looks something like this:

public class PhotoAlbumModule : IHttpModule
 { 
    public void Dispose()
    {
    }
 
    public void Init(HttpApplication context)
    {
        context.PostAuthorizeRequest += new EventHandler(context_PostAuthorizeRequest);
    }
 
    void context_PostAuthorizeRequest(object sender, EventArgs e)
    {
        HttpContext Context = HttpContext.Current;
        HttpRequest Request = Context.Request;
 
        string script = Path.GetFileName(Request.FilePath).ToLower();                            
        if (script != "createalbum.axd" && script != "updatetemplate.axd")
            return;
 
        // *** Must be authenticated
        if (Context.User == null || !Context.User.IsAuthenticated)
        {
            Context.Response.StatusCode = 401;
            Context.Response.End();
            return;
        }
 
        if (script == "createalbum.axd")
        {
            this.CreateAlbum();
        }
        else if (script == "updatetemplate.axd")
        {
            this.UpdateTemplate();
        }
    }
 
    void CreateAlbum()
    {
        HttpContext Context = HttpContext.Current;
        HttpRequest Request = Context.Request;
 
        string physicalPath = Path.GetDirectoryName(Request.PhysicalPath);            
 
        File.Copy(Context.Server.MapPath("~/Templates/showphotos.aspx"),
                   Path.Combine(physicalPath,"default.aspx"),true );
 
        string newUrl = Path.Combine(Path.GetDirectoryName(Request.FilePath), "default.aspx");
 
        Context.Response.Redirect(newUrl);
        Context.Response.End();
    }
 
    void UpdateTemplate()
    {
        this.CreateAlbum();
    }
 }

Where's my Authentication on .AXD files?

But I quickly ran into a problem with this code when running under IIS 7.  When running in IIS 7 and Integrated security I found that although the user was authenticated and other URLs show an Context.User, .AXD files do not actually have the authentication applied to them.

This code:

        // *** Must be authenticated
        if (Context.User == null || string.IsNullOrEmpty(Context.User.Identity.Name))

always fails with Context.User == null on an .AXD file but works fine on ASPX or ASHX files when running with Forms Auth.

Even more interesting when the code would fire the Status = 401 which normally fires off to the Login page, IIS 7 would simply throw up a 401 error page. In other words it looks just like IIS is completely bypassing Forms Authentication on the .AXD extension URL. So hitting the CreatePhotoAlbum.axd results in an IIS 7 error page rather than a login prompt either for Windows Auth or for Forms Auth.

Running the same code under IIS 6 (or rather on IIS 7 in ASP.NET 2.0 compatibility mode) the code works fine with all files that are mapped through ASP.NET (via ScriptMapping extension into the ASP.NET ISAPI extension), including the .AXD files being properly authenticated when using Forms Authentication. Upon closer examination it appears that only certain extensions actually get authenticated in IIS 7. ASPX and ASHX work, but  image files, css, js files etc. also don't show up with authentication in PostAuthorizeRequest even though they do fire through my module. 

For kicks I experimented around with a different pipeline events like PreRequestHandlerExecute() which is even later in the ASP.NET pipeline to see if that would change anything, but no luck. I'm not sure what rules are applied what goes through forms auth and what doesn't but apparently not everything and not even all ASP.NET mapped extensions (ie. .AXD).

Another Solution

In the end the solution for my particular problem was to use .ASPX as the extension (ie. CreateAlbum.aspx), which seems to work and get me the authenticated user both on IIS 5/6 and 7.  Originally I didn't want to use ASPX as the extension because I thought (incorrectly) that the default mappings in IIS require a mapping file to be present, but a quick check confirms that this is not the case - the check for 'Check if File/Directory exists' is not checked in IIS 7 or IIS 5/6 so using ASPX for 'virtual urls'  works fine even if it's slightly unconventional (people will go looking for a file that ain't there <g>).

It took the above failures (and a couple of pokes from Twitter) to realize that ASPX would work fine, but that's how it goes sometimes...

But this is a somewhat important issue to keep in mind. Authentication of certain files works differently in IIS 7 than it does in previous versions of IIS. I tried looking around in IIS 7's applicationhost.config file to see if there's any mapping of extensions to authentication flags, but couldn't find anything specifically. <shrug> Maybe somebody from the IIS team has a little more information

But this is something to keep in mind if you want to apply system level security to none Page resources in your application. Apparently authentication is not applied against them and Context.User is either null (Forms Auth) or the User details are blank (!Context.User.Identity.IsAuthenticated)  (Windows Auth).

Posted in ASP.NET  IIS  Security  

The Voices of Reason


 

Bilal Haidar
May 23, 2008

# re: Non ASPX Extensions and Authentication in the IIS 7 Integrated Pipeline

Hello Rick,
Did you change the modules inside your web.config as follows:

<remove name="WindowsAuthentication" />
<add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" preCondition="" />
<remove name="FormsAuthentication" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" preCondition="" />
<remove name="UrlAuthorization" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" preCondition="" />


This makes sure all the .css, .gif, and other static files to be processed by the ASP.NET engine.

Thanks

dominick
May 23, 2008

# re: Non ASPX Extensions and Authentication in the IIS 7 Integrated Pipeline

in addition to the previous comment - you also need to enable the DefaultAuthenticationModule for non-managed handlers.

dominick
May 23, 2008

# re: Non ASPX Extensions and Authentication in the IIS 7 Integrated Pipeline

...or simply set the runallManagedModuleForAllRequests (or similar ;) attribute on the module section.

Rick Strahl
May 23, 2008

# re: Non ASPX Extensions and Authentication in the IIS 7 Integrated Pipeline

@Bilal - Thanks! Yup that's it. Removing and readding the modules apparently starts authenticating all files properly.

Hmm... so it looks like the culprit is the preCondition managedHandler:

<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" preCondition="managedHandler" />


Preconditions are described here:
http://msdn.microsoft.com/en-us/library/ms690693(VS.85).aspx

managedHandler only fires on select ASP.NET resources. The default (that's what blank stands for) is integratedMode which fires on every resource including static files.

Definitely good to know!

I suspect this behavior is a performance tweak. Generally you wouldn't want to have ASP.NET/IIS authenticating everything, so turning of managedHandler should probably only be done in certain situations where you need full control.

Moral of the story: working with ASPX/ASHX extensions for virtual Urls is probably a good idea anyway you look at it.

Bilal Haidar
May 24, 2008

# re: Non ASPX Extensions and Authentication in the IIS 7 Integrated Pipeline

On of the goals behind IIS 7.0 Integration mode is to make use of the already existing features of ASP.NET. Hence, the idea as you know is that, when an application is running in the integration mode, the request can be easily accessed by the ASP.NET engine, let the request be a .NET resource or any other resource (PHP, ASP, CSS, etc ...).

That is why I believe the "preCondition" attribute exists. The ASP.NET features already exist for ASP.NET resources and hence to enable them to other resources they added the trick of "preCondition". This way, you can enable/disable a feature from resources other than ASP.NET.

Still have one comment though, is the .axd now being authenticated correctly? If yes, this is weird because the preCondition affects only non-ASP.NET resources and the .axd is a .NET one. Anything to say in here?

Thanks.

Rick Strahl
May 24, 2008

# re: Non ASPX Extensions and Authentication in the IIS 7 Integrated Pipeline

@Bilal - yes using integratedMode does make the .axd url authenticate. I suspect that there's a special exception for .AXD handlers because they are used extensively with WebResource.axd and these essentially static like resources. It certainly makes sense from a performance perspective.

Maybe the .axd handler is running native code by default to optimize certain performance characteristics like pulling resources? I don't but in any case using integratedMode makes it work because EVERYTHING authenticates then.

Bilal Haidar
May 24, 2008

# re: Non ASPX Extensions and Authentication in the IIS 7 Integrated Pipeline

Good news then!

Good luck and thanks!
Bilal

Tatham Oddie
June 04, 2008

# re: Non ASPX Extensions and Authentication in the IIS 7 Integrated Pipeline

Hi Rick,

The .axd extension is really meant for internal handlers like WebResource.axd and this is probably why it bypasses the authentication. (It's not meant to be used for things that need to be secured.)

For your own handlers, you really should be using the .ashx extension. This is another extension which has been reserved, but it is meant for use by custom handlers as opposed to framework handlers.


HTH,

Tatham

W
January 04, 2009

# re: Non ASPX Extensions and Authentication in the IIS 7 Integrated Pipeline

I also found this problem too with other IHttpHandlers with no static content (http://blog.wdoenterprises.com/2008/12/aspnet-iis7-url-routing-ihttphandler.html). Any idea why the "managedHandler" precondition doesn't work for a managed code module? They choose a pretty bad name if it controls more then "managed code".

Mike
January 22, 2010

# weird authentication

This article helped somewhat, but I'm having a weird issue with the authentication. PDF files and the aspx pages are requiring authentication properly, but the images are not being displayed. The images are in the same directory as the PDF files, but I can not access them, even directly by URL. The site does redirect me to the login page, but it seems as though the user is not being authorized.

I only added a login page and forms authentication to mimic the IIS 6 way to block access to a site, since this particular web host does not offer direct or indirect access to the IIS 7 console.

I have done the option to remove and re-add the authentication and authorization modules with an empty precondition, which protected PDFs, and there is only one user whose name and password are hard-coded into web.config. Any ideas on how to get the image to be shown?

Sebastian
March 05, 2010

# re: Non ASPX Extensions and Authentication in the IIS 7 Integrated Pipeline

THANKS Bilal, finally found the solution I was looking for! :-)

Oleh Deyneka
September 21, 2010

# re: Non ASPX Extensions and Authentication in the IIS 7 Integrated Pipeline

You need to configure IIS properly. Just try to add "runAllManagedModulesForAllRequests" attributes.

<system.webServer>
<modules runAllManagedModulesForAllRequests="true">

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024