14

I want to secure specific folders and resources in my application that are outside of the routes for my mvc application. I want these resources to only be available to authenticated users (which role is not of concequence as long as they are authenticated).

Initially it seemed that the UrlAuthorizationModule would be the answer. I followed this article, Understanding IIS 7.0 URL Authorization, and I can get the module to work in the sense that it responds to the configuration elements in the web.config.

My current problem is that I think it is enacting the rules based on the anonymous user in IIS and not the authenticated user in asp.net identity.

Test Environment

I use a standard html file for testing instead of trying to load a script as this would also be loaded outside of the MVC pipeline.

  • In Visual Studio 2015.
    • New default .net 4.6.2 web project
    • MVC template
    • Authentication = Individual User Accounts
  • IIS 8 (for testing outside Visual Studio)
    • Authentication -> Anonymous Authentication (enabled)

Add to web.config

<configuration>
...
<location path="Data">
  <system.webServer>
    <security>
      <authorization>
        <clear/>
        <add accessType="Deny" users="*"/>
        <add accessType="Allow" users="?"/>
      </authorization>
    </security>
  </system.webServer>
</location>
...
</configuration>

Add to folder structure

/Data/Protected.html // this file just has some basic Hello World content to display so you can see if it is loaded or not.

Observed Results

  • With this configuration everything in the Data path is always denied, it does not matter if the user is authenticated or not.
  • The same is true if I switch the 2 lines for Deny and Allow in the web.config.
  • If I completely remove the line with Deny then access is always allowed even when the user is not authenticated.
  • If I add a role and use roles with the role name instead of users attribute the role is also completely ignored.

Now What?

What am I missing? How can I get the Url Authorization module to work with MVC/WebAPI and ASP.NET Identity Individual user accounts or is this simply not doable?

I am open to alternative ideas as well, maybe the answer is to write a custom HttpModule or HttpHandler?


Side notes

Why & Specifics

These resources are javascript files, in short only a portion of the scripts should be available to unauthenticated users. There are 2 directories in the root, one for the authenticated part of the app and one for the non-authenticated part of the app. The reason for this has nothing to do with user authorization or security in the application, it is to limit the exposed surface area of the application to non-authenticated requests.

Legends
  • 21,202
  • 16
  • 97
  • 123
Igor
  • 60,821
  • 10
  • 100
  • 175
  • I have no experience with url authorization module, but if you would use an HttpModule you could check in the `AuthorizeRequest` event stage, if the user is authenticated and which path he wants to access... – Legends Mar 01 '17 at 20:16
  • @Legends - I had initially tried an `HttpModule` but had trouble trying to integrate with `Asp.net Identity`. Also I was hoping I would not have to re-invent the wheel. – Igor Mar 01 '17 at 20:26
  • Did you try: ` ` ? If this doesn't work, then switch the lines, don't know exactly what the right order is. – Legends Mar 01 '17 at 20:48
  • @Legends - I did, that setting restricts access to the request both when authenticated and not authenticated. Switching the order does not matter. – Igor Mar 01 '17 at 21:58
  • `?` is for anonymous users and `*` for authenticated, the order of the add statements is also important, just to be clear ;-) 250 bounty, I am sure someone will answer it.... – Legends Mar 01 '17 at 22:08
  • @Legends - Yes, I took all that into consideration. I also added the steps to reproduce the problem above which is possible with any version of VS2015 (takes maybe 5 minutes). – Igor Mar 01 '17 at 22:11
  • can you tell me why I added the `HtmlScriptHandler` handler? :-) Because the URL authorization works without it. I can remember, that I had an issue without it, but now I cannot reproduce any issue.... So, commenting this handler out in the complete web.config example, handles the url authorization correctly in my case. – Legends Mar 02 '17 at 23:13
  • @Legends - I can't get it to work with out it. Either the root or one of the sub-dir `web.config` files has to have it if you want authorization on a file type. I am guessing otherwise it by-passes the asp.net pipeline and IIS handles the request so authorization check never executes. – Igor Mar 03 '17 at 10:37
  • This answer could be helpful: https://stackoverflow.com/a/37615256/4711756 – i.furs Jun 01 '20 at 16:33

1 Answers1

13

[TL;DR;]
Go to "Complete root web.config" section to see the needed web.config setup.

Test this in incognito-mode to prevent browser caching issues! And use Ctrl+F5 because scripts and html files get cached.

First deny access to all anonymous users in the root web.config.

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

The web.config here allows one folder to be publicly accessible. This folder, in my example here, is called css and sits in the root of the MVC application. For the css folder I add the following authorization to the root web.config:

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

You can add more of these location paths if you want more public folders.

While all other files will not be accessible until the user logs in, the css folder and its contents will always be accessible.

I have also added a static file handler to the root web.config, This is critical as you want the request to be managed by the asp.net pipeline for the specific file type(s):

<handlers>
    <add name="HtmlScriptHandler" path="*.html" verb="*" preCondition="integratedMode" type="System.Web.StaticFileHandler" />
</handlers> 

Complete root web.config

<system.web>
    <authentication mode="None" />
    <authorization>
        <deny users="?"/>        
    </authorization>
    <compilation debug="true" targetFramework="4.6.2" />
    <httpRuntime targetFramework="4.6.2" />
</system.web>
<location path="css">
    <system.web>
        <authorization>          
            <allow users="*"/>
        </authorization>
    </system.web>
</location>
<system.webServer>
    <modules>
        <remove name="FormsAuthentication" />           
        <remove  name="UrlAuthorization" />
        <add  name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule"  />     
    </modules>
    <handlers>
        <add name="HtmlScriptHandler" path="*.html" verb="*" preCondition="integratedMode" type="System.Web.StaticFileHandler" />
    </handlers>      
</system.webServer>

ASP.NET by default will only apply the allow and deny rules to files handled by the managed handler. Static files are not managed by the managed handler.

You could also set: (Don't do this, if not really needed!)

 <modules runAllManagedModulesForAllRequests="true">

With runAllManagedModulesForAllRequests="true" all the HTTP modules will run on every request, not just managed requests (e.g. .aspx, ashx). This means modules will run on every .jpg ,.gif ,.css ,.html, .pdf, ... request.


One important thing
You don't have to add the UrlAuthorizationModule to the modules section as it is already part of the ASP.NET pipeline. This means, it will run only for managed files, not static!

If you now remove and then re-add the UrlAuthorizationModule to the modules section, it will run under precondition "integratedMode" and not under "managedHandler" anymore! And will therefore have access to static files.

<remove  name="UrlAuthorization" />
<add  name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />


If you set the precondition to managed: <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" preCondition="managedHandler" />, then the UrlAuthorizationModule will not restrict access to static files anymore.

You can test this by accessing a script file in the scripts folder successfully while being logged out. Hit Ctrl+F5 to make sure you get a fresh copy of the script file.


Difference between ASP.NET UrlAuthorization <--> IIS URL Authorization

It is important to keep in mind that the managedHandler precondition is on the ASP.NET UrlAuthorization module. The precondition tells you that the URL authorization module is invoked only when the code that handles the request is mapped to managed code, typically an .aspx or .asmx page. IIS URL Authorization, on the other hand, applies to all content. You can remove the managedHandler precondition from the ASP.NET Url Authorization module. It is there to prevent a performance penality you have to pay when every request (such as a request to .html or .jpg pages) would have to go through managed code.

P.S.: Some web.config attributes are case sensitive!

Legends
  • 21,202
  • 16
  • 97
  • 123
  • 1
    Thanks, using your solution I worked backwards until I got just the changes that I needed to get this to work. I am going to edit your post, please edit it further as you see fit once I am done. – Igor Mar 02 '17 at 12:12
  • 1
    So the part I was missing and why the `authorization` element never had any effect was because I did not have a registered `HttpHandler` for my restricted file type. Once I added that AND used the `authorization` element inside of `system.web` element everything worked as expected. Thanks again. – Igor Mar 02 '17 at 12:18
  • I'm glad it helped ;-) – Legends Mar 02 '17 at 13:37
  • 1
    Nice update (*250 rep. very well deserved!*) - One note: `FormsAuthentication` module is not enabled in this case because aps.net identity framework is being used for authentication/authorization. I have it running on my web site and the only reference to `FormsAuthentication` is a line item to remove it. – Igor Mar 03 '17 at 15:00
  • I've removed the add and kept the remove :D – Legends Mar 03 '17 at 15:17