Securing documents with ASP.net handlers


A client had been posting training files and some order templates in an unmarked directory on their website. None of them are really all that secret, but some contact lists and other things got picked up by google.

So, they’ve got pages and pages on their intranet that has links to these docs.  Templates for new orders, insurance forms, calculators for price lists, etc…  So, I can’t just move them.  They are a mixture of doc, xls, docx, xlsx, etc… just random crap.

Initially, I figured, I’ll just put in a nice handler to catch things first (which is so easy that it took me all damn day – I’ll get into that later!)

Here was the real problem:  The intranet site it is written in classic asp, so the session variables, so my standard “if session(“loggedin”)<>true then goto login page” isn’t going to work.  Classic asp doesn’t have the ease of a handler and if there is a way to do it, well…. I really don’t want to know.  Seriously, I’m not going to learn how to write something complicated in a dead language.  I’d prefer to learn something new that will be useful in the future.

So, sticking with the .Net handler idea.  Here’s the theory I decided upon.  Yes, I know, it is horrible, but here it is:

All I’m going to do is check to see if the referring page was within the intranet site.   Think about it…  If you really really really wanted to, you could write a script that would rip out those files, but seriously….  this is about all it really needs and I’m not going to make a new login page.

Here’s how I started:


Imports Microsoft.VisualBasic
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.IO

Public Class secureDocs : Implements IHttpHandler

    Dim mimes As New contentTypes

    Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest

        Dim f, strReqPath, filePath, sql As String
        strReqPath = context.Request.Path
        Dim ref As String = context.Request.ServerVariables("HTTP_REFERER")
        Dim fileOK As Boolean = False

        Dim strliveSite As String = ConfigurationManager.AppSettings("public-domain")  'I keep these values in my web.config as appsettings
        Dim strlocalSite As String = ConfigurationManager.AppSettings("local-domain")

        Dim liveSite As Integer = InStr(ref, "http://" &amp;amp;amp; strliveSite)
        Dim localSite As Integer = InStr(ref, "http://" &amp;amp;amp; strlocalSite)
        If liveSite > 0 Or localSite > 0 Then fileOK = True

        If Not fileOK Then
' normally, I'd be checking to see if the user was logged in
' and then send them to a login page, but I just don't have
' the budget for that right now.  so....
            context.Response.Write("THIS IS A SECURE FILE")
            Exit Sub
        End If

        f = context.Request.Path.Substring(context.Request.Path.LastIndexOf("/") + 1)
        filePath = context.Request.PhysicalPath

        Dim strContentType As String
        Dim extension As String = context.Request.Path.Substring(context.Request.Path.LastIndexOf(".") + 1)
        strContentType = mimes.getContentType(extension)

        Try

            Dim file As New FileInfo(filePath)
            Dim len As Integer = file.Length

            context.Response.Clear()
            context.Response.AddHeader("Content-Disposition", "inline; filename=" &amp;amp;amp; file.Name)
            context.Response.AddHeader("Content-Length", file.Length.ToString())
            context.Response.ContentType = strContentType
            context.Response.WriteFile(file.FullName)

            Try
                context.Response.End()
            Catch ex As Exception

            End Try

        Catch ex As Exception

            context.Response.Write("FILE DOES NOT EXIST")
            Exit Sub
        End Try

    End Sub

    Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

End Class
Class contentTypes
    Dim _types As New StringDictionary
    Sub New()
        makeKeys()
    End Sub
    Public ReadOnly Property getContentType(myExtension As String) As String
        Get

            Return _types.Item(myExtension)

        End Get

    End Property
    Sub makeKeys()

        _types.Add("acx", "application/internet-property-stream")
        _types.Add("ai", "application/postscript")
        _types.Add("aif", "audio/x-aiff")

      '---- a whole bunch more mime types here -----'

        _types.Add("z", "application/x-compress")
        _types.Add("zip", "application/zip")

    End Sub

End Class

Now, that file I stick into my app_code directory and I can then reference it by putting it in my web.config file.

it needs to be in two spots:


 <system.web>
     <httpHandlers>
			<add verb="*" path="/client_files/*" type="secureDocs" />
			<add verb="*" path="/someOtherDirectory/*" type="secureDocs" />
      </httpHandlers>
  </system.web>

And you’re also going to need to tell the server that you don’t care what the file is, but you absolutely MUST kick off aspnet when you are in the
directories where these files are. Otherwise, IIS will see .doc or .xls and just give it to them without hitting your handler.


	<system.webServer>
		<handlers>
			<add name="sd" path="client_files/*" verb="*" modules="IsapiModule"
				 scriptProcessor="C:\windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified"
				 preCondition="classicMode,runtimeVersionv4.0,bitness64" />
		</handlers>
	</system.webServer>

HOWEVER…. If you run a shitty old server that still has IIS 6.0 on it, this latter module doesn’t work.

You need to open IIS on the server, hit the properties for your website, go to “home direc

Posted in: ASP.net on October 9th by admin


No comments yet

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment