CSRF checks fail with a standard EZproxy instance
TL;DR — with a standard config, EZproxy filters out headers with the names X-CSRF-TOKEN and X-XSRF-TOKEN. Any CSRF validation checks that require one of those headers to be present in a request will fail.
What’s going on
I wanted to write something about this because my googling for the terms CSRF and EZproxy returned nothing of any use, so maybe this will help someone.
I’ve been investigating a bug where users visiting a Laravel-based site via EZproxy were getting errors when trying to do various actions. It turned out they were getting 419 errors, which is Laravel’s non-standard response denoting a CSRF validation failure.
In this case, the caching issue was ruled out by the fact that these particular actions were working fine for users visiting the site directly but failing with a 419 for proxy users. My suspicion, after playing around with a few different ways of sending the tokens, was that they must be getting filtered out before reaching our server.
To try and confirm this, I deployed a small investigatory change to our dev server because I couldn’t access my own locally hosted version of the server via the proxy. First I excluded one of the affected endpoints from the CSRF checks, as described in laravel’s docs, and then I logged the values of the X-CSRF-TOKEN and X-XSRF-TOKEN headers within the controller method for the route. Then I accessed that endpoint both with and without a proxy.
The logging showed that those headers were populated when accessed directly but empty when accessed via the proxy.
So what can you do
EZproxy provides configuration options for how it deals with headers — described here — and those let you nominate specific headers to be passed through unaltered.
The problem is that if you’re the owner of the server being visited, you don’t have any control over how somebody visiting your site has configured their proxy, and as far as I can tell those headers are filtered out by the out-of-the-box EZproxy stanza.
So I guess you have three options:
- reach out to all your customers who use a proxy and ask them nicely to update their proxy configurations
- implement a workaround where you pass the CSRF token to the server a different way and rewrite or extend your framework’s CSRF validation middleware to handle it
- turn off CSRF validation
Whether option 3 is acceptable or not will depend on your project, but it’s certainly the easiest!
If anyone is aware of other options, or if anyone has a specific workaround for this issue, I’d love to hear them.
EDIT: I discuss my approach to option 2 — i.e. working around the problem without disabling security — in this article.