Security Tip: Bypassing CSRF Protection with File Uploads
[Tip#53] Accepting File Uploads from your users is always a risky proposal, but have you considered just how easily uploaded files can be used to bypass CSRF and cookie protections?
File Uploads are always risky because unlike other user input, you usually can’t simply validate the entire contents, or place firm restrictions on what is received, and store it securely in a database. Instead, file uploads need to go into a folder on the disk, and depending on what you’re doing with them, often need to be world-accessible.
It’s that last part that is the big problem: world-accessible.
Your mind probably immediately jumps to hackers uploading .php
files and trying to get RCE, and while that definitely the biggest risk you need to consider, you also need to remember .html
(and .js
files!), and where they are accessible from.
As a general rule, you should always limit the types of files you allow to be uploaded. Laravel’s uploaded file validator is incredibly robust, so definitely check that out and ensure you validate everything. But what if you need to accept a wide range of files, or even just .html
files specifically?
The risk is that if an attacker can upload an HTML file, which the browser executes as HTML, then they can run javascript in their victim’s browser. Depending on the scope the HTML file is running in, they may have full access to session cookies and CSRF tokens. This makes CSRF and XSS attacks trivial, and leaves your app completely exposed!
Let’s take a quick look…
Consider the scenario where your primary app is: securinglaravel.com
, and you accept file uploads which are available to view/download. These are your options for paths and the risks associated with each:
securinglaravel.com/downloads/upload.html
- ❌ Directly within scope of primary domain.
- ❌ Full access to authentication cookies.
- ❌ Full access to CSRF tokens.
- ❌
.php
files can be executed, giving RCE in your app. 😱
cdn.securinglaravel.com/upload.html
- ❌ Associated within scope of primary domain.
- ❌ Within “Same-Site” scope of primary domain, bypassing protections provided by
SameSite=Lax
andSameSite=Strict
. - ⚠️ CSRF tokens potentially exposed if subdomains are allowed within cookie scope.
- ⚠️
.php
files may be executed, giving full access to all uploaded files and potentially your entire application.
securinglaravel.some-cdn-provider.com/upload.html
- ✅ Independent and outside scope of primary domain.
- ✅ Cookies are blocked.
- ✅ CSRF tokens cannot be retrieved.
- ✅ Any decent CDN/block/file storage will prevent any server side files (i.e.
.php
) from executing.
Even if you’re not specifically allowing .html
files to be uploaded, I still highly recommend you serve uploaded files from a completely separate domain. This prevents any CSRF or Cookie bypasses, and if the service doesn’t even support executing PHP scipts, then you’re not at risk if someone does manage to get a PHP script up there.
It’s all about managing your risks.