Tips for Secure File Uploads with ColdFusion

coldfusion

Allowing someone to upload a file on to your web server is a common requirement, but also a very risky operation. So here are some tips to help make this process more secure.

Don't rely on cffile accept attribute

The accept attribute gives a terrible false sense of security. Every time I present on CFML Security I ask the question: If I have this code is there any way I could upload a CFM file?.

<cffile action="upload"
  filefield="photo"
  accept="image/gif,image/jpeg"
  destination="#ExpandPath("./photos/")#">

The answer is YES, often to the surprise of most!

The cffile accept attribute uses the mime type that your browser sends to the server. Read that again... your browser tells cffile what the mime type is. It's very easy to spoof the mime type. For example:

<cfhttp url="http://target.example.com/upload" method="post">
 <cfhttpparam file="#ExpandPath("badstuff.cfm")#"
  mimetype="image/gif" 
  type="file"
  name="photo">
</cfhttp>

For this reason you need to ensure that cffile.serverFileExt is of an extension you allow, and one that your server will not execute.

Use a file extension whitelist rather than a blacklist, in other words you don't just check to make sure it is not a .cfm, make sure it is only one of: "jpg,png,gif". This way if someone installs PHP on your server, you don't have to update the code to block that file extension as well.

Validate that the File Extension matches File Format

You can use a Java API like JHOVE which reads the file contents to validate that it is of the asserted file format. It supports jpg, gif, pdf, tiff, and more.

In addition CF8 has the IsImageFile("path") and IsPDFFile("path") functions you could use. If you do use IsImageFile just make sure that you have upgraded your JVM to one that doesn't have the issue that can cause an image file to crash your server. See Mark Kruger's blog entry for details.

Always upload to a temp directory outside of the Web Root

Suppose I ran the same hack above with cfhttp but you now have code in place to delete the file if the extension is incorrect. There is a slight chance that I could execute that file before you can delete it if you uploaded it into the web root (and I could predict where it would be placed).

Once you have validated the upload, you can move it to its desired location.

Keep uploaded files outside the web root

If possible keep uploaded files outside of the web root and serve them with cfcontent. In some cases this is not possible, but seriously consider this as it does ease the risk significantly.

Remove execute permissions from upload directories

The reason for this should be obvious, but is something we often forget to do.

Upload to a static content server

If possible upload content to a server other than the application server, a server that only serves static content (for example Amazon S3).

Don't trust on the client supplied file name

The client supplied file name could possibly contain SQL Injection, cross site scripting, or CRLF Injection.

It's best to strip out non alpha numeric characters (perhaps with the exception of dash and underscore).

ColdFusion Administrator Settings

There are a few Administrator Settings that you should pay attention to related to file uploads (and large HTTP POST operations). They are under Server Settings » Settings under the heading Request Size Limits:

CF Administrator Request Size Limits

The first setting is the maximum size of a POST, and therefor also a file upload. The default 100mb is probably bigger than needed for most web apps, you can lower it to mitigate DOS potential. Chances are your web server is also capable of limiting the post size, on apache you can use the LimitRequestBody directive to do this.

The next setting Request Throttle Threshold should probably be lowered to 1MB, this puts any request larger than 1mb into a throttle for synchronous processing.

The third setting Request Throttle Memory is the maximum size of request throttle queue. The default is kind of high, if you don't have a lot of large file uploads going on at the same time this should be lowered to say 50mb (it shouldn't be lower than the Maximum size of post data, or the Request Throttle Threshold, but it could be equal to the max size.). Consider that on a 32bit server, the max JVM size is typically not much bigger than 1GB, you could allow 1/5th of your server resources to be consumed by file uploads with the default setting.

Sandboxes

If you are using the Enterprise edition of ColdFusion you can setup a sandbox for your file upload directory, and remove execute permission. This only applies to ColdFusion template execution (not PHP scripts for example).

Restrict using Web Server

Use you should limit your uploads directory to only allow static files to be requested. For example on IIS you can remove the handler mappings for CF, and then use Request Filtering to limit file extensions to a specific whitelist, so that IIS will reject any request under /images/ that is not a .gif, .png, or .jpg for example.


Do you have any additional tips?



Related Entries

2 people found this page useful, what do you think?

 Download FuseGuard WAF for ColdFusion

Trackbacks

Trackback Address: 701/EF20EBCFDE17E3640C4E91EBFA85B5FB

Comments

On 06/24/2009 at 2:05:59 PM UTC pedrom40 wrote:
1
This may be a silly question, but if someone is uploading from a Mac, will it still be able to verify from the extension if there isn't one?

On 06/24/2009 at 2:10:05 PM UTC Pete Freitag wrote:
2
I think the browser may be able to send the appropriate mime type if there is no file extension (I would have to look into that further), but remember you can't trust what the browser sends anyways, it could be spoofed.

On 06/24/2009 at 3:48:35 PM UTC Ben Nadel wrote:
3
Pete, excellent tips. Disabling execute permissions on uploads directory is really nice.

On 06/24/2009 at 5:27:31 PM UTC Tom Mollerus wrote:
4
Great set of tips; I'd also suggest that if you have Apache, watch out for any uploaded files that have multiple file extensions (e.g., file.php.html). By default, Apache will run the file with the PHP handler even though the last extension is something else. See http://www.mollerus.net/tom/blog/2009/02/watch_out_for_apache_handling_of_files_with_multip.html

On 06/24/2009 at 5:28:22 PM UTC Pete Freitag wrote:
5
Thanks Ben!

On 06/24/2009 at 5:29:58 PM UTC Pete Freitag wrote:
6
@Tom Great tip, thanks for posting

On 06/24/2009 at 8:42:28 PM UTC jason olmsted wrote:
7
I'm revisiting an app that allows customer file uploading, and one approach I'm considering is using CreatUUID() to generate a server side file name and stick the customer provided filename in a related database entry (going through cfqueryparam, of course).

On 06/26/2009 at 5:51:03 PM UTC Jason Dean wrote:
8
Pete, great post. Thanks for the tips. I've been meaning to blog about this myself. You beat me to it. But you also covered quite a lot that I didn't know, so thank you for that. Very enlightening.

On 06/27/2009 at 9:30:53 AM UTC Mike Letson wrote:
9
When I upload files, there are two things I always to before it gets to the action page (or code block). First, I use JavaScript to inspect the file extension and act accordingly. Second, I do the same extension validation on the server side. If all is well, then the suggestions offered here would be good!

On 06/30/2009 at 10:24:13 AM UTC Pete Freitag wrote:
10
@jason olmsted - Make sure you sanitize the customer supplied file name before sticking it in the db, as they could potentially include something to trigger XSS, you can use this regex: ReReplace(fileNane, "[^a-zA-Z0-9_-]", "", "ALL"), and ofcourse still use cfqueryparam (always use it :)

@jason dean - glad you found it useful, feel free to post about this on your blog as well. The more people who read about it the better.

@mike letson - Using JavaScript validation is good for user experience but doesn't have any effect on security.

On 06/30/2009 at 10:28:49 AM UTC Jamie Krug wrote:
11
Great tips, thanks for sharing, Pete. I'm comforted by the fact that I tend to follow all suggestions you've made, with the exception of a static content server. I really do like that idea and intend to leverage Amazon S3 for static content whenever possible in the future.

It's worth noting that you could achieve similar security on your own server, if needed, by leveraging Apache and creating a static content virtual host. You can have that virtual host limit file extensions and/or mime types and rewrite to 403-forbidden any suspicious requests.

On 06/30/2009 at 10:36:20 AM UTC Pete Freitag wrote:
12
@Jamie thanks, yes that is worth noting. I didn't intend to suggest that S3, or some third party CDN was the only way.

On 07/01/2009 at 4:15:00 PM UTC Sean Corfield wrote:
13
Just so I'm clear: the problem isn't really that they can upload a CFML file, it's that they can predict the URL afterwards in order to execute it, right?

If so, placing an Application.cfc / Application.cfm file in your images directory that blocks requests would be sufficient to prevent execution?

Or am I missing something?

On 07/02/2009 at 8:08:56 AM UTC Pete Freitag wrote:
14
@Sean - They don't necessarily have to be able to predict it, the application may disclose it in an image tag, or link.

Adding Application.cfc/cfm is a good idea, but it can only block ColdFusion requests, I could upload a .jsp file instead, or some other file type that might be executed server side.

On 07/02/2009 at 10:21:24 PM UTC Nathan Dye wrote:
15
Extending the sandbox design:

We protect uploads from getting downloaded, without the application running more CFMX code to authorize:

Having Enterprise on *IX, we build a sandbox, where

* doc, a directory outside the Apache directories, allows read, write, delete for CFMX. OS permissions allow only j2ee to write, any can read.

* backend, another directory outside Apache, allows only read for CFMX. OS permissions allow only the project owner to write, any can read.

Now CFMX code can scan the backend directory and authorize what the user can see.

Meanwhile Apache can't leak the files on its own. Application code must decide whether to read from those directories, and decide what to send to who.

(And it's late, so I'm too tired to clean the grammar.)

Anyway, dittos on all the rest, and THANKS! Hope it helps.

On 07/02/2009 at 10:22:55 PM UTC Nathan Dye wrote:
16
(sorry forgot to subscribe, I'm way interested in feedback)

On 07/06/2009 at 2:25:15 AM UTC Sebastiaan wrote:
17
What about if you have CFIDE available for a shared server (for CFAJAX stuff) - are you then more vulnerable for these attacks that wreak havoc in the CF-community these days?

And how to defend yourself and your server (and hostingprovider)?

PS: My Gravatar is enabled via my Hotmail address - any chance you'll allow those mail-extensions in the future?

On 07/09/2009 at 10:01:37 PM UTC Michael Williams wrote:
18
@Sean, You make an excellent point I haven't thought about. You can effectively disable CF from ever executing from that folder with the right application logic. However, it still leaves open the possibility that bad files can 'exist' on the server to be exploited outside of being executed by CF.

On 07/31/2009 at 9:11:49 AM UTC Marllon Capdeville wrote:
19
@Pete, How could I do a good file extension test before the file itself has been uploaded by CFFILE even using all the security mesures pointed here? Or the file will be uploaded first, then tested for extension and finally deleted by CFFILE DELETE? Thanks.

On 07/31/2009 at 9:19:00 AM UTC Jamie Krug wrote:
20
@Marllon:

I'm fairly certain you'll need to upload the file first. You could use some JavaScript to check the extension, but since that's client side it could always be spoofed. You don't have definitive control/evidence until the file is server-side.

Check out Pete's "Always upload to a temp directory outside of the Web Root" section, above.

On 07/31/2009 at 11:30:50 AM UTC Pete Freitag wrote:
21
@Marllon Yes as Jamie mentioned you need to upload the file to a dir outside of the web root and then check the file extension. Otherwise the only way you could do this before calling cffile would be to use a Servlet Filter, or something else that runs before the CFML engine.

On 02/24/2010 at 1:35:17 PM UTC Jordan wrote:
22
I'd just like to point out, in response to the first commenter, that Mac OS X files do indeed have file extensions. By default they are hidden to the user but upon sending a file out (as in this case) they do apply.

On 03/02/2010 at 7:12:59 PM UTC Brian H.. wrote:
23
I just wanted to chime in to remind people that the same goes for emails which attachments that are downloaded by CFpop. I have a system which acts as a web client for accessing email ( <gasp> and saves the attachments into a pub directory</gasp> ), and I basically crapped myself when I realized that I was doing no checking, and someone could have simply emailed us a CFM file. My two faults here are A: serving attachments publicly and B: Not filtering attachments. Very old app, but Jeeze! Horrible.

-Brian

On 03/15/2010 at 5:38:20 AM UTC James wrote:
24
Hi, I've seen comments about checking for a double file extensions. Does anybody have any code that would allow me to do this.

On 09/03/2010 at 2:54:56 AM UTC Zafer Altun wrote:
25
Very helpful tips. Thanks.

On 01/25/2011 at 6:21:13 PM UTC fgwenger wrote:
26
Does anyone have any suggestions for virus scanning on ColdFusion file uploads? We want to allow trusted sources to upload files to us, but we are worried about files with viruses being accidentally uploaded. Thoughts? Thanks.

Post a Comment




  



Spell Checker by Foundeo

Recent Entries



foundeo


did you hack my cf?