A recent question was asked on the ASP.NET forums:
“i’m building a page to let people upload images to my site. they are supposed to see the images after uploading them.
i wondered if somebody can take ane “exe” file, change the extension to “jpg”, upload it, and then run it by pressing the “view picture” button. will my server run this exe file? if yes, how can i check if it image (“jpg” and “gif”) or something else?
great thanks…”
The question actually encompasses a very frequent request that users have. The fact that someone knew to ask about this before just implementing the upload feature proves that the mentality toward securing applications has been changed slightly from “Lets implement as many features as we can” to “How will implementing this feature affect the security of my application”. This mentality, I feel, is spurred by several factors such as Microsoft’s increased Trustworthy Compupting initiative.
Since I feel its good to reward people for thinking about security first, I will do my best to answer this question for the user here on my blog. In the next few days, I will be launching a series of posts relating to uploading files in a web environment, the security issues, and better ways to implement the feature. I’ll start by defining the functionality that is ultimately wanted, provide some insight into the various ways to implement this functionality and the security issues surrounding each approach, and then move on to designing a solution and perhaps even provide some sample code.
For now, let’s focus on the functionality and delve a bit into some various ways to implement this feature.
In general, the developer wants users to be able to upload image files to a website, and let someone or several people view the images after they are uploaded. This feature is implemented in the very software that is used to run this blog — Community Server. Over the years, this feature has been implemented in more ways than I care to count. Some folks store the uploaded file as is to the file system. Others have stored the binary data as a BLOB in a database. Others have used hybrids of this mechanism. For this portion of the article, we will focus on a file-based approach and follow up with a database approach.
Storing in the File System
The most logical way to store files, it would seem, is in a file system. From a public internet standpoint, however, one has to consider the trade offs of this approach. The first problem is that your web user account (most likely the ASPNET account) has to have permission to write to the file system in order to pull this off. The second issue is that if you write to a file within the scope of your web’s root folder, those files can most likely be accessed through IIS as well (and since this particular user desires for the images to be available to the public, they almost have to be available). The security concerns are massive. A user could upload a web-executable script to the server that they could then use as a trojan to gain access to the rest of the server. More advanced hacks could be used to compromise memory buffers and cause an executable to run in the server process (one of the main resons behind the new aspnet_wp running under the context of a low-privileged user). There are some ways to decrease the risk involved in this method, however. Some of them involve security through obscurity (not sufficient by itself). Some of them involve coupling the best of OS security, web security and application security.
Directory Structure
For starters, consider the fact that you do not want any files that are uploaded to be scriptable on the server side. So lets set up a directory structure and security for our saved files. Start by creating your main upload directory outside of the web root. For example, if your web root is found at C:inetpubwwwroot , create your image directory at C:inetpubuploadroot. So why did we do this? This prevents anyone from uploading malicious scriptable code to your server that can be executed on the server. Once you create your directory, make sure you grant read and write permission to ASPNET user (if you are not using impersonation) or to the Groups and Users which you will allow to upload files. In my case, I granted the following permissions:
Administrators Full Control
ASPNET List Folder Contents, Read, Write
Network Service List Folder Contents, Read, Write
System Full Control
Uploading your File
Once you have set up your directory structure, you’ll need to create your upload page. You only need an HTML file control and a submit button. Once you add your HTML controls, make sure you run them at the server side.
Once you’ve set up the page, add the following code to save the file:
private void btnSubmit_Click(object sender, System.EventArgs e) {
HttpPostedFile file = fleUpload.PostedFile;
if( file != null ) {
string serverFilePath = String.Format(
@"C:domainstitus.touploadroot{0}",
file.FileName.Substring(
file.FileName.LastIndexOf(@"") ) );
// TODO: Write file validation routine.
if( ValidateIsJpeg( file ) ) {
file.SaveAs( serverFilePath );
}
}
}
Validating The File
This get’s the job done for uploading, but it still isn’t secure and we haven’t given the user any way to retreive the file. Now, I’m not an expert in image file types, but I know that each has a format. I would HIGHLY recommend that you learn the proper methods to validate each file type you intend to accept and write a routine to test the uploaded file against the expected format. For JPEG files, you can read an online FAQ at http://www.faqs.org/faqs/jpeg-faq/ that will explain the format and give you clues on how to validate this file. Remember that ALL INPUT IS EVIL and with file uploading, this is particularly important. However, I’ll leave this implementation up to you since this can get rather complex and will change based on each file type you want to accept.
In Part #2, we’ll discuss how to retreive images that are stored on the file server using an HttpHandler.