|
| 1 | +# File Upload Security |
| 2 | +For an in-depth understanding of the potential security risks of providing file uploads and possible mitigations, please refer to the [OWASP - Unrestricted File Upload](https://www.owasp.org/index.php/Unrestricted_File_Upload) documentation. |
| 3 | + |
| 4 | +To securely setup the project to serve uploaded files, please refer to the sample [Secure file upload serving configurations](#secure-file-upload-serving-configurations). |
| 5 | + |
| 6 | +By default, all sample upload handlers allow only upload of image files, which mitigates some attack vectors, but should not be relied on as the only protection. |
| 7 | + |
| 8 | +Please also have a look at the [list of fixed vulnerabilities](VULNERABILITIES.md) in jQuery File Upload. |
| 9 | + |
| 10 | +## Mitigations against file upload risks |
| 11 | + |
| 12 | +### Prevent code execution on the server |
| 13 | +To prevent execution of scripts or binaries on server-side, the upload directory must be configured to not execute files in the upload directory (e.g. `server/php/files` as the default for the PHP upload handler) and only treat uploaded files as static content. |
| 14 | + |
| 15 | +The recommended way to do this is to configure the upload directory path to point outside of the web application root. |
| 16 | +Then the Webserver can be configured to serve files from the upload directory with their default static files handler only. |
| 17 | + |
| 18 | +Limiting file uploads to a whitelist of safe file types (e.g. image files) also mitigates this issue, but should not be the only protection. |
| 19 | + |
| 20 | +### Prevent code execution in the browser |
| 21 | +To prevent execution of scripts on client-side, the following headers must |
| 22 | +be sent when delivering generic uploaded files to the client: |
| 23 | + |
| 24 | +``` |
| 25 | +Content-Type: application/octet-stream |
| 26 | +X-Content-Type-Options: nosniff |
| 27 | +``` |
| 28 | + |
| 29 | +The `Content-Type: application/octet-stream` header instructs browsers to display a download dialog instead of parsing it and possibly executing script content e.g. in HTML files. |
| 30 | + |
| 31 | +The `X-Content-Type-Options: nosniff` header prevents browsers to try to detect the file mime type despite the given content-type header. |
| 32 | + |
| 33 | +For known safe files, the content-type header can be adjusted using a **whitelist**, e.g. sending `Content-Type: image/png` for PNG files. |
| 34 | + |
| 35 | +### Prevent distribution of malware |
| 36 | +To prevent attackers from uploading and distributing malware (e.g. computer viruses), it is recommended to limit file uploads only to a whitelist of safe file types. |
| 37 | + |
| 38 | +Please note that the detection of file types in the sample file upload handlers is based on the file extension and not the actual file content. This makes it still possible for attackers to upload malware by giving their files an image file extension, but should prevent automatic execution on client computers when opening those files. |
| 39 | + |
| 40 | +It does not protect at all from exploiting vulnerabilities in image display programs, nor from users renaming file extensions to inadvertently execute the contained malicious code. |
| 41 | + |
| 42 | +## Secure file upload serving configurations |
| 43 | + |
| 44 | +### Apache config |
| 45 | +Add the following directive to the Apache config, replacing the directory path with the absolute path to the upload directory: |
| 46 | + |
| 47 | +```ApacheConf |
| 48 | +<Directory "/path/to/project/server/php/files"> |
| 49 | + # To enable the Headers module, execute the following command and reload Apache: |
| 50 | + # sudo a2enmod headers |
| 51 | +
|
| 52 | + # The following directives prevent the execution of script files |
| 53 | + # in the context of the website. |
| 54 | + # They also force the content-type application/octet-stream and |
| 55 | + # force browsers to display a download dialog for non-image files. |
| 56 | + SetHandler default-handler |
| 57 | + ForceType application/octet-stream |
| 58 | + Header set Content-Disposition attachment |
| 59 | +
|
| 60 | + # The following unsets the forced type and Content-Disposition headers |
| 61 | + # for known image files: |
| 62 | + <FilesMatch "(?i)\.(gif|jpe?g|png)$"> |
| 63 | + ForceType none |
| 64 | + Header unset Content-Disposition |
| 65 | + </FilesMatch> |
| 66 | +
|
| 67 | + # The following directive prevents browsers from MIME-sniffing the content-type. |
| 68 | + # This is an important complement to the ForceType directive above: |
| 69 | + Header set X-Content-Type-Options nosniff |
| 70 | +</Directory> |
| 71 | +``` |
| 72 | + |
| 73 | +### NGINX config |
| 74 | +Add the following directive to the NGINX config, replacing the directory path with the absolute path to the upload directory: |
| 75 | + |
| 76 | +```Nginx |
| 77 | +location ^~ /path/to/project/server/php/files { |
| 78 | + root html; |
| 79 | + default_type application/octet-stream; |
| 80 | + types { |
| 81 | + image/gif gif; |
| 82 | + image/jpeg jpg; |
| 83 | + image/png png; |
| 84 | + } |
| 85 | + add_header X-Content-Type-Options 'nosniff'; |
| 86 | + if ($request_filename ~ /(((?!\.(jpg)|(png)|(gif)$)[^/])+$)) { |
| 87 | + add_header Content-Disposition 'attachment; filename="$1"'; |
| 88 | + # Add X-Content-Type-Options again, as using add_header in a new context |
| 89 | + # dismisses all previous add_header calls: |
| 90 | + add_header X-Content-Type-Options 'nosniff'; |
| 91 | + } |
| 92 | +} |
| 93 | +``` |
0 commit comments