Over a million developers have joined DZone.

Secure User Uploads and Exploiting Served User Content

DZone's Guide to

Secure User Uploads and Exploiting Served User Content

· DevOps Zone
Free Resource

Download “The DevOps Journey - From Waterfall to Continuous Delivery” to learn learn about the importance of integrating automated testing into the DevOps workflow, brought to you in partnership with Sauce Labs.

Hosting user uploaded files (photos, PDF, forum avatar images, etc.) opens a potential  attack vector against your website. In this blog post I discuss about the common pitfalls with hosted user content and how to prevent using them as an attack vector. The blog post discusses this in Python programming languageDjango web framework and Plone CMS, but the advices apply on all systems.

I look the issues from the perspective of compromising user data and your server (shellcode); denial of service and other brute force attacks are not considered here. Also, web technologies which maps executable scripts to URLs, like PHP, are open for far more attack vectors which are not discussed here (prone to uploading server-side executable issues like this).

The issue list here is non-exhaustive. Please submit in the blog comments if I missed something.

1. Filename and path attacks

Letting user uploaded filenames through to the server file system from HTTP request is a subject to attack vectors both when storing the file and later when serving the file.

Do not disclose the server path where you are storing user uploaded files (full path disclose).

Do not save the user uploaded filenames on the file system as is. It’s ok to save this information separately to the database if you want to later hint the browser to save the download with the original name, as with the content-disposition header example below. Instead, use a running counter (database id), a random hex string or a hash as a name when writing the file. Counters are good, since one needs to avoid filename conflicts in any case. In Django, File Storage API takes care of filename conflicts and sanitizes the uploaded filenames, in Plone/Zope filenames are tied to the database transaction id (blobstorage).

Do not mix paths and filenames as a string operation e.g. by concatenating the upload folder path with the filename . The attacker then could use relative (..) and absolute (/) paths to overwrite any writeable file on your server.

If you cannot tie the filename to the database entry, you may want to normalize (munge) the filename before writing it to the disk. Also, non-ASCII filenames cause issues in intra-filesystem communications (backups, migrations, etc.).

Distribute user uploaded files in hashed folder structure. Do not store all the uploads in the same file system folder. Even with small amount of files (1000+) you may start overloading the file system.  For example, Django web framework simply dumps all the files to the same folder by default. If possible, put all user specific content to a folder with username and then create hashes inside this folder.

2. Serving unsanitized documents

Downloading the user uploaded file from your server is the other side of the user content issues. Though it might not look like a potential attack vector first, due to history of HTML and HTTP, the backwards compatibility and how browsers handle downloads this is another can of worms.

Serve the unsanitized user content from a separate top level domain (TLD) – not from the domain where the user is logged in. This protects against vulnerabilities where a malicious user uploads a file on your server (HTML, JavaScript, Flash) and then links other users to open this file or just views it as an image. Mostly the issues are related to HTTP cookie stealing and hijacking logged in session. Browser have had various bugs related to fooling them with the download content type (Interner Explorer MHTML content type detection bug). Read Google Online Security Team’s opinion regarding this matter.

Never read files from the file system by user provided filename coming in a HTTP request. Always refer files by ids stored in the database. (Relative Path Travelsal attackDirectory Traversal in Django security guide).

When serving the file, do not put user supplied filenames to download URLs as it is potential XSS attack vector. Instead, use HTTP response Content-disposition header. Just make sure your web framework is not open to a HTTP response header injection exploitby using new lines in the filename.

3. Image uploads and content decode attacks

When you are processing user uploads on the server side, e.g. by resizing images, you are open to codec bugs. Most codecs are implemented by open source C libraries which  have native bindings to your run-time environment (Python, Ruby, Node.js). When you are processing the user uploaded file, the code path jumps to a native library code which is open for traditional C exploits (buffer overflow).

To reduce the attack surface, you want to limit the number codecs used on your website and only enable the codecs which you really want to support. In the case images, usually these are JPEG, GIF and PNG. Even the most common codecs can be unsafe now and then (PNG vulnerability 2012). By default, the imaging libraries may have all file formats enabled and are not picky about what they process.

File extension blacklists or whitelists are useless. Native libraries usually operate by detecting the content type from the payload. Here is example of how Python imaging library (Pillow) detects the image format. This means that even if you are allowing file uploads with .PNG extension only, the user can potentially upload a renamed SVG file; SVG has much more complex codec increasing the attack surface against your service. In Python’s case you can use python-magic to detect the actual file payload and do your own file content validation in the form validation phase. Or alternative hack Pillow by dropping out file formats from PIL.Image.OPEN registry.

If you are processing very complex files with native codecs (video, PDF, .DOC, etc.) it might make sense to run the processor in an external process with limited UNIX privileges.

4. More resources

Discover how to optimize your DevOps workflows with our cloud-based automated testing infrastructure, brought to you in partnership with Sauce Labs


Published at DZone with permission of Mikko Ohtamaa, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.


Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.


{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}