Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Figuring out the state of the art in CSRF protection, late-2022 edition #2

Open
simonw opened this issue Oct 9, 2022 · 11 comments
Open
Labels

Comments

@simonw
Copy link
Owner

simonw commented Oct 9, 2022

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:

  • Does SameSite=Lax make CSRF tokens obsolete for regular forms?
    • What if you are worried about a subdomain of your main domain ending up with XSS holes or having insecure code deployed to it?
  • Are there ways that JavaScript fetch() calls can interact with JSON APIs that mean you can skip CSRF tokens for those particular requests?
@simonw simonw added the research label Oct 9, 2022
@simonw
Copy link
Owner Author

simonw commented Oct 9, 2022

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)

@simonw
Copy link
Owner Author

simonw commented Oct 9, 2022

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.)

@simonw
Copy link
Owner Author

simonw commented Oct 9, 2022

Also relevant: https://simonwillison.net/2021/Aug/3/samesite/ when I explored SameSite cookies last year.

@simonw
Copy link
Owner Author

simonw commented Oct 9, 2022

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.

@simonw
Copy link
Owner Author

simonw commented Oct 9, 2022

https://twitter.com/samuel_colvin/status/1579215646415388672 says:

https://github.com/samuelcolvin/foxglove/blob/8cf9165526829a3ee23c43560947e0bd6a7c2432/foxglove/middleware.py#L246

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.

@simonw
Copy link
Owner Author

simonw commented Oct 9, 2022

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).

@simonw
Copy link
Owner Author

simonw commented Oct 9, 2022

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

@simonw
Copy link
Owner Author

simonw commented Oct 10, 2022

Some demos I'd like to see:

  • Setting __Host-x=y cookies on csrf.club and www.csrf.club and seeing if those are visible from attack.csrf.club
  • Same but for regular cookies without the __Host- prefix
  • A CSRF attack from attack.csrf.club against www.csrf.club that takes advantage of a SameSite=Lax cookie
  • An attempt at an attack like that but that uses an application/json POST to an API
  • A CSRF token protected form on www.csrf.club that cannot be attacked from attack.csrf.club due to the CSRF token cookie not being visible

@jspraul
Copy link

jspraul commented Oct 11, 2022

@simonw
Copy link
Owner Author

simonw commented Oct 14, 2022

https://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.

https://www.youtube.com/watch?v=tl6JCLAj5os

@simonw
Copy link
Owner Author

simonw commented Oct 14, 2022

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/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants