Why make an extremely complicated set up, with many edge cases, all to save yourself from a single token?
- Privacy extensions often times block referrer headers.
- POST requests are usually necessary.
- Open redirects are common bugs, and getting your website to initiate one can be a problem.
- Disabling CORS also relies on you killing crossdomain.xml, which you might overlook.
Instead you can just roll out CSRF tokens.
Having rolled them out myself many times. You probably want to use a library if you aren't a cryptographer or security person:
I've seen other people mention SameSite cookies, but we aren't near a time when browsers all support them. Don't get fancy preventing CSRF. It's a stupid bug.
Not to be That Guy, but: your sample will reuse the same token over and over until it expires, which is how you get the BREACH attack.
In Django 1.10, we switched the CSRF token generation to use a consistent base value, but to combine it in a reversible way with a randomly-generated per-request nonce. CSRF verification then consists of recovering the base value and checking it; this lets you have a longer-lived "token" without sending the same value in every request/response cycle.
Agreed. The solution being proposed with CORS is fragile and complex. Maybe it will be viable in another 5 years' time, with better browser support. But I can't imagine that after reading the article, any dev in his/her right mind will be rushing off to ditch CSRF tokens on production sites.
CSRF tokens are taken care of by every web framework out there, anyway. They really should involve zero work to implement. For example, Flask-WTF handles them virtually transparently.
I believe the point of the article is that tokens don't get sent for HEAD or OPTIONS requests, and it's very possible that your API/server are still performing some sort of action on such requests. Or even if not performing a specific action, it still needs to be dealt with and hence is a DDOS vector.
In addition to everything you've mentioned, the double submit cookie approach for csrf defense saves you from storing any state if you don't want to use a backend session.
I used to prefer the origin/referer approach to blocking CSRF because it can be done upstream of the application server (or in a middleware), transparent to developers who often get these things wrong.
However there's been enough referer spoofing browser bugs lately that I'd rather have the extra safety (and complexity) of CSRF tokens. Just 3 months ago Edge had (another) referer spoofing bug:
https://www.brokenbrowser.com/referer-spoofing-patch-bypass/
There are plenty of middleware-like solutions that can add solutions automatically too. And if you fail closed, you will always notice in testing and can add tokens where they are missing.
I can't shed the feeling that CORS is just piling more crap onto crap; this seems just way too complex. On one hand, I'm somewhat glad that I don't work on web things and don't have to deal with it, and on the other hand I'm scared of the many web developers are not (sufficiently) aware of the range of possible vulnerabilities and thus not protecting them.
They tried to propose a simple modern solution but after reading the article it does not look simple and works much worse than traditional approach with tokens.
And of course it would be better if browsers would not make cross-origin requests unless permitted by server.
Hi @codedokode, I'm one of the authors of the post. There's a lot of background material at the top but if you skip to the use of our new module (direct link: https://github.com/mixmaxhq/cors-gate/#usage) I think you'll find it simpler in both code and infrastructure than a typical CSRF setup. https://github.com/expressjs/csurf, for instance, requires you to lock down every API both server-side and client-side, and by default requires session middleware; whereas with cors-gate you can register it once, server-side, before any API routes.
I guess the modern part here is the use of the Origin header, but in all honestly this feels really flaky in comparison to using a CSRF token. I'd argue that SameSite cookies are a better 'modern' alternative than this (for a majority of use-cases), especially because of all the edge cases that have to be dealt with for this approach (Origin not being sent, falling back to Referers, policies for those...). It feels like a giant hack.
Also, I really don't get the relevance of the refutation of "Use only JSON APIs" - it's a fixed bug that seemingly only impacted Chrome anyway? The second point is that preflights are expensive, which might have some weight - but e.g. Twitch seem to cope okay with these preflights and I think a lot of browsers do cache them for a short period of time now anyway.
I'll be sticking to CSRF tokens plus SameSite cookies (for where the browser supports them). The only issue I can see with SameSite is that JS can still send requests (with no credentials), but I'm not convinced this is a credible DoS vector.
I do this in one of our business' apps. It does strict checking of the Origin and Referer headers when it receives a POST request. We only support modern browsers, and doing this instead of using CSRF tokens feels modern and clean.
I'm paranoid though, so at the last minute, before shipping the first public version of the app, I added a traditional CSRF token check in addition to the Origin/Referer check. I guess it's a layered defense?
You can trust the browser itself (not application code, but the native code) to issue the appropriate headers. If you could find a way of compromising that, it would be a vulnerability wayyy beyond the scope of our protection.
(Caveat: browsers may not implement the specs completely/bug-free yet, as we cover in our post. But we fully expect they will, and in the meantime our module supports fallbacks. This approach is "skating to where the puck will be".)
Non-browser clients can spoof these headers, but the risk then is DOS, not clients leveraging the user's credentials—which is the primary focus of CSRF protection. It's nice that our method can prevent browser-based DOS attacks, but that's by no means complete DOS protection.
Then that user can only 'hack themselves'. CSRF is a security vulnerability is because you can do a HTTP request on behalf of someone else, on someone else's browser, in someone else's session/security context.
If I were to drop CSRF tokens (which work reliably) I would go directly to Origin verification. No Referer. Something in the middle is worst of both worlds.
I'm curious about your comment. We (attempted) to address cases where we needed to infer the Origin from the Referer due to incomplete browser support. What about using both makes this necessarily worse, when the use of the Referer is really only a temporary bandage for said incomplete support?
[+] [-] ejcx|8 years ago|reply
Having rolled them out myself many times. You probably want to use a library if you aren't a cryptographer or security person:
I've seen other people mention SameSite cookies, but we aren't near a time when browsers all support them. Don't get fancy preventing CSRF. It's a stupid bug.[+] [-] ubernostrum|8 years ago|reply
In Django 1.10, we switched the CSRF token generation to use a consistent base value, but to combine it in a reversible way with a randomly-generated per-request nonce. CSRF verification then consists of recovering the base value and checking it; this lets you have a longer-lived "token" without sending the same value in every request/response cycle.
[+] [-] jaza|8 years ago|reply
CSRF tokens are taken care of by every web framework out there, anyway. They really should involve zero work to implement. For example, Flask-WTF handles them virtually transparently.
[+] [-] jordanlev|8 years ago|reply
[+] [-] jadacyrus|8 years ago|reply
[+] [-] arkadiyt|8 years ago|reply
However there's been enough referer spoofing browser bugs lately that I'd rather have the extra safety (and complexity) of CSRF tokens. Just 3 months ago Edge had (another) referer spoofing bug: https://www.brokenbrowser.com/referer-spoofing-patch-bypass/
[+] [-] Eridrus|8 years ago|reply
There are plenty of middleware-like solutions that can add solutions automatically too. And if you fail closed, you will always notice in testing and can add tokens where they are missing.
[+] [-] unknown|8 years ago|reply
[deleted]
[+] [-] minus7|8 years ago|reply
[+] [-] codedokode|8 years ago|reply
And of course it would be better if browsers would not make cross-origin requests unless permitted by server.
[+] [-] wearhere|8 years ago|reply
[+] [-] lol768|8 years ago|reply
Also, I really don't get the relevance of the refutation of "Use only JSON APIs" - it's a fixed bug that seemingly only impacted Chrome anyway? The second point is that preflights are expensive, which might have some weight - but e.g. Twitch seem to cope okay with these preflights and I think a lot of browsers do cache them for a short period of time now anyway.
I'll be sticking to CSRF tokens plus SameSite cookies (for where the browser supports them). The only issue I can see with SameSite is that JS can still send requests (with no credentials), but I'm not convinced this is a credible DoS vector.
[+] [-] rossy|8 years ago|reply
I'm paranoid though, so at the last minute, before shipping the first public version of the app, I added a traditional CSRF token check in addition to the Origin/Referer check. I guess it's a layered defense?
[+] [-] alexchamberlain|8 years ago|reply
[+] [-] wearhere|8 years ago|reply
(Caveat: browsers may not implement the specs completely/bug-free yet, as we cover in our post. But we fully expect they will, and in the meantime our module supports fallbacks. This approach is "skating to where the puck will be".)
Non-browser clients can spoof these headers, but the risk then is DOS, not clients leveraging the user's credentials—which is the primary focus of CSRF protection. It's nice that our method can prevent browser-based DOS attacks, but that's by no means complete DOS protection.
[+] [-] bhhaskin|8 years ago|reply
[+] [-] alexlongterm|8 years ago|reply
[+] [-] hamandcheese|8 years ago|reply
Am I the only one very surprised to hear this? In 2017?
Is there any reason browsers shouldn't just send origin headers along with all requests? Why the exceptions?
[+] [-] skeggse|8 years ago|reply
[+] [-] Retr0spectrum|8 years ago|reply
[+] [-] nevir|8 years ago|reply
TL;DR - evergreen browsers, mobile browsers (except for images), IE 11+, and IE 8+ partially supports it
[+] [-] ziwikiwi|8 years ago|reply
[+] [-] makkesk8|8 years ago|reply
[+] [-] treve|8 years ago|reply
[+] [-] mrmagooey|8 years ago|reply
[+] [-] vmasto|8 years ago|reply
[+] [-] homakov|8 years ago|reply
[+] [-] skeggse|8 years ago|reply
[+] [-] bradavogel|8 years ago|reply
[+] [-] janwillemb|8 years ago|reply