- Adding 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 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
- This 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.
- Right 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"
- I'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/
But I didn't know about the Sec-Fetch-Site header, good to know.
- If 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_str...
This checks Scheme, Port and Origin to decide whether the request should be allowed or not.
- The 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.
- reminds me of something similar
https://news.ycombinator.com/item?id=46321651
e.g. serve .svg only when "Sec-Fetch-Dest: image" header is present. This will stop scripts
- Am 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…
- This 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 ?
- Are there any approaches to csrf tokens that don't require storing issued tokens on server-side?
- > 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?
