Secure PDFs by Roles and Log File Downloads With a Custom IHttpHandler

If you are using FormsAuthentication and a RoleProvider in ASP.NET, chances are at some point you will want to secure access to static files like PDFs (or any other file type, for that matter) just like you other secure areas on your site.

This post covers how to lock-down PDFs to certain users/roles and also how to log whenever a user accesses the files.

1. Secure the folder

This part you likely already know how to do. To secure the folder, drop a web.config in it:

<?xml version="1.0"?>
<configuration>
    <system.web>
      <authorization>
        <allow roles="Admin" />
        <deny users="*" />
      </authorization>
    </system.web>
</configuration>

You'll note that while this secured the web pages, someone can still navigate directly to a PDF. That is because the PDF, like other static files, are being served up directly by IIS. You can force PDFs to go through the ASP.NET pipleline by adding one line to your root web.config in the handlers section.

2. Add a handler for PDFs

<system.webServer>
  <handlers>
    <add name="PDFHandler" type="System.Web.StaticFileHandler" path="*.pdf" verb="GET"  />
  </handlers>
</system.webServer>

Now the PDFs are secured by roles just like the web pages in the folder where you put that web.config file. If all you wanted to do is secure PDFs, you can stop now.

If you want to take an action every time a PDF is accessed, here is how:

3. Create a custom handler (ASHX) for your PDFs

using System.IO;
using System.Web;

namespace WebApplication1
{
    /// 
    /// Serve up PDFs and log access by Authenticated Users
    /// 
    public class PDFService : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            string fileToServe = context.Request.Path;
            if (HttpContext.Current.User.Identity.IsAuthenticated)
            {
                //Log the user and the file served to the DB. This next line is for example only.
                MyLogger.LogFileAccess(HttpContext.Current.User.Identity.Name, fileToServe);
            }
            FileInfo pdf = new FileInfo(context.Server.MapPath(fileToServe));
            context.Response.ClearContent();
            context.Response.ContentType = "application/pdf";
            context.Response.AddHeader("Content-Disposition", "attachment; filename=" + pdf.Name);
            //change "attachment" to "inline" if you'd rather the browser just load the file instead of prompting download
            context.Response.AddHeader("Content-Length", pdf.Length.ToString());
            context.Response.TransmitFile(pdf.FullName);
            context.Response.Flush();
            context.Response.End();
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

The last step is to change the root web.config we edited in Step 2 to use your new IHttpHandler instead of that StaticFileHandler.

4. Update your web.config

<add name="PDFHandler" type="WebApplication1.PDFService" path="*.pdf" verb="GET"  />

And that is it! Now regular anchor tag links to your PDFs will be secured and logging all access.

<a href="/files/ExecSum.pdf">Download our Executive Summary</a>