Avoiding common file upload vulnerabilities
The web applications of today are always looking for new ways to make the user experience as personal as possible, one way that this is achieved is by allowing their users to upload a personal profile image, also known as an avatar. This functionality is great, however, if not securely implemented can open the application up to a host of security issues. To try and mitigate these security threats, developers use a number of programming techniques to restrict file uploads to a select number of approved file types. Here are some standard validation methods and how these methods can prove ineffective.
A blacklist is a reasonably simple concept, in this use case, a blacklist would be a list of forbidden file extensions. Although from the outside this seems like a good way of confirming what file types aren’t allowed these can often be bypassed. For example, let’s say the extension .php is blocked and is not allowed to be uploaded. Pretty secure right? Now, what happens if an attacker uploads a file called harmful.pHp? As this filename has not been banned within the blacklist it would be allowed and so it would be uploaded. Furthermore, as file extensions are not case-specific this would be executable and so the attacker would have remote code execution.
Additionally, file types can often accept a number of file extensions. Let’s take PHP as an example, the usual file extension is .php, however, another accepted extension which can often work is .php3. As multiple extensions can exist, a blacklist can often be hard to maintain due to the sheer number of possible extensions.
So, what’s an alternative to a blacklist? One valid option could be a whitelist. Rather than specifying all of the extensions which are banned, you would simply need to specify the small number of extensions which are allowed. This can often be a must easier way of maintaining your application as you may only need to allow 4 extensions rather than banning 1000. Building on top of this, if the application in question only allows .jpg, .png and .gif images it would be bad practice to block .JPG files which is why before processing the upload and validating it can often be a smart idea to convert the filename to lowercase.
Trusting the provided filename
Upload functionality is often implemented in such a way that the specified file is uploaded exactly as it would appear on the user’s computer, filename included. The problem with this approach is that although the specified uploads directory may have permissions in place which prevent certain file types from executing this would not affect other directories. If an attacker were to include path traversal ../ dot-dot-slash in their filename then the uploaded file would step up a directory and be executable, this is because it would not be located in the uploads folder. Here’s how that path would look when the application tries to move the selected file.
In the example provided, the supplied filename would be ../provided-file.php. So, how could you protect against this? Rather than simply uploading the file and saving it to the specified location, it’s recommended to create a random filename and simply append the extension to the end, this filename could look like fsys62hsg27qa1.php.
While we’re on the topic of trusting file names, using include functions as a method to determine the file type is extremely insecure as this simply checks to see if the given string exists within the file name. Let’s give an example, here we have a file called malicious.jpg.php, when an include function runs it checks the filename to see if .jpg exists. As the exact text .jpg does appear in this filename it would say it’s valid even though the file extension is actually .php.
Trusting the file contents
Developers can often build their applications in such a way that they check the contents of the specified file to see if match up with what’s actually allowed. Although files are almost always different there are often common strings of characters which can be found within them, one example being PDF files. You will almost always find the first few characters of a PDF document are %PDF-1.4. With this knowledge, an attacker may simply choose to upload a file with that string at the beginning, if further checks haven’t been implemented then the file would be accepted. Here’s an example of how they may choose to do this.
<?php echo system($_GET['command']); ?>
As you can see, the file would appear as though it’s a PDF document, but if gets saved to the file system as a .php file then this would be a backdoor to the web server.
Things to remember when building a secure file upload
With the knowledge we’ve now gained we can look at how we can take this information and apply it when making a secure file upload. For starters, we should only allow files to be uploaded which are included in an accepted extension list, also known as a whitelist. Second, we should not trust the original filename as this can lead to path traversal, a randomly generated name should always do the trick. Additionally, adding the extension onto the new filename yourself will ensure that even if the upload is successful it will only be saved as the trusted extension you specify.
Like a lot of security threats, developers should not trust the information which has been provided. Always verifying the information which is being supplied by the user is a must which is why we recommend not simply using include functions to look for certain pieces of text within the file or its name.