25 comments

[ 3.7 ms ] story [ 54.6 ms ] thread
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"

Can you share links to better guidance than OWASP?
The OWASP Top 10 is a list of vulnerabilities, not a checklist of things you have to actually "do".
(comment deleted)
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.

Yep SameSite lax, and just make sure you never perform any actions using Get requests, which you shouldn’t anyway.
This is "not allowing cross site at all" so, technically it's not "request forgery" protection. Yes, this is very semantic, but, CSRF is a vulnerability introduced by enabling CS and CORS. So, technically, same-site cookies are not "protection" against CSRF.
I’m confused, how does this prevent a CSRF attack?

SameSite or not is inconsequential to the check a backend does for a CSRF token in the POST.

SameSite doesn’t protect against same-site cross-origin requests, so you are staking your app’s security on the security of the marketing blog.
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…
CSRF exists as a consequence of insecure-by-default browser handling of cookies, whereby the browser sends the host’s cookies on requests initiated by a third-party script to the vulnerable host. If a script can fake all headers, it’s not running in a browser, and so was never exposed to the insecure browser cookie handling to be able to leverage it as a vector. If no prerequisite vector, then no vulnerability to mitigate.
As I understand it, the moment you’re dealing with custom scripts, you’ve left the realm of a csrf attack. They’re dependent upon session tokens in cookies
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.

Are there any approaches to csrf tokens that don't require storing issued tokens on server-side?
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 ?

> 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?

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

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

If i open a link with a new target, say "foo" then post a form to the same "foo" target. What would be the origin?
I 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 :)
> this would also reject HTTP clients that are not browsers

Why? I can send any headers from a client I make.