AppSec Blog

AppSec Blog

8 Basic Rules to Implement Secure File Uploads

The IIS semicolon file extension issue prompted me to jot down some of the rules to implement file uploads securely. This is in particular complex as there is usually no easy way to validate the content of the file.

The overall goal is to build a set of defensive layers that tightly control the process of uploading the file and later retrieval of the file. The user will always interact indirectly with the file and never directly access the file system without application control.

1. Create a new file name


Do not use the user supplied file name as a file name on your local system. Instead, create your own unpredictable file name. Something like a hash (md5/sha1) works as it is easily validated (it is just a hex number). Maybe add a serial number or a time stamp to avoid accidental collisions. You may add a secret to the name to make it harder to guess the file name. If you need to keep the original file name: use a look-up table to link the validated user supplied file name to the server created name.

2. Store the file outside of your document root


If your document root is /var/www/html, create a directory /var/www/uploads and use it to store uploaded files. That way, an attacker will not be able to retrieve the file directly. This will allow you to provide fine grained access control. The file will not be parsed by the server's application language module but the source of the file will be streamed.

3. Check the file size


You should set a maximum file size in the upload form, but remember: It is just advisory. Make sure to check the file size after the upload completed. Be in particular careful if you allow the upload of compressed files and later uncompress them on the server. This scenario is very hard to secure.

4. Extensions are meaningless


The motivation for this post is the ';' issue in IIS. However, even Apache doesn't always behave the way you expect it to. Try 'something.php.x' in Apache and chances are that php code will be executed. Its a feature ;-) . If you stream a file back to the user, the extension isn't what matters, but the Content-Type header and the file's header. It is best to use the "file" command on unix to check the file type. But even this is not fool proof. It will just check the first few bytes. In PHP for example, a file may start with a GIF header, but later if the PHP engine sees a "<?php" tag, it will happily interpret an embedded PHP script.

5. Try a malware scan


The extension is right, and you checked that the file is actually a valid JPEG file per it's header. However, it could still be a malicious JPEG using one of the many image parser bugs to exploit clients downloading the file. There is no great defense against this as far as I am aware. One possible work around is to "rebuild" the file. Convert the JPEG to a GIF and back to a JPEG. This will likely strip out any malicious feature. But this technique could expose your servers to just the same image parser bugs.

6. Keep tight control of permissions


Any uploaded file will be owned by the web server. But it only needs read/write permission, not execute permissions. After the file is downloaded, you could apply additional restrictions if this is appropriate. Sometimes it can be helpful to remove the execute permission from directories to prevent the server from enumerating files.

7. Authenticate file uploads


File uploads, in particular if these files are viewable by others without moderator review, have to be authenticated. This way it is at least possible to track who uploaded an objectionable file.

8. Limit the number of uploaded files


Many developers limit the file size, but not all limit the number of files uploaded in a request. Make sure to apply reasonable limits. But be also ready for a DoS attack that just uploads a large number of small files. Pick an appropriate directory structure to limit the number of files per directory and pick an appropriate file system.

Conclusion


Let me know if you can think of other issues to consider. Some depend on the application, but the eight above are generic. For example, if you deal with XML files you can validate them against a schema. A text file can be validated based on dictionaries. Particular image formats can be analyzed more closely for malicious content. For PDFs, you can strip out javascript which often causes problems and for HTML you could use libraries like HTML purifier. Using a distinct upload partition can help against having a denial of service attack impact other parts of the system and it will also allow for additional access control. A human moderator may be advisable if inappropriate content is a problem.

Finally: Remember the #1 rule of good web application security. All users are evil!

5 Comments

Posted February 16, 2010 at 9:07 PM | Permalink | Reply

Karim Said

All really good points. Thank you.

Some other possibilities:
* Whenever possible, avoid uploading the file at all. Simply parsing files in as bitstreams and pulling only the specific information you need can help to limit the possibility of malicious code getting anywhere near your system. Plus, the file will never have to live anywhere on your box.
* If it's functionally applicable, offer a pre-formatted template to users. Encode the template in some way that an attacker would have a difficult time spoofing. Upon upload, just validate the file is what you're expecting or dump it.

Posted August 04, 2010 at 4:45 AM | Permalink | Reply

TNA

All great tips. Thanks

Posted October 26, 2010 at 9:18 PM | Permalink | Reply

Irina

"Keep tight control of permissions"
I'm all for that, but some servers wont allow uploading of anything unless permissions are set to 777. Is there a way to go around that 'cant write, permission denied' thing?
Great tips, I use most of them!

Posted May 23, 2013 at 12:05 PM | Permalink | Reply

sadman

Nice tutorial.

I have found another website with fancy php scripts like contact form,php file upload,user registration form etc for free.

Posted August 22, 2013 at 3:52 PM | Permalink | Reply

Amin

4. Extensions are meaningless

I'm checking the extension before upload. but before downloading the file, check if the file is Image (using .NET 4.0 System.Drawing.Image).
if the file is image I write it into response with header "image/jpeg, gif..."
if not an image, force it to download using response.writefile and not show or execute it. I also have only read/Write permission on my uploads folder outside of my APP directory.

Is everything safe? Should I sleep comfortable at nights?

Post a Comment






* Indicates a required field.