7 ms·
CSRF protection without tokens or hidden form fields
- owenthejumper 9mo agoRight now the problem is what the author already mentions - the use of Sec-Fetch-Site (FYI, HTTP headers are case insensitive :) - is considered defense in depth in OWASP right now, not a primary protection. Unfortunately OWASP rules the world. Not because it's the best way to protect your apps, but because the corporate overloads in infosec teams need to check the box with "Complies with OWASP Top 10"
- miguelgrinberg 9mo agoHi, author here. This was actually a mistake. If you look at the OWASP cheat sheet today you will see that Fetch Metadata is a top-level alternative to the traditional token-based protection. I'm not sure I understand why, but the cheat sheet page was modified twice. First it entered the page with a top-level mention. Then someone slipped a revision that downgraded it to defense in depth without anyone noticing. It has now been reverted back to the original version. Some details on what happened are in this other discussion from a couple of days ago: https://news.ycombinator.com/item?id=46347280 https://news.ycombinator.com/item?id=46347280.
- nchmy 9mo agoCan you share links to better guidance than OWASP?
- tptacek 9mo agoThe OWASP Top 10 is a list of vulnerabilities, not a checklist of things you have to actually "do".
- flomo 9mo agoCompletely agree. But fyi there is a bunch of dev training stuff around this, implying like "don't do an owasp or you're in trouble".
- ozim 9mo agoIf you look from perspective of vulnerability assessment, it kind of is.
- scott_w 9mo agoWhile you’re correct, corporate security teams demand suppliers “comply with OWASP,” despite this being a nonsensical statement to anyone who’d read the website. Unfortunately, the customer purchasing your product doesn’t know this and (naturally) trusts their own internal experts over you. Especially given all their other suppliers are more than happy to state they’re certified!
- tptacek 9mo agoI'm, uh, pretty familiar with the routine. I stand by what I said: you do not need any particular CSRF defense in place; you need to not have CSRF vulnerabilities. There's no OWASP checkbox-alike that requires you to have CSRF tokens, and plenty of real line-of-business apps at gigantic companies don't.
- scott_w 9mo agoTo be fair, though, you’re a lot more knowledgeable and experienced than some security “experts” I’ve had to deal with ;-)
- 8n4vidtmkvmk 9mo agoSince when are they case sensitive? https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/... says otherwise. It's possible for a server to treat them as case sensitive, but that seems like a bad idea.
- thatwasunusual 9mo ago>> FYI, HTTP headers are case insensitive > Since when are they case sensitive? [...]
- thomascountz 9mo agoPerhaps the OG comment was misread or confusion was caused by a typo and/or edit. When I originally read it hours ago, I also read it as "...HTTP headers are case sensitive," (emphasis mine). That said, there is one caveat regarding case sensitivity for headers encoded for HTTP/2.
- thomascountz 9mo ago+1 HTTP/2, headers are not unique if they only differ by casing, but they must be encoded as lowercase. Just as in HTTP/1.x, header field names are strings of ASCII characters that are compared in a case-insensitive fashion. However, header field names MUST be converted to lowercase prior to their encoding in HTTP/2. A request or response containing uppercase header field names MUST be treated as malformed (Section 8.1.2.6).[1] HTTP/1.X, headers are insensitive to casing for reasons of comparison and encoding. Each header field consists of a name followed by a colon (":") and the field value. Field names are case-insensitive.[2] So, if Sec-Fetch-Site is sensitive at all, it would be sec-fetch-site when sending via HTTP/2 and you're responsive for encoding/decoding. [1]: https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2 https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2 [2]: https://datatracker.ietf.org/doc/html/rfc2616#section-4.2 https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
- jonway 9mo agoMy primitive instincts lead me to believe that sometimes they end up being Case-Sensitive and Sometimes NoT! (depending on implementation)
- deleted 9mo ago[deleted]
- tmsbrg 9mo agoI'm surprised there's no mention of the SameSite cookie attribute, I'd consider that to be the modern CSRF protection and it's easy, just a cookie flag: https://scotthelme.co.uk/csrf-is-dead/ https://scotthelme.co.uk/csrf-is-dead/ But I didn't know about the Sec-Fetch-Site header, good to know.
- miguelgrinberg 9mo agoThe OWASP CSRF prevention cheat sheet page does mention SameSite cookies, but they consider it defense in depth: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#samesite-cookie-attribute https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Re....
- tptacek 9mo agoBecause of clientside Javascript CSRF, which is not a common condition.
- nchmy 9mo agoClient side js is not particularly relevant to csrf.
- tptacek 9mo agoI mostly agree, but that's the logic OWASP uses to argue you should still be doing explicit tokens even if you're using SameSite and Sec-Fetch.
- nchmy 9mo agoBut that's not what owasp argues. Fetch Metadata is recommended as a primary, standalone defense against CSRF (you can be forgiven for not knowing this - I worked on getting the doc updated and it landed a couple weeks ago, then was reverted erroneously, and fixed yesterday)
- 9mo ago
- shermantanktop 9mo agoAm I missing something? The suggested protection helps with XSS flavors of CSRF but not crafted payloads that come from scripts which have freedom to fake all headers. At that point you also need an oauth/jwt type cookie passed over a private channel (TLS) to trust the input. Which is true for any sane web app, but still…
- varenc 9mo agoIf an attacker has a user's private authentication token, usually stored in a __Host prefixed cookie, then it's game over anyway. CSRF is about protecting other sites forcing a user to make a request to a site they're authenticated to, when the malicious site doesn't actually have the cookie/token. CSRF is when you don't have the authentication token, but can force a user to make a request of your choosing that includes it. In this context you're using HTML/JS and are limited by the browser in terms of what headers you can control. The classic CSRF attack is just a <form> on a random site that posts to "victim.com/some_action". If we were to re-write browser standards today, cross-domain POST requests probably just wouldn't be permitted.
- naasking 9mo ago> If we were to re-write browser standards today, cross-domain POST requests probably just wouldn't be permitted. That would be a terrible idea IMO. The insecurity was fundamentally introduced by cookies, which were always a hack. Those should be omitted, and then authorization methods should be designed to learn the lessons from the 70s and 80s, as CSRF is just the latest incarnation of the Confused Deputy: https://en.wikipedia.org/wiki/Confused_deputy_problem https://en.wikipedia.org/wiki/Confused_deputy_problem
- varenc 9mo agoAh, so true. That's what i mean! Cross domain requests that pass along the target domain's cookies. As in, probably every cookie would default to current __Host-* behavior. (and then some other way to allow a cookie if you want. Also some way of expressing desired cookie behavior without a silly prefix on its name...)
- rvnx 9mo agoIf you want, “SameSite=Strict” may also be helpful and is supported on “all” browsers so it is reasonable to use it (but like you did, adding server validation is always a +). https://caniuse.com/mdn-http_headers_set-cookie_samesite_strict https://caniuse.com/mdn-http_headers_set-cookie_samesite_str... This checks Scheme, Port and Origin to decide whether the request should be allowed or not.
- simonw 9mo agoI find that cookie setting really confusing. It means that cookies will only be respected on requests that originated on the site that set them... but that includes when you click links from one site to another. So if you follow a link (e.g. from a Google search) to a site that uses SameSite=Strict cookies you will be treated as logged out on the first page that you see! You won't see your logged in state until you refresh that page. I guess maybe it's for sites that are so SPA-pilled that even the login state isn't displayed until a fetch() request has fired somewhere?
- ctidd 9mo agoYou want lax for the intuitive behavior on navigation requests from other origins. Because there’s no assumption navigation get requests are safe, strict is available as the assumption-free secure option.
- macNchz 9mo agoSameSite=Strict is belt-and-suspenders protection in the case where you could have GET requests that have some kind of impact on state, and the extra safety is worth the UX impact (like with an online banking portal). Discussions about this often wind up with a lot of people saying "GET requests aren't supposed to change state!!!", which is true, but just because they're not supposed to doesn't mean there aren't some floating around in large applications, or that there aren't clever ways to abuse seemingly innocuous side effects from otherwise-stateless GET requests (maybe just visiting /posts/1337/?shared_by_user=12345 exposes some tiny detail about your account to user #12345, who can then use that as part of a multi-step attack). Setting the strict flag just closes the door on all of those possibilities in one go.
- altmind 9mo agoAre there any approaches to csrf tokens that don't require storing issued tokens on server-side?
- t-writescode 9mo agoMost of them. You can send in a cookie and a field and compare. CSRF is about arbitrary clicks in emails and such that automagic your logged-in-session cookies to the server. If you require an extra field and compare it, you’re fine
- maxbond 9mo agoThe alternative to storing tokens is to use an AEAD encryption scheme like AES-GCM to protect tokens from forgery or tampering. You will still have to worry about reuse, so you will probably want to restrict use of this token to the user it was generated for and to a lifetime (say, 24 hours). That is a very high level description, there are details (like nonce generation) that must be done correctly for the system to be secure.
- deleted 9mo ago[deleted]
- deleted 9mo ago[deleted]
- est 9mo agoreminds me of something similar https://news.ycombinator.com/item?id=46321651 https://news.ycombinator.com/item?id=46321651 e.g. serve .svg only when "Sec-Fetch-Dest: image" header is present. This will stop scripts
- amluto 9mo agoOr sending Content-Security-Policy: script-src 'none' for everything that isn’t intended to be a document. Or both. IMO it’s too bad that suborigins never landed. It would be nice if Discord’s mintlify route could set something like Suborigin: mintlify, thus limiting the blast radius to the mintlify section.
- est 9mo agomaybe adding a dedicated cookie for that specific path?
- amluto 9mo agoHTTP-only cookies ought to work fine for this. I imagine there’s a fair amount of complexity that would need to be worked out, mostly because the browser doesn’t know the suborigin at the time it makes a request. So Sec-Fetch-Site and all the usual CORS logic would not be able to respect suborigins unless there was a pre-flight check for the browser to learn the suborigin. But this doesn’t seem insurmountable: a server using suborigins would know that request headers are sent as if the request were aimed at the primary origin, and there could be some CORS extensions to handle the case where the originating document has a suborigin.
- louiskottmann 9mo agoThis is a massive change for cache in webapp templates as it makes their rendering more stable and thus more cacheable. A key component here is that we are trusting the user's browser to not be tampered with, as it is the browser that sets the Sec-Fetch-Site header and guarantees it has not been tampered with. I wonder if that's a new thing ? Do we already rely on browsers being correct in their implementation for something equally fundamental ?
- tptacek 9mo agoThe entire web security model assumes we can trust browsers to implement web security policies!
- louiskottmann 9mo agoI appreciate that, but in the case of TLS or CSRF tokens the server is not blindly trusting the browser in the way Sec-Fetch-Site makes it.
- tptacek 9mo agoSure it is. The same-origin rule that holds the whole web security model together is entirely a property of browser behavior.
- louiskottmann 9mo agoThat's indeed a good example of prior full trusting of the browser by the server.
- nchmy 9mo agoIt's a shame you talked about browser tampering, since better caching is indeed a benefit of fetch metadata headers.
- vasco 9mo ago> One option is to reject all requests that do not have the Sec-Fetch-Site header. This keeps everyone secure, but of course, there's going to be some unhappy users of old devices that will not be able to use your application. Plus, this would also reject HTTP clients that are not browsers. If this is not a problem for your use case, then great, but it isn't a good solution overall. If my client is not a browser surely I can set whatever headers I want? Including setting it to same-origin?
- nchmy 9mo agoSec fetch has 98% browser coverage now. You can fall back to origin, which has 100% coverage. Non-browser clients can be either blocked or even just given a pass, since CSRF is about tricking someone into clicking a link that then sends their Auth cookie along with the request. Either the non-browser request includes a valid cookie in the request and is allowed to mutate state, or it doesn't and nothing happens as the request doesn't get authenticated.
- rfmoz 9mo agoAdding more security headers every year feels like strapping seatbelts onto a collapsing roller coaster. It would be better to stop this "sec headers stack" in favour of simpler, secure by default browser primitives with explicit opt-out. Getting an example from https://securityheaders.com https://securityheaders.com the list nowadays is as follows: - Strict-Transport-Security - Content-Security-Policy - X-Frame-Options - X-Content-Type-Options - Referrer-Policy - Permissions-Policy - Cross-Origin-Embedder-Policy - Cross-Origin-Opener-Policy - Cross-Origin-Resource-Policy
- thaumasiotes 9mo agoYeah, redoing the defaults would probably be good. On the other hand, I tried doing a Google search with javascript disabled today, and I learned that Google doesn't even allow this. (I also thought "maybe that's just something they try to pawn off on mobile browsers", but no, it's not allowed on desktop either.) So the state of things for "how should web browsers work?" seems to be getting worse, not better.
- PhilipRoman 9mo agoWow, I used to be able to search google even from terminal browsers like 'elinks'
- rhdunn 9mo agoI used elinks once to find a solution to an issue where the login screen was broken after an upgrade. I was able to switch to a virtual console, find out about the issue, identify the commands to fix the issue, and use them to resolve the issue.
- paffdragon 9mo agoI think it still works if you set your user agent to something like lynx. I had a custom UA set for Google search in Firefox just for this purpose and to disable AI overviews.
- 9mo ago
- NorwegianDude 9mo agoThe simplest way to prevent CSRF is to use the Referer header, and that has been used since forever. If the header is missing, you no-op the post. Origin is similar, and can be used with referer as fallback, but it's not needed for most sites.
- nchmy 9mo agoFetch Metadata headers, as discussed in this post, are just as simple and much more effective. There's lots of issues with referer, and even some with origin.
- talkin 9mo agoNO. Please don’t spread wrong solutions. Your attempt has similarities to the idea behind Checking Sec-Fetch-Site. Implementing that header is the same amount of work. But this header is exactly meant for this purpose, and referer is haunted with problems. So for officially intended protections, implementing this header and samesite cookies gets you a very long way without any complexity, assumptions, or tricks of old lore.
- NorwegianDude 9mo agoIt's not a wrong solution. It's been commonly used since forever, tens of years before the sec-fetch-site header existed, and it stops CSRF. Sec-fetch-site is not supported in old browsers, so relying on that is unsafe without any fallbacks.
- justarandomname 9mo agoI worked on an legacy application that did this as a stop-gap as CSRF tokens were being implemented and it just kept both approaches.
- magmostafa 9mo agoThis approach using Sec-Fetch-* headers is elegant, but it's worth noting the browser support considerations. According to caniuse, Sec-Fetch-Site has ~95% global coverage (missing Safari < 15.4 and older browsers). For production systems, a layered defense works best: use Sec-Fetch-Site as primary protection for modern browsers, with SameSite cookies as fallback, and traditional CSRF tokens for legacy clients. This way you get the UX benefits of tokenless CSRF for most users while maintaining security across the board. The OWASP CSRF cheat sheet now recommends this defense-in-depth approach. It's especially valuable for APIs where token management adds significant complexity to client implementations.
- mxey 9mo agoWithout those headers, you can as a fallback compare the Origin header to the Host header. See https://words.filippo.io/csrf/ https://words.filippo.io/csrf/
- yread 9mo ago> UX benefits of tokenless CSRF What are those?
- nchmy 9mo ago98% coverage if you exclude browsers that caniuse doesn't track (which is surely appropriate, since even things like checkbox elements have only 96% coverage if you include un tracked browsers). And you can fall back to origin header, which has universal coverage. Then block anything else. Also, owasp doesn't recommend it as defense in depth. It is a primary, standalone defense against CSRF. https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#fetch-metadata-headers https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Re...
- 6510 9mo agoIf i open a link with a new target, say "foo" then post a form to the same "foo" target. What would be the origin?
- dorianmariecom 9mo agorails does this in 8.2
- nchmy 9mo ago*will do I just went looking for docs and it seems that 8.2 is not out yet https://github.com/rails/rails/pull/56350/ https://github.com/rails/rails/pull/56350/
- foobarkey 9mo agoI put the session cookie as http_only, same_site=strict and turned off csrf. Then pentesters came and quoted owasp in the report, while not being able to demonstrate an attack. Some drone added csrf back, everyone congratulated themselves in making things more secure :)
- deepsun 9mo ago> this would also reject HTTP clients that are not browsers Why? I can send any headers from a client I make.