Sunday, May 16, 2004

Solution to the directory version application security problem

What do you do when you need to secure the directory in IIS, but you need security at the application level as well? Since IIS doesn't give your application all of the username and password information, you can't use directory security. However you have files that need to be secured at a directory level. Of course if you use application security, your application can't communicate with IIS to give it the username and password information. So it's kind of a catch22 isn't it? So when presented with a choose, choice both. My application give access to office files if your Active Directory account is in the correct groups. So that is application level. But with application level security if anyone knows, or can guess the url of the documents, they can view them right in their web browse. Now what is the use of setting up Active Directory groups and using the System.DirectoryServices namespace, if in the end anybody can browse to it.

The solution that I can up with was to lock down the my content directories in IIS. No rights to anyone. Then I created a page to be the proxy between the web and the NTFS level. So now you can only browse to the files if you have the correct authorization from the you Active Directory group, and if you don't it takes you to a login screen. What's really interesting about this solution is it envolves a lot lets code then you may think. The article I based my code on is in the Microsoft knowledge base, Article -306654.

privatevoidPage_Load(object sender, System.EventArgs e)

{

FileInfo MyFileInfo;

long StartPos = 0, FileSize;

string filepath = "";

try

{

//Check to see if there is anything in the QueryString.

if (Request.QueryString["doc"].ToString() != "")

{

//Set filepath to Querystring.

filepath = Request.QueryString["doc"].ToString();

}

}

catch

{

//If the Querystring is empty send the user back to Default.aspx.

Response.Redirect("Default.aspx");

}

try

{

//Set the appropriate document type

Response.ContentType = "Application/pdf";

//Response.ContentType = "Application/pdf";

//get the physical file path

stringstrFilePath = utilities.getPath(filepath);

//instantiate the FileInfo class

MyFileInfo = newFileInfo(strFilePath);

//Get the size of the file.

FileSize = MyFileInfo.Length;

//Write the file directly to http content output stream

Response.WriteFile(strFilePath, StartPos, FileSize);

Response.End();

}

catch(Exception err)

{

//Write to custom error logging function

logging.writeError(err.Message.ToString());

}

}

So Pretty straight forward. You locate the document give the Response.WriteFile function the files path and a few properties and your aspx page opens up as if it were that document. Of course the one thing you have to be mindful of is the Content Type. That  must be set to the correct MIME type for the type of file you are opening. Wher are some of the types.

  • "text/HTML"
  • "image/GIF"
  • "image/JPEG"
  • "text/plain"
  • "Application/msword" (for Microsoft Word files)
  • "Application/x-msexcel" (for Microsoft Excel files)
  • I have ran in to a couple of issues worth mentioning. First in some cases with users that are running Internet Explorer, the page will appear to be completely blank. The issue seems to be that IE is second guessing the content type and doesn't display anything. The fix is simple, but on the client side, in their pdf reader they need to disable view pdf in browser. Here is an article that covers all of the variations of Acrobat and Internet Explorer settings to do that

    Second, is speed, with files that are larger than 2MB it takes a little longer. Not a lot, but it is noticable. This is because our ASPX page is reading the file in binary and rewriting it in binary to the browser. I haven't figured out how to speed that up just yet. If I do I will post it. However a document being 10 times more secure, is worth it being a little slower.

    No comments: