Open simonw opened 2 years ago
Started a Twitter conversation here:
https://twitter.com/simonw/status/1578953514973134848
Do I still need to check CSRF tokens for POST requests where the client has specified Content-Type: application/json ?
Internet search results seem slightly uncertain on this issue (and are mostly dated 5-10 years ago)
(I'm still one of those paranoid types that doesn't 100% trust SameSite=Lax cookies to protect against CSRF because of the risk that someone will CNAME
helpdesk.mydomain.com
over to a third-party vendor who end up with an XSS hole of their own)Maybe I should trust the incoming Origin: header instead as a sign that CSRF checks can be skipped? https://twitter.com/jaffathecake/status/1238080408216047617
Urgh, lots of complex notes about Origin: here: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#verifying-origin-with-standard-headers
What does this mean? "Internet Explorer 11 does not add the Origin header on a CORS request across sites of a trusted zone."
OWASP do seem to be in favour of custom request headers as a way to skip CSRF checks for fetch() calls though: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#use-of-custom-request-headers
I remember there used to be CSRF attacks that took advantage of features in Flash that could make cross-domain cookied requests that set custom HTTP request headers
Presumably the chance someone has a vulnerable version of the Flash player installed is effectively zero now?
Like did Adobe consider that a bug in Flash and roll out a fix for that 15 years ago?
Another question: what's the absolute cutting edge state of the art in CSRF protection these days?
Any new web frameworks that are doing something thoroughly robust based on modern browser characteristics?
Is Django still the gold standard here?
(My hunch is that modern frameworks might just be leaving this entirely up to SameSite cookies, which I'm not sold on as a 100% solid approach)
I'm specifically looking for a solution which protects against the insecure subdomain scenario - the scenario where the code I want to protect runs on mysite.com
, but it's possible someone may CNAME support.mysite.com
off to a vendor who themselves have a XSS hole in their code.
So I want malicious code on support.mysite.com
to be unable to perform successful CSRF attacks against mysite.com
.
(Or maybe just against www.mysite.com
if it turns out protecting the www.
variant is easier than protecting the naked root domain.)
Also relevant: https://simonwillison.net/2021/Aug/3/samesite/ when I explored SameSite cookies last year.
https://twitter.com/MaltheJorgensen/status/1579229773498580992 says:
I decided 5 years ago that: yes, when the Content-Type is application/json then it is safe from CSRF.
The landscape is definitely muddy. It seems that until Chrome 59 (June 2017) you could use navigator.sendBeacon to POST JSON cross-origin: https://stackoverflow.com/questions/11008469/are-json-web-services-vulnerable-to-csrf-attacks?noredirect=1&lq=1#comment77025657_11024387
I feel like the Flask or Werkzeug docs used to contain some nice thoughts on this subject, but I can't find it -- even looking at fairly old versions, so that's probably just a false memory on my part.
Given that old
navigator.sendBeacon()
vuln. I'm wondering if there are newer similar browser holes, since the browsers surface is generally being expanded quite fast.Closing thoughts: I like your idea of adding a custom header from the client side. Even if a browser bug allows Content-Type: application/json cross-origin, having a custom header on top narrows the exposure.
https://twitter.com/samuel_colvin/status/1579215646415388672 says:
Is what I use, has passed multiple pen testing inspections (for what that's worth), and hasn't caused any problems for users in a long time.
Would love your opinion.
I replied:
Since it uses SameSite cookies, I wonder if it's vulnerable to CSRF attacks from an XSS attack against a subdomain of the primary domain it's hosted on?
That's the one very obscure edge case I'm most worried about here
The origin and referrer header checks might protect against that?
Samuel said:
That's the idea.
Bought a new domain, csrf.club
, to host some demos to help explore this more (like I did in https://simonwillison.net/2021/Aug/3/samesite/ for SameSite cookies).
Great comment here: https://twitter.com/mountain_ghosts/status/1579244599360425986
your app accepting JSON doesn't prevent a page on another origin sending you an un-pre-flighted POST request, to which the browser might attach cookies
some frameworks abstract over different request body encodings so your app might accept normal form data without you realising
I tend to default to whatever's safest in general. yes you don't need CSRF tokens if you have nothing else hosted on the same site, and if your app definitely rejects content-types that don't require CORS permissions
but these assumptions are easy to forget and break
like it's very easy years down the line for someone to stick a third party app under your domain and not realise it means they need to change how your session and other security stuff works
Some demos I'd like to see:
__Host-x=y
cookies on csrf.club
and www.csrf.club
and seeing if those are visible from attack.csrf.club
__Host-
prefixattack.csrf.club
against www.csrf.club
that takes advantage of a SameSite=Lax
cookieapplication/json
POST to an APIwww.csrf.club
that cannot be attacked from attack.csrf.club
due to the CSRF token cookie not being visiblehttps://twitter.com/jub0bs/status/1580905051840602113
The __Host- cookie name prefix is very useful if you rely on the so-called "double submit cookie" defence against CSRF. Otherwise, a malicious subdomain could create a homonymous cookie scoped at a common parent domain but with an arbitrary value...
... which would defeat the "double submit cookie" defence.
This presentation is amazingly useful: https://speakerdeck.com/filedescriptor/the-cookie-monster-in-your-browsers
It references this post by GitHub where they talk about why they moved GitHub pages content to *.github.io
: https://github.blog/2013-04-09-yummy-cookies-across-domains/
Started thinking about this here:
Short version: I want to write some JavaScript that does a POST to a JSON API endpoint, and I'd like to not have to bother with extracting a CSRF token from a cookie and sending it with that POST.
And more generally, I'd like to update my mental model of CSRF protection for what works best circa 2022.
Fundamental questions to answer:
SameSite=Lax
make CSRF tokens obsolete for regular forms?fetch()
calls can interact with JSON APIs that mean you can skip CSRF tokens for those particular requests?