37

I have a website running on a IIS 7.5 server with ASP.NET 4.0 on a shared host, but in full trust.

The site is a basic "file browser" that allows the visitors to login and have a list of files available to them displayed, and, obviously, download the files. The static files (mostly pdf files) are located in a sub folder on the site called data, e.g. http://example.com/data/...

The site uses ASP.NET form authentication.

My question is: How do I get the ASP.NET engine to handle the requests for the static files in the data folder, so that request for files are authenticated by ASP.NET, and users are not able to deep link to a file and grab files they are not allowed to have?

Dharman
  • 30,962
  • 25
  • 85
  • 135
Egil Hansen
  • 15,028
  • 8
  • 37
  • 54

6 Answers6

51

If you application pool is running in Integrated mode then you can do the following.

Add the following to your top level web.config.

  <system.webServer>
    <modules>
      <add  name="FormsAuthenticationModule"  type="System.Web.Security.FormsAuthenticationModule" />
      <remove  name="UrlAuthorization" />
      <add  name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule"  />
      <remove  name="DefaultAuthentication" />
      <add  name="DefaultAuthentication"  type="System.Web.Security.DefaultAuthenticationModule" />
    </modules>
  </system.webServer>

Now you can use the standard ASP.NET permissions in your web.config to force forms authentication for all files in the directory.

<system.web>
    <authorization>
        <deny users="?" />
    </authorization>
    <authentication mode="Forms" />
</system.web>
Joel Cunningham
  • 13,620
  • 8
  • 43
  • 49
  • Thanks Joel. A comment though. I was not allowed to use the "authentication" section in web.configs that are not at the root of the site, anyway it seems to work with users. However, I could not get it to work when I had , they would not authenticate, but if i added a users explicitly to the allow list and denied all other authenticated users, it works. Am I missing something in the webServer modules section to make this work with roles as well as users? – Egil Hansen May 27 '10 at 15:09
  • Ugh, thank you! This shouldn't have been so difficult to figure out. Working for me now. – Ed Charbeneau May 15 '13 at 19:02
  • 1
    This works, but also blocks any css/gif files used on the login page. Any way to get those to show? – eych Dec 14 '13 at 14:52
  • 1
    Be VERY careful with this solution! This will mean all your static resources will go through the FormsAuthenticationModule. If you're using this in combination with a sliding expiration on your cookies, and some downstream caching of static resources, then you may actually wind up caching auth cookies and serving them up to other users! https://medium.com/@flopasquier/the-dramatic-impersonation-a-developer-horror-tale-c794b1ab5c20 – extremeandy Feb 25 '18 at 23:02
14

I had the same problem with getting roles to authenticate. Through trial and error I finally got it to work with a small edit to @Joel Cunningham's code:

<modules runAllManagedModulesForAllRequests="true" >

I used these two sites as references: http://forums.iis.net/t/1177964.aspx and http://learn.iis.net/page.aspx/244/how-to-take-advantage-of-the-iis-integrated-pipeline/

Brian Webster
  • 30,033
  • 48
  • 152
  • 225
Danielle
  • 157
  • 1
  • 2
  • 5
    This is just crap, by making it working and you don't know why. This causes big PERFORMANCE problems as all the managed modules catch all the requests. @John's solutions is what you need. – DATEx2 Sep 04 '14 at 08:36
  • 2
    Hey, I agree with @DotNetWise. If you have to, then make sure you cherry pick the modules that will be executed for all requests instead of running all managed modules for all requests. e.g.: - notice that the precondition is empty. – Anton Jan 19 '16 at 13:25
10

This is an old thread, but I happened on it and ran into the same problem as Egil. Here is the version of Joel's fix that includes roles:

<modules runAllManagedModulesForAllRequests="false">
      <remove name="FormsAuthenticationModule" />
      <add name="FormsAuthenticationModule" type="System.Web.Security.FormsAuthenticationModule" />
      <remove name="UrlAuthorization" />
      <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule"  />
      <remove name="RoleManager" />
      <add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
      <remove name="DefaultAuthentication" />
      <add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" />
</modules>
DATEx2
  • 3,585
  • 1
  • 24
  • 26
John
  • 772
  • 7
  • 17
9

Addendum:

As @eych noted the accepted answer also blocks access to the ~/Content folder (or wherever you have your CSS), and ~/Scripts, and so on.

If you want to allow exceptions -- i.e. allow certain files/folders to be accessible by unauthenticated users -- you can do that by means of the location element. Add the following to web.config:

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

Update: An alternative solution is to is to leave access on by default -- which will allow access to your CSS / JavaScript / etc. -- and apply the "lock" (only) to the folder where the static content is stored:

<location path="data">
  <system.web>
    <authorization>
      <deny users="?"/>
    </authorization>
  </system.web>
</location>

Caveat: in our case (an MVC site) we needed to decorate all our controller actions (except login) with [AuthorizeAttribute]. Which is a good idea anyway, but had previously not been necessary (because previously any unauthorized request was redirected to the login page).

David
  • 2,226
  • 32
  • 39
8

I wanted to know why it would be required to re-add modules (with default options) that are added by default for the Integrated Pipeline, so I dug a little deeper.

You need to remove and re-add the modules because, by default, the modules aren't added with the default options. They have a precondition added for backwards compatibility to run only for content handled by a registered ASP.NET handler (e.g., .aspx pages).

The default looks like this:

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

By removing the modules and re-adding them without a precondition, those individual modules run for every request (including your static content). It is more granular than enabling runAllManagedModulesForAllRequests.

You can read about it in a couple articles from when the Integrated Pipeline was introduced with IIS 7:

Note that there is a typo or the module name in the second article (and @John's answer) was changed from FormsAuthenticationModule to FormsAuthentication at some point.

The set of working modules in IIS 7.5 thru 8.5 looks like this for me:

<system.webServer>
  <modules>
    <!-- Re-add auth modules (in their original order) to run for all static and dynamic requests -->
    <remove name="FormsAuthentication" />
    <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
    <remove name="DefaultAuthentication" />
    <add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" />
    <remove name="RoleManager" />
    <add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
    <remove name="UrlAuthorization" />
    <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
  </modules>
</system.webServer>
nekno
  • 19,177
  • 5
  • 42
  • 47
  • Might be obvious, but I would point out that depending on your particular situation you might not need all of these modules (I did not need RoleManager for example) and you may need others which have similar restrictions such as System.Web.SessionState.SessionStateModule – Shaun Dec 12 '16 at 16:13
2

If you application pool is running in Classic mode, you can do the following. You will have to repeat these steps for each file extension you'd like to handle, but I'm using .html here.

First, add a page build provider to the Web.config:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.web> 
    <compilation>
      <buildProviders>
        <add type="System.Web.Compilation.PageBuildProvider" extension=".html"/>
      </buildProviders>
    </compilation>
  </system.web> 
</configuration>

Then add a page handler factory:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.web> 
    <httpHandlers>
      <add type="System.Web.UI.PageHandlerFactory" path="*.html" verb="*"/>
    </httpHandlers>
  </system.web> 
</configuration>

Then add a page handler:

<?xml version="1.0" encoding="UTF-8"?>
<configuration> 
  <system.webServer>
    <handlers>
      <add scriptProcessor="C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness32" path="*.html" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" name="HtmlHandler-Classic-32" />
      <add scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness64" path="*.html" verb="GET,HEAD,POST,DEBUG" name="HtmlHandler-Classic-64"/>
    </handlers>
  </system.webServer>
</configuration>

This worked for me. (Credit: http://www.ifinity.com.au/Blog/EntryId/66/How-To-301-Redirect-htm-or-html-pages-to-DotNetNuke-aspx-pages.)

Jay Sullivan
  • 17,332
  • 11
  • 62
  • 86
  • You saved the day! There is one thing that I would fix here: the 64-bit handler should have modules="IsapiModule" too. – ewolfman Dec 02 '19 at 15:37