-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Update Fetch Metadata positioning #1875
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
base: master
Are you sure you want to change the base?
Changes from 4 commits
435a7d5
3ae17e8
0920907
0c2aa20
8d1a394
ae616bd
8cc3255
8baf8d4
86fc26b
c3579b5
68310fc
a6d7336
d216055
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,7 +14,8 @@ In short, the following principles should be followed to defend against CSRF: | |
|
|
||
| - **See the OWASP [XSS Prevention Cheat Sheet](Cross_Site_Scripting_Prevention_Cheat_Sheet.md) for detailed guidance on how to prevent XSS flaws.** | ||
| - **First, check if your framework has [built-in CSRF protection](#use-built-in-or-existing-csrf-implementations-for-csrf-protection) and use it** | ||
| - **If the framework does not have built-in CSRF protection, add [CSRF tokens](#token-based-mitigation) to all state changing requests (requests that cause actions on the site) and validate them on the backend** | ||
| - **If the framework does not have built-in CSRF protection, add [CSRF tokens](#token-based-mitigation) to all state changing requests (requests that cause actions on the site) and validate them on the backend, or validate [Fetch Metadata headers](#fetch-metadata-headers) on the backend for all state-changing requests.** | ||
| - **If your software is intended to be used only on modern browsers, you may rely primarily on [Fetch Metadata headers](#fetch-metadata-headers) to block cross-site state-changing requests** | ||
| - **Stateful software should use the [synchronizer token pattern](#synchronizer-token-pattern)** | ||
| - **Stateless software should use [double submit cookies](#alternative-using-a-double-submit-cookie-pattern)** | ||
| - **If an API-driven site can't use `<form>` tags, consider [using custom request headers](#employing-custom-request-headers-for-ajaxapi)** | ||
|
|
@@ -151,6 +152,97 @@ Since an attacker is unable to access the cookie value during a cross-site reque | |
|
|
||
| Though the Naive Double-Submit Cookie method is simple and scalable, it remains vulnerable to cookie injection attacks, especially when attackers control subdomains or network environments allowing them to plant or overwrite cookies. For instance, an attacker-controlled subdomain (e.g., via DNS takeover) could inject a matching cookie and thus forge a valid request token. [This resource](https://owasp.org/www-chapter-london/assets/slides/David_Johansson-Double_Defeat_of_Double-Submit_Cookie.pdf) details these vulnerabilities. Therefore, always prefer the _Signed Double-Submit Cookie_ pattern with session-bound HMAC tokens to mitigate these threats. | ||
|
|
||
| ## Fetch Metadata headers | ||
|
|
||
| Fetch Metadata request headers provide extra context about how an HTTP request was made, and how the resource will be used, enabling servers to reject suspicious cross-site requests. Servers can use these headers — most importantly `Sec-Fetch-Site` — as a lightweight and reliable method to block obvious cross-site requests. See the [Fetch Metadata specification](https://www.w3.org/TR/fetch-metadata/) for details. | ||
|
|
||
| Although Fetch Metadata headers are relatively new compared to [token-based defenses](#token-based-mitigation), they provide a simple way to block cross-origin state-changing requests and on modern browsers—can serve as a primary CSRF mitigation. The main caveat is compatibility: some legacy browsers, non-browser clients and certain webviews may not send `Sec-Fetch-*` headers, so deployments should include tested fallback behavior and a careful rollout plan. | ||
|
|
||
| The Fetch Metadata request headers are: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this section be focused on the subset of Sec-Fetch-* headers that are used to protect against CSRF? (Just
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @artis3n, sorry for the wait on the review If I understood your comment correctly, you’re suggesting narrowing this section down to just Sec-Fetch-Site and Origin/Host header validation. I agree this would simplify things, but my thinking was that it’s important to keep the broader context of Fetch Metadata headers, since they work as a family and the spec encourages using them together.
we already link out to the W3C spec for readers who want deeper detail. Maybe you can draft a short suggestion of what you’d like this section to look like?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, there are examples of Sec-Fetch-Mode, Sec-Fetch-Dest, and Sec-Fetch-User further down in the section. Because of that, I think it makes sense to keep the broader description here, so all the related information stays in one place and developers don’t need to jump between different resources to understand how the full set of headers works together. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That makes sense if we want to keep them co-located. I do think we should distinguish between the headers you need to use for CSRF protection vs. the ones you can use for additional checks. E.g. mode, dest, and user.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mode, dest, and user are still part of the overall Fetch Metadata–based CSRF protection strategy, the Sec-Fetch-Site header is just the primary signal we rely on. But I get your point, I’ll try to update the wording to make that distinction clearer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that making it clear that sec-fetch-site is the primary signal would be important, so as to not overcomplicate/overwhelm people. The others can be included for reference/deep dive.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated |
||
|
|
||
| - Sec-Fetch-Site — indicates relationship between request initiator’s origin and it's target's origin: `same-origin`, `same-site`, `cross-site`, or `none`. | ||
| - Sec-Fetch-Mode — indicates the request's [mode](https://fetch.spec.whatwg.org/#concept-request-mode)(e.g., `navigate`, `no-cors`, `cors`, `same-origin`, and `websocket`), which allows to distinguish between requests originating from a user navigating between HTML pages, and requests to load images and other resources. | ||
| - Sec-Fetch-Dest — indicates the [destination](https://fetch.spec.whatwg.org/#concept-request-destination) for the requested resource (e.g., `document`, `image`, `script`, etc.). | ||
| - Sec-Fetch-User — present only for navigation requests initiated by user. When sent value is `?1`, meaning `true`. | ||
|
|
||
| If any of headers above contain values not listed in the specification, in order to support forward-compatibility, servers should ignore those headers. | ||
|
|
||
| ### Ease of use | ||
|
|
||
| Unlike [synchronizer tokens](#synchronizer-token-pattern) or [double-submit patterns](#alternative-using-a-double-submit-cookie-pattern) — which require additional client/server coordination and are easy to misimplement — Fetch Metadata checks are straightforward to implement correctly. They typically require only a small amount of server-side logic (inspect Sec-Fetch-Site, optionally refine with Sec-Fetch-Mode/Sec-Fetch-Dest) and no client changes. That simplicity reduces complexity, making the approach attractive for many applications. | ||
|
|
||
| ### Browser compatibility | ||
|
|
||
| Fetch Metadata request headers are supported in most modern browsers on both desktop and mobile (Chrome, Edge, Firefox, Safari 16.4+, and even in webviews on both iOS and Android). For compatibility detail, see the [browser support table](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Site#browser_compatibility). | ||
|
|
||
| If your project requires absolute, 100% client coverage, [CSRF tokens](#token-based-mitigation) remain the safest universal option. | ||
|
|
||
| ### How to treat Fetch Metadata headers on the server-side | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd recommend we update this section. Maintaining language agnosticity, I'd like to see the pattern outlined in https://words.filippo.io/csrf/#protecting-against-csrf-in-2025 personally.
Also the pattern described, basically, in https://web.dev/articles/fetch-metadata#how_to_use_fetch_metadata_to_protect_against_cross-origin_attacks . There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And I'm partial since it is the language I am usually in but would be nice to note with the examples that Go developers can follow the above pattern by just using https://pkg.go.dev/net/[email protected]#CrossOriginProtection in the standard library as of Go 1.25. Perhaps similar to https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#javascript-automatically-including-csrf-tokens-as-an-ajax-request-header , a section for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This PR's aproach was actually based primarily on the approach outlined in https://web.dev/articles/fetch-metadata#how_to_use_fetch_metadata_to_protect_against_cross-origin_attacks, however, I intentionally changed the flow to make it deny-by-default, rather than the more allow-by-default used in both that article and Filippo’s blog. Both external sources propose flows that can short-circuit early and allow requests without evaluating Fetch Metadata. For example:
In this example, the request never reaches any Fetch Metadata checks. That effectively makes the Origin header the primary defense.
I’m not fully comfortable relying on that assumption. My interpretation is that it’s based on the earlier point that older (pre-2020) browsers sometimes omitted the Origin header on POST. The wording could also be misread as “pre-2020 browsers can’t be affected by CSRF,” which I’m sure isn’t the intended meaning. if as a community we agree that Fetch Metadata is the stronger and more reliable signal in modern browsers, then it seems logical to evaluate Sec-Fetch-Site first and use standard headers (Origin/Referer/Host) only as a fallback for legacy or non-compliant clients.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Regarding your second suggestion, that’s a great idea. We should definitely add a Go section to the “CSRF Prevention in Modern Frameworks” area. I can write a short part for it and include a reference to the CrossOriginProtection middleware along with a link to the documentation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While the blog post uses Origin first, the actual Go source code checks sec-fetch-site first: I have to assume this concern was raised while implementing the feature, and thus appropriately uses origin as a fallback.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for bringing this up, I went straight for the blog. As we discussed, we’ll definitely add a reference to the Go implementation in the Frameworks section at the end of the document. Do you feel the current “### How to treat Fetch Metadata headers on the server-side” section needs any updates? @nickchomey
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added this as part of "### Use Built-In Or Existing CSRF Implementations for CSRF Protection" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems OK to me. |
||
|
|
||
| `Sec-Fetch-Site` is the most useful Fetch Metadata header for blocking CSRF-like cross-origin requests and should be the primary signal in a Fetch-Metadata-based policy. Use other Fetch Metadata headers (`Sec-Fetch-Mode`, `Sec-Fetch-Dest`, `Sec-Fetch-User`) to further refine or tailor policies to your application (for example, allowing navigate mode top-level requests or permitting specific Dest values for resource endpoints). | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is navigate mode defined anywhere? |
||
| **Policy (high level)** | ||
|
|
||
| 1. If `Sec-Fetch-Site` is present: | ||
| 1.1. Treat cross-site as untrusted for state-changing actions. By default, reject non-safe methods (POST / PUT / PATCH / DELETE) when `Sec-Fetch-Site: cross-site`. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't really a comment about the doc, but a related question. In wordpress, Ajax requests are often made with GET to make mutations. This is, of course, wrong in many ways, but does happen. Though, wp encourages the use of their nonce mechanism for Ajax, and doesn't use fetch metadata at all. Still, I will likely put in some effort to see if sec fetch can replace the wp nonces in a drop-in manner (just turn off/ignore nonces if they're there and use sec fetch) So, I wonder what can be done to handle state-changing GET requests? Should anything be mentioned here for the benefit of other frameworks or sites that use GET incorrectly? |
||
|
|
||
| ```JavaScript | ||
| const SAFE = new Set(['GET','HEAD','OPTIONS']); | ||
| const site = req.get('Sec-Fetch-Site'); // e.g. 'cross-site','same-site','same-origin','none' | ||
|
|
||
| if (site === 'cross-site' && !SAFE.has(req.method)) { | ||
| return false; // forbid this request | ||
| } | ||
| ``` | ||
|
|
||
| 1.2. Allow `same-origin`. Treat `same-site` as allowed only if your threat model trusts sibling subdomains; otherwise handle `same-site` conservatively (for example, require additional validation). | ||
|
|
||
| ```JavaScript | ||
| const trustSameSite = false; // set true only if you trust sibling subdomains | ||
|
|
||
| if (site === 'same-origin') { | ||
| return true; | ||
| } else if (site === 'same-site') { | ||
| // handle same-site separately so the subcondition is clearly scoped to same-site | ||
| if (!trustSameSite && !SAFE.has(req.method)) { | ||
| return false; // treat same-site as untrusted for state-changing methods | ||
| } | ||
| return true; | ||
| } | ||
| ``` | ||
|
|
||
| 1.3. Allow none for user-driven top-level navigations (bookmarks, typed URLs, explicit form submits) where appropriate. | ||
|
|
||
| 2. If `Sec-Fetch-*` headers are absent: choose a fallback based on risk and compatibility requirements: | ||
| 2.1. Fail-safe (recommended for sensitive endpoints): treat absence as unknown and block the request. | ||
| 2.2. Fail-open (compatibility-first): fallback to other security measure (CSRF tokens, validate Origin/Referer, and/or require additional validation). | ||
|
|
||
| 3. Additionall options | ||
mkhanas marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 3.1 To ensure that your site can still be linked from other sites, you have to allow simple (HTTP GET) top-level navigation. | ||
|
|
||
| ```JavaScript | ||
| if (req.get('Sec-Fetch-Mode') === 'navigate' && | ||
| req.method === 'GET' && | ||
| req.get('Sec-Fetch-Dest') !== 'object' && | ||
| req.get('Sec-Fetch-Dest') !== 'embed') { | ||
| return true; // Allow this request | ||
| } | ||
| ``` | ||
|
|
||
| 3.2 Whitelist explicit cross-origin flows. If certain endpoints intentionally accept cross-origin requests (CORS JSON APIs, third-party integrations, webhooks), explicitly exempt those endpoints from the global Sec-Fetch deny policy and secure them with proper CORS configuration, authentication, and logging. | ||
|
|
||
| ### Limitations and gotchas | ||
|
|
||
| - Not universal. Some older browsers, webviews, bots, and non-browser HTTP clients do not send Sec-Fetch-*. Do not assume presence on every request — implement fallbacks. | ||
| - May break legitimate cross-origin integrations. A global Sec-Fetch policy can unintentionally block legitimate CORS or third-party flows; plan explicit whitelisting. | ||
| - One limitation is that Fetch Metadata request headers are only sent to [potentially trustworthy URLs](https://www.w3.org/TR/secure-contexts/#is-url-trustworthy). This means the headers will generally be present for requests to origins whose scheme is `https`, `wss`, or `file`, and for `localhost` (hosts in the `127.0.0.0/8` or `::1/128` ranges). For the full rules and additional edge cases (the algorithm the user agent uses to decide trustworthiness), see the [W3C Secure Contexts spec](https://www.w3.org/TR/secure-contexts/#is-origin-trustworthy). | ||
|
|
||
| ### Rollout & testing recommendations | ||
|
|
||
| - Include an appropriate `Vary` header, in order to ensure that caches handle the response appropriately. For example, `Vary: Accept-Encoding, Sec-Fetch-Site`. See more [Fetch Metadata specification](https://w3c.github.io/webappsec-fetch-metadata/#vary). | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add any info on what the issue is here that is being addressed? It isn't obvious to me, and surely most others. |
||
| - Start in “log only” mode. Record requests that would be blocked and review for false positives before enforcing. This is the safest way to discover legitimate flows that need whitelisting. | ||
| - Monitor UA coverage. Track which user agents include `Sec-Fetch-*` and which don’t; ensure your fallback logic covers missing-header cases. Use metrics to decide when to enforce stricter policies. | ||
| - Document exceptions. Keep an explicit list of endpoints whitelisted for cross-origin access. | ||
|
|
||
| ## Disallowing simple requests | ||
|
|
||
| When a `<form>` tag is used to submit data, it sends a ["simple" request](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests) that browsers do not designate as "to be preflighted". These "simple" requests introduce risk of CSRF because browsers permit them to be sent to any origin. If your application uses `<form>` tags to submit data anywhere in your client, you will still need to protect them with alternate approaches described in this document such as tokens. | ||
|
|
@@ -368,89 +460,6 @@ Cookie prefixes [are supported by all major browsers](https://developer.mozilla. | |
|
|
||
| See the [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#Directives) and [IETF Draft](https://tools.ietf.org/html/draft-west-cookie-prefixes-05) for further information about cookie prefixes. | ||
|
|
||
| ### Use Fetch Metadata headers to verify nature of the request | ||
|
|
||
| Fetch Metadata request headers provide extra context about how an HTTP request was made, and how the resource will be used, enabling servers to reject suspicious cross-site requests. Servers can use these headers — most importantly `Sec-Fetch-Site` — as a lightweight method to block obvious cross-site requests. See the [Fetch Metadata specification](https://www.w3.org/TR/fetch-metadata/) for details. | ||
|
|
||
| It is important to note that Fetch Metadata headers should be implemented as an additional layer defense in depth concept. This attribute should not replace a CSRF tokens (or equivalent framework protections). | ||
|
|
||
| The Fetch Metadata request headers are: | ||
|
|
||
| - Sec-Fetch-Site — indicates relationship between request initiator’s origin and it's target's origin: `same-origin`, `same-site`, `cross-site`, or `none`. | ||
| - Sec-Fetch-Mode — indicates the request's [mode](https://fetch.spec.whatwg.org/#concept-request-mode)(e.g., `navigate`, `no-cors`, `cors`, `same-origin`, and `websocket`), which allows to distinguish between requests originating from a user navigating between HTML pages, and requests to load images and other resources. | ||
| - Sec-Fetch-Dest — indicates the [destination](https://fetch.spec.whatwg.org/#concept-request-destination) for the requested resource (e.g., `document`, `image`, `script`, etc.). | ||
| - Sec-Fetch-User — present only for navigation requests initiated by user. When sent value is `?1`, meaning `true`. | ||
|
|
||
| If any of headers above contain values not listed in the specification, in order to support forward-compatibility, servers should ignore those headers. | ||
|
|
||
| Browser compatability: Fetch Metadata request headers are supported in most modern browsers on both desktop and mobile (Chrome, Edge, Firefox, Safari 16.4+, and even in webviews on both iOS and Android). For compatibility detail, see the [browser support table](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Site#browser_compatibility). | ||
|
|
||
| #### How to treat Fetch Metadata headers on the server-side | ||
|
|
||
| Sec-Fetch-Site is the most useful Fetch Metadata header for blocking CSRF-like cross-origin requests and should be the primary signal in a Fetch-Metadata-based policy. Use other Fetch Metadata headers (Sec-Fetch-Mode, Sec-Fetch-Dest, Sec-Fetch-User) to further refine or tailor policies to your application (for example, allowing navigate mode top-level requests or permitting specific Dest values for resource endpoints). | ||
| **Policy (high level)** | ||
|
|
||
| 1. If Sec-Fetch-Site is present: | ||
| 1.1. Treat cross-site as untrusted for state-changing actions. By default, reject non-safe methods (POST / PUT / PATCH / DELETE) when `Sec-Fetch-Site: cross-site`. | ||
|
|
||
| ```JavaScript | ||
| const SAFE = new Set(['GET','HEAD','OPTIONS']); | ||
| const site = req.get('Sec-Fetch-Site'); // e.g. 'cross-site','same-site','same-origin','none' | ||
|
|
||
| if (site === 'cross-site' && !SAFE.has(req.method)) { | ||
| return false; // forbid this request | ||
| } | ||
| ``` | ||
|
|
||
| 1.2. Allow `same-origin`. Treat `same-site` as allowed only if your threat model trusts sibling subdomains; otherwise handle `same-site` conservatively (for example, require additional validation). | ||
|
|
||
| ```JavaScript | ||
| const trustSameSite = false; // set true only if you trust sibling subdomains | ||
|
|
||
| if (site === 'same-origin') { | ||
| return true; | ||
| } else if (site === 'same-site') { | ||
| // handle same-site separately so the subcondition is clearly scoped to same-site | ||
| if (!trustSameSite && !SAFE.has(req.method)) { | ||
| return false; // treat same-site as untrusted for state-changing methods | ||
| } | ||
| return true; | ||
| } | ||
| ``` | ||
|
|
||
| 1.3. Allow none for user-driven top-level navigations (bookmarks, typed URLs, explicit form submits) where appropriate. | ||
|
|
||
| 2. If Sec-Fetch-* headers are absent: choose a fallback based on risk and compatibility requirements: | ||
| 2.1. Fail-safe (recommended for sensitive endpoints): treat absence as unknown and block the request. | ||
| 2.2. Fail-open (compatibility-first): fallback to other security measure (CSRF tokens, validate Origin/Referer, and/or require additional validation). | ||
|
|
||
| 3. Additionall options | ||
| 3.1 To ensure that your site can still be linked from other sites, you have to allow simple (HTTP GET) top-level navigation. | ||
|
|
||
| ```JavaScript | ||
| if (req.get('Sec-Fetch-Mode') === 'navigate' && | ||
| req.method === 'GET' && | ||
| req.get('Sec-Fetch-Dest') !== 'object' && | ||
| req.get('Sec-Fetch-Dest') !== 'embed') { | ||
| return true; // Allow this request | ||
| } | ||
| ``` | ||
|
|
||
| 3.2 Whitelist explicit cross-origin flows. If certain endpoints intentionally accept cross-origin requests (CORS JSON APIs, third-party integrations, webhooks), explicitly exempt those endpoints from the global Sec-Fetch deny policy and secure them with proper CORS configuration, authentication, and logging. | ||
|
|
||
| #### Limitations and gotchas | ||
|
|
||
| - Not universal. Some older browsers, webviews, bots, and non-browser HTTP clients do not send Sec-Fetch-*. Do not assume presence on every request — implement fallbacks. | ||
| - May break legitimate cross-origin integrations. A global Sec-Fetch policy can unintentionally block legitimate CORS or third-party flows; plan explicit whitelisting. | ||
| - One limitation is that Fetch Metadata request headers are only sent to [potentially trustworthy URLs](https://www.w3.org/TR/secure-contexts/#is-url-trustworthy). This means the headers will generally be present for requests to origins whose scheme is `https`, `wss`, or `file`, and for `localhost` (hosts in the `127.0.0.0/8` or `::1/128` ranges). For the full rules and additional edge cases (the algorithm the user agent uses to decide trustworthiness), see the [W3C Secure Contexts spec](https://www.w3.org/TR/secure-contexts/#is-origin-trustworthy). | ||
|
|
||
| #### Rollout & testing recommendations | ||
|
|
||
| - Include an appropriate Vary header [RFC9110], in order to ensure that caches handle the response appropriately. For example, `Vary: Accept-Encoding, Sec-Fetch-Site`. See more [Fetch Metadata specification](https://w3c.github.io/webappsec-fetch-metadata/#vary). | ||
| - Start in “log only” mode. Record requests that would be blocked and review for false positives before enforcing. This is the safest way to discover legitimate flows that need whitelisting. | ||
| - Monitor UA coverage. Track which user agents include Sec-Fetch-* and which don’t; ensure your fallback logic covers missing-header cases. Use metrics to decide when to enforce stricter policies. | ||
| - Document exceptions. Keep an explicit list of endpoints whitelisted for cross-origin access. | ||
|
|
||
| ### User Interaction-Based CSRF Defense | ||
|
|
||
| While all the techniques referenced here do not require any user interaction, sometimes it's easier or more appropriate to involve the user in the transaction to prevent unauthorized operations (forged via CSRF or otherwise). The following are some examples of techniques that can act as strong CSRF defense when implemented correctly. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.