[HN Gopher] CSRF protection without tokens or hidden form fields
       ___________________________________________________________________
        
       CSRF protection without tokens or hidden form fields
        
       Author : adevilinyc
       Score  : 288 points
       Date   : 2025-12-22 05:38 UTC (3 days ago)
        
 (HTM) web link (blog.miguelgrinberg.com)
 (TXT) w3m dump (blog.miguelgrinberg.com)
        
       | owenthejumper wrote:
       | 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"
        
         | miguelgrinberg wrote:
         | Hi, 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.
        
         | nchmy wrote:
         | Can you share links to better guidance than OWASP?
        
         | tptacek wrote:
         | The OWASP Top 10 is a list of vulnerabilities, not a checklist
         | of things you have to actually "do".
        
           | flomo wrote:
           | Completely 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 wrote:
           | If you look from perspective of vulnerability assessment, it
           | kind of is.
        
           | scott_w wrote:
           | While 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 wrote:
             | I'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 wrote:
               | To be fair, though, you're a lot more knowledgeable and
               | experienced than some security "experts" I've had to deal
               | with ;-)
        
         | 8n4vidtmkvmk wrote:
         | Since when are they case sensitive?
         | 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.
        
           | thomascountz wrote:
           | +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
           | 
           | [2]:
           | https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
        
           | thatwasunusual wrote:
           | >> FYI, HTTP headers are case insensitive
           | 
           | > Since when are they case sensitive?
           | 
           | [...]
        
             | thomascountz wrote:
             | Perhaps 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.
        
           | jonway wrote:
           | My primitive instincts lead me to believe that sometimes they
           | end up being Case-Sensitive and Sometimes NoT! (depending on
           | implementation)
        
       | tmsbrg wrote:
       | 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.
        
         | miguelgrinberg wrote:
         | The OWASP CSRF prevention cheat sheet page does mention
         | SameSite cookies, but they consider it defense in depth:
         | https://cheatsheetseries.owasp.org/cheatsheets/Cross-
         | Site_Re....
        
           | tptacek wrote:
           | Because of clientside Javascript CSRF, which is not a common
           | condition.
        
             | nchmy wrote:
             | Client side js is not particularly relevant to csrf.
        
               | tptacek wrote:
               | I 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 wrote:
               | But 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)
        
             | tmsbrg wrote:
             | What do you mean with clientside Javascript CSRF?
        
           | hn_throwaway_99 wrote:
           | I don't understand the potential vulnerabilities listed at
           | the linked section here:
           | https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-
           | rfc...
           | 
           | They give 2 reasons why SameSite cookies are only considered
           | defense in depth:
           | 
           | ----
           | 
           | > Lax enforcement provides reasonable defense in depth
           | against CSRF attacks that rely on unsafe HTTP methods (like
           | "POST"), but does not offer a robust defense against CSRF as
           | a general category of attack:
           | 
           | > 1. Attackers can still pop up new windows or trigger top-
           | level navigations in order to create a "same-site" request
           | (as described in section 2.1), which is only a speedbump
           | along the road to exploitation.
           | 
           | > 2. Features like "<link rel='prerender'>" [prerendering]
           | can be exploited to create "same-site" requests without the
           | risk of user detection.
           | 
           | > When possible, developers should use a session management
           | mechanism such as that described in Section 8.8.2 to mitigate
           | the risk of CSRF more completely.
           | 
           | ----
           | 
           | But that doesn't make any sense to me. I think "the robust
           | solution" should be to just be sure that you're only
           | performing potential sensitive actions on POST or other
           | mutable method requests, and always setting the SameSite
           | attribute. If that is true, there is absolutely no
           | vulnerability if the user is using a browser from the past
           | seven years or so. The 2 points noted in the above section
           | would only lead to a vulnerability if you're performing a
           | sensitive state-changing action on a GET. So rather than tell
           | developers to implement a complicated "session management
           | mechanism", it seems like it would make a lot more sense to
           | just say don't perform sensitive state changes on a GET.
           | 
           | Am I missing something here? Do I not understand the
           | potential attack vectors laid out in the 2 bullet points?
        
         | tordrt wrote:
         | Yep SameSite lax, and just make sure you never perform any
         | actions using Get requests, which you shouldn't anyway.
        
           | paulryanrogers wrote:
           | Unsubscribe often need to be GET, or at least start as GET
        
             | eli wrote:
             | list-unsubscribe header sends a POST. Probably makes more
             | sense to just use a token from an email anyway.
        
               | hn_throwaway_99 wrote:
               | The way the list-unsubscribe header works, it essentially
               | _must_ use a token when one click unsubscribe (i.e when
               | the List-Unsubscribe-Post: List-Unsubscribe=One-Click
               | header is also passed) is used, and since GMail has
               | required one click unsubscribe for nearly 2 years now, my
               | guess is all bulk mail senders support this. Relevant
               | section from the one click unsubscribe RFC:
               | 
               | > The URI in the List-Unsubscribe header MUST contain
               | enough information to identify the mail recipient and the
               | list from which the recipient is to be removed, so that
               | the unsubscription process can complete automatically.
               | Since there is no provision for extra POST arguments, any
               | information about the message or recipient is encoded in
               | the URI. In particular, one-click has no way to ask the
               | user what address or from what list the user wishes to
               | unsubscribe.
               | 
               | > The POST request MUST NOT include cookies, HTTP
               | authorization, or any other context information. The
               | unsubscribe operation is logically unrelated to any
               | previous web activity, and context information could
               | inappropriately link the unsubscribe to previous
               | activity.
               | 
               | > The URI SHOULD include an opaque identifier or another
               | hard-to-forge component in addition to, or instead of,
               | the plaintext names of the list and the subscriber. The
               | server handling the unsubscription SHOULD verify that the
               | opaque or hard-to-forge component is valid. This will
               | deter attacks in which a malicious party sends spam with
               | List-Unsubscribe links for a victim list, with the
               | intention of causing list unsubscriptions from the victim
               | list as a side effect of users reporting the spam, or
               | where the attacker does POSTs directly to the mail
               | sender's unsubscription server.
               | 
               | > The mail sender needs to provide the infrastructure to
               | handle POST requests to the specified URI in the List-
               | Unsubscribe header, and to handle the unsubscribe
               | requests that its mail will provoke.
        
         | nhumrich wrote:
         | 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.
        
           | hn_throwaway_99 wrote:
           | I don't understand your distinction at all. I may not quite
           | grok your meaning here, but CORS is usually discussed in the
           | context of allowing cross-origin AJAX calls.
           | 
           | But cross origin form posts are and have always been
           | permitted, and are the main route by which CSRF
           | vulnerabilities arise. Nothing on the client or server needs
           | to be enabled to allow these form posts.
           | 
           | Furthermore, the approach detailed in the article simply has
           | the server block requests if they are cross site/origin
           | requests, so I'm not sure what the semantic difference is.
        
             | true_religion wrote:
             | Yeah, CORS is not a safety mechanism. It's a procedure of
             | loosening the default safety mechanism of not sharing any
             | response data from a cross site request with client side
             | JavaScript.
        
           | nchmy wrote:
           | Cs and cors have nothing to do with csrf... Though, yes,
           | neither does same-site
        
             | nchmy wrote:
             | I don't know why I said same-site cookies have nothing to
             | do with csrf. They can be helpful as defense in depth, but
             | not primary defense.
        
               | hn_throwaway_99 wrote:
               | I haven't seen any proposed attack vectors where they are
               | insufficient primary defense when using SameSite Lax as
               | long as you don't do any sensitive state change
               | operations on non-mutative methods like GET.
               | 
               | I feel like people are just parroting the OWASP "they're
               | just defense in depth!" line without understanding what
               | the actual underlying vulnerabilities are, namely:
               | 
               | 1. If you're performing a sensitive operation on a GET,
               | you're in trouble. But I think that is a bigger problem
               | and you shouldn't do that.
               | 
               | 2. If a user is on a particularly old browser, but these
               | days SameSite support has been out on all major browsers
               | for nearly a decade so I think that point is moot.
               | 
               | The problem I have with the "it's just defense in depth"
               | line is people don't really understand how it protects
               | against any underlying vulnerabilities. In that case,
               | CSRF tokens add complexity without actually making you
               | any safer.
               | 
               | I'd be happy to learn why my thinking is incorrect, i.e.
               | where there's a vulnerability lurking that I'm not
               | thinking of if you use SameSite Lax and only perform
               | state changes on mutable methods.
        
         | hatefulheart wrote:
         | 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.
        
           | tptacek wrote:
           | No? The whole point of SameSite=(!none) is to prevent
           | requests from unexpectedly carrying cookies, which is how
           | CSRF attacks work.
        
             | hatefulheart wrote:
             | What does this even mean?
             | 
             | I'm not being rude, what does it mean to unexpectedly carry
             | cookies? That's not what I understand the risk of CSRF is.
             | 
             | My understanding is that we want to ensure a POST came from
             | our website and we do so with a double signed HMAC token
             | that is present in the form AND the cookie, which is also
             | tied to the session.
             | 
             | What on earth is unexpectedly carrying cookies?
        
               | demurgos wrote:
               | The "unexpected" part is that the browser automatically
               | fills some headers on behalf of the user, that the
               | (malicious) origin server does not have access to. For
               | most headers it's not a problem, but cookies are more
               | sensitive.
               | 
               | The core idea behind the token-based defense is to prove
               | that the origin server had access to the value in the
               | first place such that it could have sent it if the
               | browser didn't add it automatically.
               | 
               | I tend to agree that the inclusion of cookies in cross-
               | site requests is the wrong default. Using same-site fixes
               | the problem at the root.
               | 
               | The general recommendation I saw is to have two cookies.
               | One without same-site for read operations, this allows to
               | gracefully handle users navigating to your site. And a
               | second same-site cookie for state-changing operations.
        
           | hn_throwaway_99 wrote:
           | The only reason CSRF is even possible is because the browser
           | sends (or, well, used to send) cookies for a particular
           | request even if that request initiated from a different site.
           | If the browser never did that (and most people would argue
           | that's a design flaw from the get go) CSRF attacks wouldn't
           | even be possible. The SameSite attribute makes it so that
           | cookies will only be sent if the request that originated them
           | is the same origin as the origin that originally wrote the
           | cookie.
        
             | hatefulheart wrote:
             | I think I understand now, the Cookie just is not present in
             | the POST if a user clicked on, for example, a maliciously
             | crafted post from a different origin?
        
               | kassner wrote:
               | Exactly.
        
             | zenmac wrote:
             | Never needed the CSRF and assumed that cookies was always
             | SameSite, but can see that it was introduced in 2016. Just
             | had the sitename put into the value of the cookie since,
             | and never really needed to think about that.
             | 
             | Just feels like all these http specs are super duck tapped
             | together. I guess that is only way to ensure mass adoption
             | for new devs and now vibe coders.
        
               | alserio wrote:
               | I'm not sure I'm understanding your solution
        
               | hn_throwaway_99 wrote:
               | Given what was written, I'm not quite sure the author
               | does either.
        
               | zenmac wrote:
               | If the domain name is in the cookie value then that can't
               | be used when submit another request from another domain.
               | Yes you can configure the dns to bypass that, but at that
               | point it is also pointless for CSRF.
        
         | FiloSottile wrote:
         | 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.
        
           | tmsbrg wrote:
           | What do you mean with same-site cross-origin requests?
        
             | FiloSottile wrote:
             | See the same-site section of https://words.filippo.io/csrf/
        
               | tmsbrg wrote:
               | Oh, thanks. I learned something new. Never knew that
               | different subdomains are considered the same "site", but
               | MDN confirms this[0]. This shows just how complex these
               | matters are imo, it's not surprising people make mistakes
               | in configuring CSRF protection.
               | 
               | It's a pretty cool attack chain, if there's an XSS on
               | marketing.example.com it can be used to execute a CSRF on
               | app.example.com! It could also be used with dangling
               | subdomain takeover or if there's open subdomain
               | registration.
               | 
               | [0] https://developer.mozilla.org/en-
               | US/docs/Glossary/Site
        
               | FiloSottile wrote:
               | It's why I like Sec-Fetch-Site: the #1 risk is for the
               | developer to make a mistake trying to configure something
               | more complex. Sec-Fetch-Site delegates the complexity to
               | the browser.
        
               | hxtk wrote:
               | It's a real problem for defense sites because .mil is a
               | public suffix so all navy.mil sites are the "same site"
               | and all af.mil sites etc.
        
           | hn_throwaway_99 wrote:
           | Thanks very much for your comment. I posted elsewhere that I
           | felt like SameSite: Lax should be considered a primary
           | defense, not just "Defense in depth" as OWASP calls it, but
           | your rationale makes sense to me, while OWASP's does not.
           | 
           | That is, if you are using SameSite Lax and not performing
           | state changes on GETs, there is no real attack vector, _but_
           | like you say it means you need to be able to trust the
           | security of all of your subdomains equally, which is rarely
           | if ever the case.
           | 
           | I'm surprised browser vendors haven't thought of this. Like
           | even SameSite: Strict will still send cookies when the
           | request comes from a subdomain. Has there been any talk of
           | adding something like a SameSite: SameOrigin or something
           | like that? It seems weird to me that the Sec-Fetch-Site
           | header has clear delineations between site and origin, but
           | the SameSite header does not.
        
             | FiloSottile wrote:
             | Browser vendors have absolutely thought about this, at
             | length.
             | 
             | The web platform is intricate, legacy, and critical.
             | Websites by and large can't and don't break with browser
             | updates, which makes all of these things like operating on
             | the engine in flight.
             | 
             | For example, click through some of the multiple iterations
             | of the Schemeful Same Site proposal linked from my blog.
             | 
             | Thing is, SameSite's primary goal was not CSRF prevention,
             | it was privacy. CSRF is what Fetch metadata is for.
        
       | shermantanktop wrote:
       | 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...
        
         | varenc wrote:
         | If 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 wrote:
           | > 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
        
             | varenc wrote:
             | Ah, 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...)
        
           | ImJamal wrote:
           | How would you make SSO work without cross domain posts?
        
         | ctidd wrote:
         | 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.
        
         | t-writescode wrote:
         | 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
        
           | nchmy wrote:
           | Csrf is not dependent on js. It happens via normal links on
           | external sites.
        
             | t-writescode wrote:
             | That's what I said, yes.
        
               | nchmy wrote:
               | Sorry, I misread your comment
        
       | rvnx wrote:
       | 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.
        
         | simonw wrote:
         | I 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 wrote:
           | You 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 wrote:
           | SameSite=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.
        
         | Macha wrote:
         | Note SameSite=Strict also counts against referrals too, which
         | means your first request will appear unauthenticated. If this
         | request just loads your SPA skeleton, that might be fine, but
         | if you're doing SSR of any sort, that might not be what you
         | want.
        
           | rocqua wrote:
           | That's why someone suggested a non samesite cookie for reads
           | and a samesite cookie for requests with side effects.
           | 
           | CSRF is mostly about causing side effects, not about access
           | to information. And presumably just displaying your landing
           | page should not have side effects, even when doing
           | authenticated server side rendering. At least no side effects
           | other than creating logs.
        
       | altmind wrote:
       | Are there any approaches to csrf tokens that don't require
       | storing issued tokens on server-side?
        
         | t-writescode wrote:
         | Most 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 wrote:
         | The 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.
        
       | est wrote:
       | 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
        
         | amluto wrote:
         | Or 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 wrote:
           | maybe adding a dedicated cookie for that specific path?
        
             | amluto wrote:
             | HTTP-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 wrote:
       | 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 ?
        
         | tptacek wrote:
         | The entire web security model assumes we can trust browsers to
         | implement web security policies!
        
           | louiskottmann wrote:
           | I 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 wrote:
             | Sure it is. The same-origin rule that holds the whole web
             | security model together is entirely a property of browser
             | behavior.
        
               | louiskottmann wrote:
               | That's indeed a good example of prior full trusting of
               | the browser by the server.
        
         | nchmy wrote:
         | It's a shame you talked about browser tampering, since better
         | caching is indeed a benefit of fetch metadata headers.
        
       | vasco wrote:
       | > 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 wrote:
         | Sec 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 wrote:
       | 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
        
         | thaumasiotes wrote:
         | Yeah, 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 wrote:
           | Wow, I used to be able to search google even from terminal
           | browsers like 'elinks'
        
             | rhdunn wrote:
             | I 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 wrote:
           | I 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.
        
             | c17r wrote:
             | I just tried with the "links" browser and I get a "Update
             | your browser. Your browser isn't supported anymore. To
             | continue your search, upgrade to a recent version"
        
           | rfmoz wrote:
           | The reference of robots.txt offer a good way to define
           | specific behavior for the whole domain, as example. Something
           | like that for security could be enough for large amount of
           | websites.
           | 
           | Also, a new header like "sec-policy: foo-url" may be a clean
           | way to move away that definitions from the app+web+proxy+cdn
           | mesh to a fixed clear point.
        
             | zwnow wrote:
             | These files are just ignored by everything. We dont need
             | .txt files, we need good defaults.
        
             | rfmoz wrote:
             | I reply myself because I've found that idea already
             | porposed:
             | 
             | "Origin policy was a proposal for a web platform mechanism
             | that allows origins to set their origin-wide configuration
             | in a central location, instead of using per-response HTTP
             | headers." - https://github.com/WICG/origin-policy
             | 
             | But their status is "[On hold for now]" since, at least,
             | three years ago.
        
         | _heimdall wrote:
         | This is an extremely common approach across industries. Look
         | into diesel engine emission control systems sometime if you
         | aren't familiar. The last few decades has been bolting one new
         | system on every dew years because the ones already added
         | continue to cause unintended reliability problems.
        
       | NorwegianDude wrote:
       | 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.
        
         | nchmy wrote:
         | Fetch 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 wrote:
         | NO. 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 wrote:
           | It'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 wrote:
         | I 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 wrote:
       | 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.
        
         | mxey wrote:
         | Without those headers, you can as a fallback compare the Origin
         | header to the Host header.
         | 
         | See https://words.filippo.io/csrf/
        
         | yread wrote:
         | > UX benefits of tokenless CSRF
         | 
         | What are those?
        
         | nchmy wrote:
         | 98% 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_Re...
        
       | 6510 wrote:
       | 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?
        
       | dorianmariecom wrote:
       | rails does this in 8.2
        
         | nchmy wrote:
         | *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/
        
       ___________________________________________________________________
       (page generated 2025-12-25 23:01 UTC)