Never trust an upload's filename
Tags: security, securecoding
Published: 2021-05-22 09:25:21

---
canonical_url: https://kahdev.hashnode.dev/never-trust-an-uploads-filename
---


It's generally accepted that we should never simply trust a user's input. Otherwise, we're vulnerable to malicious input ([CE-5](https://owasp.org/www-project-proactive-controls/v3/en/c5-validate-inputs)). For a file upload from a user, it’s common to check their file size or type, especially when restricting them by those two attributes. But one that might get overlooked is the filename.

Checking the filename is especially important when it displayed on a web page to prevent HTML or XSS injection. That’s just on the frontend. On the backend, there are the other usual injections to worry about. Like SQL injection, when storing the name in a database. Or path injection, if storing in a path based on the name.

It might be tempting to dismiss this possibility as something that would never happen. After all, we must make the files first before we can upload them and there are file-naming rules. We can't simply make one with just any name they want! However, the rules won’t prevent malicious filenames!

## OS file naming rules won't prevent injection

Operating systems have rules about what *can* be in a filename. But, different operating systems can have different rules. So, a filename prohibited in one operating system may be allowed in another.

For example, `<img onerror="alert('XSS')" src="nowhere">.txt`. The file can't be created in Windows because it _doesn't_ allow `<` and `>` (see [Naming a file](https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions)). However, Linux _does_ allow them. For example, we could use [touch](https://man7.org/linux/man-pages/man1/touch.1.html) to create the file:

```bash
touch '<img onerror="alert('"'XSS'"')" src="nowhere">.txt'
```

## Actually, we don't even need to make the files

HTTP clients usually send file uploads using a multi-part POST request. We can use [Wireshark](https://www.wireshark.org/) to see what the request looks like. Here's one from [cURL](https://curl.se/):

```
POST /attachments HTTP/1.1
Host: 127.0.0.1:3000
User-Agent: curl/7.76.1
Accept: */*
Content-Length: 250
Content-Type: multipart/form-data; boundary=------------------------6c44be865e1d13e6

--------------------------6c44be865e1d13e6
Content-Disposition: form-data; name="attachment"; filename="Lorem_Ipsum.txt"
Content-Type: text/plain

Lorem ipsum dolor sit amet, consectetur cras amet. 
--------------------------6c44be865e1d13e6--
```

Notice the filename field is at the end of the `Content-Disposition` header. We can use this as a template to make our own requests. Simply. copy and paste into a file, change the filename and update the `Content-Length`. Then use [Ncat](https://nmap.org/ncat/guide/index.html/) to send the request. For example, to send the request in `request.http` to `http://localhost:3000`:

```bash
ncat localhost 3000 < request.http
```

## Using HTTPS isn't enough either

The example above sent a forged request to an insecure server (i.e one that isn't using SSL/TLS). But changing over to HTTPS still won't be enough to prevent malicious filenames - [Ncat](https://nmap.org/ncat/guide/index.html/) also works with HTTPS connections:

```
 ncat -C --ssl localhost 3000 < request.http
```

Even if it didn't, it we could go back to using a web browser to upload files over HTTPS.

## Pentesting tools makes this easier
Using [Wireshark](https://www.wireshark.org/) and [Ncat](https://nmap.org/ncat/guide/index.html/) was how I first learnt to do this. It turns out pentesting tools like [mitmproxy](https://mitmproxy.org/) make this easier - it can [intercept](https://docs.mitmproxy.org/stable/mitmproxytutorial-interceptrequests/) and [modify](https://docs.mitmproxy.org/stable/mitmproxytutorial-modifyrequests/) the requests in-flight.

![MITM Proxy intercepting requests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wpkiwzhg5foah227fkqa.png)

[Burp Suite](https://portswigger.net/burp)'s [proxy](https://portswigger.net/burp/documentation/desktop/tools/proxy/getting-started) has similar features.

![BurpSuite intercepting requests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nrxw8vejuentelqr3o57.png)

## Prevent injection via filenames

There is just no way to guarantee a server never receiving an upload with a bad filename. The server must guard against it - usually by sanitising or encoding and escaping where it is used ([OWASP CE-4](https://owasp.org/www-project-proactive-controls/v3/en/c4-encode-escape-data.html)). Doing so will help to prevent injection attacks via filenames.