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?

Security Tip: Bypassing CSRF Protection with File Uploads

Greetings friends! I hope you enjoyed last week’s "Th1nk Lik3 a H4cker" Walkthrough (part 1) - I had fun diving into the details, and the second part of the walkthrough will be out in 2 weeks, as our next In Depth article. I am also excited to let you know that the recording from Laracon US is now available!

This week I stumbled upon a writeup about a website hack1 where the hacker used weak file upload protections to bypass CSRF and Cookie protections. It reminded me of a vulnerability I found a few months ago during an audit where I did the same thing, and I realised it’s perfect for us to cover today.2

⚠️ Laravel Security Audits and Penetration Tests → I’m now fully booked until November, and have limited spots left until February. Reach out now to book in an audit! 🕵️

Bypassing CSRF Protection with File Uploads

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 everything3. 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: ``, and you accept file uploads which are available to view/download. These are your options for paths and the risks associated with each:

  • ``

    • ❌ 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. 😱

  • ``

    • ❌ Associated within scope of primary domain.

    • ❌ Within “Same-Site” scope of primary domain, bypassing protections provided by `SameSite=Lax` and `SameSite=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.

  • ``

    • ✅ 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 scipts4, then you’re not at risk if someone does manage to get a PHP script up there.

It’s all about managing your risks.

Looking to learn more?
Security Tip #34: Encrypting Environment Files?
▶️ In Depth #12: "Password Generator" Security Audit

  1. If you’re interested, you can find the article at:

  2. It’s also very similar to one of the challenges in my Practical Laravel Security course.

  3. It is User input after all, and you always need to validate user input.

  4. Or other server-side scripts