Making User File Uploads Safe(r)

April 6, 2016

Peter Wolanin

Acquia Engineering & Drupal Security Teams

pwolanin

Photo by amazeelabs, by-nc-sa

You allow users to upload files?

  • Cross-site scripting (XSS) risks
  • cross-domain data hijacking (CSRF)
  • DoS with large files
  • DoS or other attacks via image effects

Browser security model - same origin

Scripts being served by a website can access that site as you by using session cookies

This is why XSS is dangerous

Common gotchas with browser security

  • Browser mime-type sniffing
  • Cookies shared to subdomains or paths
  • Cross-domain attacks
A safe-looking file extension parsed as HTML + javascript

File content


<html>
<body>
<script>
alert(document.cookie);
</script>
</body>
</html>
              
No content-type, browser "guessed".
Defused by setting nosniff

Drupal 7 and 8

  • nosniff in .htaccess since 7.40 (October): https://www.drupal.org/node/462950
  • (Optional) enable/configure mod_mime for apache
  • Or configure $WEBSERVER to send nosniff

Cross Domain Data Hijacking

https://soroush.secproject.com/blog/2014/05/even-uploading-a-jpg-file-can-lead-to-cross-domain-data-hijacking-client-side-attack/

A valid file upload like a .jpg can actually be flash content

Embedding with an OBJECT tag another site can enable CSRF and data hijacking

The heavy fix - content disposition


<IfModule mod_headers.c>
  <FilesMatch "\.(?i:pdf)$">
    ForceType application/octet-stream
    Header set Content-Disposition "attachment"
  </FilesMatch>
</IfModule>
              

Lighter fix - distinct files domain

By using a different domain or subdomain you can avoid sending session cookies

For example, gmail attachments are served from https://mail-attachment.googleusercontent.com

www. stripping

Avoid bare domain

Set $cookie_domain in settings.php for www. (Drupal 7)

Drupal 7 core issue: https://www.drupal.org/node/2522002


/**
 * Drupal automatically generates a unique session cookie name
 *  for each site based on its full domain name...
 */
# $cookie_domain = 'example.com';
              

Safe(r) files recipe

  • Serve uploaded files from a subdomain or different domain (make sure session cookie is not sent)
  • Do that via CDN module or a little custom module
  • Use sites directory, sites.php, or redirect to prevent Drupal on the files domains
  • Block public files on the Drupal domain

Custom code version - alter hook


function mymodule_file_url_alter(&$uri) {
  if (file_uri_scheme($uri) == 'public') {
    $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
    $path = $wrapper->getDirectoryPath();
    $path .= '/' . file_uri_target($uri);
    $uri = 'http://downloads.drupal-7.local:8083/' . $path;
  }
}
              

Block direct access via .htaccess in files directory


RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteRule . - [F]
              

Protect against HTTP HOST Header attacks

It's important to configure your site to respond to only the expected domains

See: https://www.drupal.org/node/1992030

Thanks! Questions?