Creating a file upload sandbox for security using ColdFusion
I've noticed a few people searching for information on file upload sandboxes pitching up at my site at the general CF security advice article. I built one for a client last year, so here's why you want one and how to do it. This code has been tested in ColdFusion v5 and MX.
Why do I need a file upload sandbox?
If you are allowing visitors or clients to upload files through their website on to your server, you have the potential that if they know where the file is stored (e.g. http://www.yourwebsite.com/files/) that they can run it themselves, or leave something malicious for someone else to download. For instance, http://www.yourwebsite.com/files/evil.exe or http://www.yourwebsite.com/files/nasty.doc. Either of which could contain a virus or other malicious code. You don't want the chance of visitors downloading an infected file to their computer from a website you are responsible for, nor do you want someone to execute malicious code on your server.
To prevent this you can create what's called a 'sandbox' where the files can be put, but not run via the website. Once on the server, they can be virus checked and cleaned or removed (depending on your virus checker settings.) When a visitor requests to view the file, we can use CFCONTENT to read the file from the server and send it to the browser for downloading, where people can check it themselves if they wish to. At no time will the file be moved in to an executable area.
You could easily take this code and adapt it to give you a secure area to store files that only particular visitors could access, by having the final script using CFCONTENT protected in a login area. Using filtering you could also limit what visitors can see to their particular permissions level.
How to do it
First we'll create a simple upload form, and set the place to save the file out of the website hierarchy.
Then we'll look out how to use CFCONTENT to pull those files out of the sandbox and show them to the visitor.
Here's the code to create a form with the option to browse for a file:
<form name="form1" method="post" action="upload_file.cfm" ENCTYPE="multipart/form-data">
<p><input type="file" name="uploadfile" /></p>
<p><input type="Submit" /></p>
Note: it's the 'ENCTYPE="multipart/form-data"' that lets the form send the file using the 'input type="file"' input box. You need both to make uploading files work.
Form Recieving Page
Here's the code that will receive the file and store it in a directory. In this case the directory is 'c:\documentstore' which is not available through the website, so is safe from people accessing the files directly.
This code also contains the extra processing required for letting ColdFusion understand if it is receiving an empty file from a Mac using Internet Explorer. Unfortunately the Mac version of IE automatically sends a space, ' ', through if you don't put anything in it's input boxes. Normally this isn't a problem as you can use Trim() to remove them, but when it's a file upload box IIS automatically creates a temporary file, which when you try and access it suddenly realises it doesn't exist and throws an error. The extra code detects whether or not an actual file has been uploaded, I have the people in the Macromedia ColdFusion forums to thank for helping me find it.
<CFSET attachfile = ''>
<CFSET localpath = 'c:\documentstore'>
<CFIF FindNoCase('Mac','#CGI.HTTP_USER_AGENT#') GT 0 AND FindNoCase('MSIE','#CGI.HTTP_USER_AGENT#')>
<CFCATCH TYPE="Any"><CFSET aBinaryObj = 'abc'></CFCATCH>
<CFSET aBinaryObj = 'abc'>
<CFIF len(aBinaryObj) gt 2 AND Len(Trim(FORM.uploadfile)) GT 0>
<CFSET attachfile = File.ServerFile>
Listing files in the sandbox
This code will list all of the files in the directory you set as 'filestore'. All files are made in to links to 'file_display.cfm'
<CFSET filestore = 'c:\documentstore'>
<CFIF type IS 'File'>
<a href="file_display.cfm?file=#name#">#name#</a><br />
Displaying files from the sandbox
Here we're going to use the CFCONTENT tag to read the file. The file name has been given to us through the URL by the previous script, which listed the contents of the directory. To get the browser to download the file, or ask you what to do with it, we're going to set the content type of the page to 'application/unknown'. This is the most reliable way I've found of getting various browsers to ask what to do with the file.
To ensure CFCONTENT is given something it can display, the CFIFs in this script first check that there is something in the 'file=' coming through the URL, and then it uses the name of the file as the CFDIRECTORY filter. If the file exists in the directory, CFHEADER is used to tell the browser that the file is an attachment, and what the name of it is, and CFCONTENT is then used to 'display' it to the browser, which means it can download it as a file. (Thanks go to Marco Fagiolini of Inetware for the CFHEADER tip.)
<CFPARAM NAME="URL.file" DEFAULT="">
<CFSET filestore = 'c:\documentstore'>
<CFIF URL.file IS NOT ''>
<CFIF FileCheck.RecordCount GT 0>
<CFHEADER name="Content-disposition" value="attachment; filename=""#URL.file#""">
<CFCONTENT type="application/unknown" file="#filestore#\#URL.file#" DeleteFile="No"></CFCONTENT>
Enhancement: If you wished, you could automatically detect what the type of file is by looking at the last three characters (presuming they have been uploaded from a Windows machine) and set the content type of the page to match that file type. E.g. for .doc files you could use type="application/msword" and if the person has it, Word will automatically be started to read the file when they click on the link. Using 'image/gif' and 'image/jpeg' for Gifs and Jpegs will generally cause them to be opened in the browser, just like they would if you referenced the image direct.
Here's a working version of the code, four different files for the different tasks required: zip of scripts
Paul Silver. October 2003