CORS on Firefox
Published on: 2024-04-14
CORS is one of those things that I think are needed, important, and annoying at the same time. I am happy it exists, because it protects against a certain class of attacks where a website can "pretend" to be you and request third-party resources in your name. But it's also a bit annoying, specially when things that should work, don't. This blog post is short, but if it helps a single person on planet earth, then it has done its deed.
Imagine a web application which consists of many different endpoints (as most do). You secured one of them with TLS client auth - also called mTLS.
Now, another web application on a different subdomain, requests (via GET) this resource on the client (browser) side. You anticipate this, and enable CORS headers on the protected endpoint.
add_header 'Access-Control-Allow-Origin' 'https://$URL' always;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
Sweet, that should do it, right? Trembling with anticipation, you refresh the page of the web application which requests this URL, only for Firefox to greet you with:
HTTP/1.1 403 Forbidden
Server: nginx
Date: Sun, 14 Apr 2024 18:02:33 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 146
Connection: keep-alive
No CORS headers, and a 403. What's up with that? Turns out (after a while of debugging, as usual) that the CORS "pre-flight" check failed. This is an OPTIONS request toward the URL in question, in which the browser checks for the headers above. The problem is that this request doesn't support mTLS authentication! As an added bonus, there's a ten year old Firefox bug about this behavior. To be fair, this seems to be an oversight in the CORS spec itself, and not in Firefox.
Anyhow, you can fix all of this by setting an about:config property network.cors_preflight.allow_client_cert
to true
.
After this, your CORS requests will use mTLS authenticated pre-flight requests to obtain the Access-Control-Allow-* headers, and Bob's your uncle.