8 ms·
Securing your API: a modern alternative to CSRF tokens
- codedokode 9y agoThey 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.
- wearhere 9y agoHi @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 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 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.
- lol768 9y agoI 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.
- arkadiyt 9y agoI 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/ https://www.brokenbrowser.com/referer-spoofing-patch-bypass/
- Eridrus 9y agoAnd another one on slide 33: https://speakerdeck.com/filedescriptor/exploiting-the-unexploitable-with-lesser-known-browser-tricks https://speakerdeck.com/filedescriptor/exploiting-the-unexpl... 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.
- wearhere 9y agoInteresting. But in both these cases (yours and @arkadiyt's) these vulnerabilities only affect GET requests right? In which case—though we would love to lock down GET requests, to prevent DOS attacks, and because GET routes _might_ in some cases modify state—the impact is pretty limited. I (one of the co-authors of the post) would also characterize our approach as "skating to where the puck will be". I'm sure that browsers will patch these bugs, the Edge one was fixed quickly. Our product only nominally supports the latest - 1 versions of Chrome and Safari. This is of course a luxury not available to all developers.
- Eridrus 9y agoAlmost no one ever gets hacked through web app bugs at all, let alone CSRF, so in the end this doesn't really matter. I still wouldn't recommend this as a solution though, since it's been broken repeatedly.
- 9y ago
- bradavogel 9y agoThoughtful article!
- janwillemb 9y agoThus said the "Co-founder & CTO @Mixmax" ;)
- bradavogel 9y ago:-D
- minus7 9y agoI 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.
- ejcx 9y agoWhy 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: expiration_time_of_1_day || hmac_sha256( secret_key, { expiration_time_of_1_day, user_id }) 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.
- jordanlev 9y agoI 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.
- ubernostrum 9y agoNot 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.
- makkesk8 9y agoWhat stops a user from writing a simple HTTP client to spoof the header?
- treve 9y agoThen 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.
- alexchamberlain 9y agoI don't really like the word secure next to CORS; you're relying on the browser to be secure... which it is not.
- bhhaskin 9y agoYou are totally correct. CORS also falls apart if the client isn't a browser.
- boucher 9y agoBut, CSRF is inherently a browser vulnerability.
- tcheard 9y agoYes, but then you get people that fail to understand that CSRF only applies to a browser, and CORS only affects a user agent that implements a Same Origin Policy. And they fail to protect their resources otherwise. They add CORS support to their server resources and get the false assumption that their resources can only ever be retrieved from a web page under their origin or an origin they have trusted (via CORS). They believe that this also protects them from malicious users making requests from outside of a browser (eg. via curl). CORS is not security, it is loosening of security (namely SOP). The number of times that I have had someone ask "why can someone access the endpoint via curl? I thought this was protected by CORS" makes me sad.
- X-Istence 9y agoCSRF doesn't save you here... I can send up a CSRF token using curl just as easily...
- tcheard 9y agoI wasn't saying a CSRF token saves you here. All I'm saying is that security is a complicated topic. People already misunderstand things like CSRF, CORS, resource protection, etc. And they already get them wrong. CORS is designed to loosen the security protections added by a same origin policy. It is not designed to increase security. Piling on CSRF protections is just blurring the use case for CORS. All this is going to do is confuse people more, and more people are going to get it wrong.
- homakov 9y agoIf 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.
- skeggse 9y agoI'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?
- homakov 9y agoWorst because it is neither proven as tokens which are used for decades already nor as convenient as simply whitelisted Origin and you even offer an extra dependency. Are those scenarios without Origin important? Like ff for form request - you can use xhr there and drop referer support entirely.
- skeggse 9y agoYou're right, support for cross-origin form POSTs isn't important for us. What is important, however, is same-origin requests, and Firefox doesn't send the Origin header with those. For us, the Referer serves its most important role by indicating where those requests originate from. EDIT: when Firefox _does_ start sending the Origin header, we may drop the Referer - we'll see.
- homakov 9y agoJust checked, indeed no origin on same site. It sucks, this makes origin barely reliable.
- Retr0spectrum 9y agoThe author admits that this solution won't work for "older" browsers. Which browser versions specifically?
- nevir 9y agohttps://caniuse.com/#feat=cors https://caniuse.com/#feat=cors and hit "show all" TL;DR - evergreen browsers, mobile browsers (except for images), IE 11+, and IE 8+ partially supports it
- skeggse 9y agoFor our specific solution, we also rely on (partial) support for Referrer-Policy: https://caniuse.com/#feat=referrer-policy https://caniuse.com/#feat=referrer-policy
- rossy 9y agoI 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?
- hamandcheese 9y ago> Firefox will not send the Origin header with any same-origin requests (bug) 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 9y agoWell, it's a bug. It absolutely should send the origin header, but it doesn't ¯\_(ツ)_/¯
- hamandcheese 9y agoI see that it's unclear, but my second set of questions was related to how chrome and safari don't send it for requests caused by img tags.
- ziwikiwi 9y agoWhat a well-written, insightful article! You have outdone yourself good sir
- mrmagooey 9y agoIsn't JWT a modern alternative to CSRF tokens?
- alexlongterm 9y agoOn the same topic of CSRF prevention, we also recently wrote about the dangers of using Content Types for security -- https://medium.com/@longtermsec/chrome-just-hardened-the-navigator-beacon-api-against-cross-site-request-forgery-csrf-690239ccccf https://medium.com/@longtermsec/chrome-just-hardened-the-nav...