Cross-Site Request Forgery Prevention Cheat Sheet¶
Introduction / 介绍¶
A Cross-Site Request Forgery (CSRF) attack occurs when a malicious web site, email, blog, instant message, or program tricks an authenticated user's web browser into performing an unwanted action on a trusted site. If a target user is authenticated to the site, unprotected target sites cannot distinguish between legitimate authorized requests and forged authenticated requests.
Since browser requests automatically include all cookies including session cookies, this attack works unless proper authorization is used, which means that the target site's challenge-response mechanism does not verify the identity and authority of the requester. In effect, CSRF attacks make a target system perform attacker-specified functions via the victim's browser without the victim's knowledge (normally until after the unauthorized actions have been committed).
However, successful CSRF attacks can only exploit the capabilities exposed by the vulnerable application and the user's privileges. Depending on the user's credentials, the attacker can transfer funds, change a password, make an unauthorized purchase, elevate privileges for a target account, or take any action that the user is permitted to do.
In short, the following principles should be followed to defend against CSRF:
IMPORTANT: Remember that Cross-Site Scripting (XSS) can defeat all CSRF mitigation techniques! While Cross-Site Scripting (XSS) vulnerabilities can bypass CSRF protections, CSRF tokens are still essential for web applications that rely on cookies for authentication. Consider the client and authentication method to determine the best approach for CSRF protection in your application.
- See the OWASP XSS Prevention Cheat Sheet for detailed guidance on how to prevent XSS flaws.
- First, check if your framework has built-in CSRF protection and use it
- If the framework does not have built-in CSRF protection, add CSRF tokens to all state-changing requests (requests that cause actions on the site) and validate them on the backend.
中文:如果框架没有内置的CSRF保护,请为所有导致网站动作的状态改变请求(即所有状态改变请求)添加CSRF令牌并在后端进行验证。** - If your software targets only modern browsers, you may rely on Fetch Metadata headers together with the fallback options described below to block cross-site state-changing requests.
中文:如果您的软件仅针对现代浏览器,您可能可以依赖Fetch元数据头以及以下描述的回退选项来阻止跨站状态改变请求。** - Stateful software should use the synchronizer token pattern
中文:有状态软件应使用同步器令牌模式** - Stateless software should use double submit cookies
中文:无状态软件应使用双重提交cookie** - If an API-driven site can't use
<form>tags, consider using custom request headers
中文:如果API驱动的网站不能使用<form>标签,请考虑使用自定义请求头** - Implement at least one mitigation from Defense in Depth Mitigations section
中文:至少实施深度防御缓解措施部分中的一项缓解措施** - SameSite Cookie Attribute can be used for session cookies but be careful to NOT set a cookie specifically for a domain. This action introduces a security vulnerability because all subdomains of that domain will share the cookie, and this is particularly an issue if a subdomain has a CNAME to domains not in your control.
中文:SameSite cookie属性可用于会话cookie但请注意不要为特定域名设置cookie。此操作引入了安全漏洞,因为该域名的所有子域名都将共享此cookie,如果子域名有CNAME指向您无法控制的域名,这将尤其成问题。 - Consider implementing user interaction based protection for highly sensitive operations
中文:考虑为高度敏感的操作实施基于用户交互的保护** - Consider verifying the origin with standard headers
中文:考虑使用标准头部验证来源(#使用标准头部验证来源)** - Do not use GET requests for state changing operations.
中文:不要使用GET请求进行状态更改操作** - If for any reason you do it, protect those resources against CSRF
中文:如果出于任何原因这样做,请保护这些资源免受CSRF攻击**
Built-In Or Existing CSRF Implementations / 内置或现有CSRF实现¶
Before building a custom token or Fetch-Metadata implementation, check whether your framework or platform already provides CSRF protection you can use. Built-in defenses are generally preferable because they’re maintained by the framework authors and reduce the risk of subtle implementation mistakes. For example:
在构建自定义令牌或Fetch-Metadata实现之前,检查您的框架或平台是否已经提供了您可以使用CSRF保护。内置防御通常更可取,因为它们由框架作者维护,并减少了细微实现错误的风险。例如:
- .NET can use built-in protection to add tokens to CSRF vulnerable resources. If you choose to use this protection, .NET makes you responsible for proper configuration (such as key management and token management).
中文:.NET可以使用内置保护为CSRF易受攻击的资源添加令牌。如果您选择使用此保护,.NET将使您负责适当的配置(例如密钥管理和令牌管理)。 - Starting from 1.25, Go developers can rely on the built-in CrossOriginProtection type. It implements a Fetch-Metadata-based CSRF defense (including validation of Sec-Fetch-Site and related headers) directly in the standard library.
中文:从1.25版本开始,Go开发者可以依赖内置的CrossOriginProtection类型。它直接在标准库中实现了基于Fetch-Metadata的CSRF防御(包括Sec-Fetch-Site和相关头部的验证)。
Token-Based Mitigation / 基于令牌的缓解¶
The synchronizer token pattern is one of the most popular and recommended methods to mitigate CSRF.
同步令牌模式(#同步令牌模式) 是缓解CSRF最流行和推荐的方法之一。
Synchronizer Token Pattern / 同步令牌模式¶
CSRF tokens should be generated on the server-side and they should be generated only once per user session or each request. Because the time range for an attacker to exploit the stolen tokens is minimal for per-request tokens, they are more secure than per-session tokens. However, using per-request tokens may result in usability concerns.
For example, the "Back" button browser capability can be hindered by a per-request token as the previous page may contain a token that is no longer valid. In this case, interaction with a previous page will result in a CSRF false positive security event on the server-side. If per-session token implementations occur after the initial generation of a token, the value is stored in the session and is used for each subsequent request until the session expires.
When a client issues a request, the server-side component must verify the existence and validity of the token in that request and compare it to the token found in the user session. The request should be rejected if that token was not found within the request or the value provided does not match the value within the user session. Additional actions such as logging the event as a potential CSRF attack in progress should also be considered.
CSRF tokens should be:
- Unique per user session.
- Secret
- Unpredictable (large random value generated by a secure method).
中文:不可预测的(由安全方法生成的大随机值)。
CSRF tokens prevent CSRF because without a CSRF token, an attacker cannot create valid requests to the backend server.
Transmitting CSRF Tokens in Synchronized Patterns¶
The CSRF token can be transmitted to the client as part of a response payload, such as a HTML or JSON response, then it can be transmitted back to the server as a hidden field on a form submission or via an AJAX request as a custom header value or part of a JSON payload. A CSRF token should not be transmitted in a cookie for synchronized patterns. A CSRF token must not be leaked in the server logs or in the URL. GET requests can potentially leak CSRF tokens at several locations, such as the browser history, log files, network utilities that log the first line of a HTTP request, and Referer headers if the protected site links to an external site.
CSRF令牌可以作为响应负载的一部分传输到客户端,例如HTML或JSON响应,然后它可以作为表单提交中的隐藏字段或通过AJAX请求作为自定义头值或JSON负载的一部分传输回服务器。对于同步模式,CSRF令牌不应通过cookie传输。CSRF令牌不得泄露在服务器日志或URL中。GET请求可能会在多个位置泄露CSRF令牌,例如浏览器历史记录、日志文件、记录HTTP请求第一行的网络工具,以及如果受保护站点链接到外部站点,则Referer头。
For example:
例如:
<form action="/transfer.do" method="post">
<input type="hidden" name="CSRFToken" value="OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZMGYwMGEwOA==">
[...]
</form>
Since requests with custom headers are automatically subject to the same-origin policy, it is more secure to insert the CSRF token in a custom HTTP request header via JavaScript than adding a CSRF token in the hidden field form parameter.
由于带有自定义头的请求自动受到同源策略的影响,因此通过JavaScript在自定义HTTP请求头中插入CSRF令牌比在隐藏字段表单参数中添加CSRF令牌更安全。
ALTERNATIVE: Using A Double-Submit Cookie Pattern / 替代方案:使用双提交cookie模式¶
If maintaining the state for CSRF token on the server is problematic, you can use an alternative technique known as the Double Submit Cookie pattern. This technique is easy to implement and is stateless. There are different ways to implement this technique, where the naive pattern is the most commonly used variation.
如果维护服务器上CSRF令牌的状态有问题,可以使用另一种称为双提交cookie模式的技术。这种技术易于实现且是无状态的。有几种实现这种技术的方法,其中最常用的变体是_naive_模式。
Signed Double-Submit Cookie (RECOMMENDED) / 签名双提交cookie(推荐)¶
The most secure implementation of the Double Submit Cookie pattern is the Signed Double-Submit Cookie, which explicitly ties tokens to the user's authenticated session (e.g., session ID). Simply signing tokens without session binding provides minimal protection and remains vulnerable to cookie injection attacks. Always bind the CSRF token explicitly to session-specific data.
双提交cookie模式最安全的实现是_Signed Double-Submit Cookie_,它明确地将令牌与用户的认证会话(例如会话ID)关联起来。仅对令牌进行签名而不绑定会话提供最小的保护,并且仍然容易受到cookie注入攻击。始终明确地将CSRF令牌绑定到特定于会话的数据。
If the token contains sensitive information (like session IDs or claims), always use Hash-based Message Authentication (HMAC) with a server-side secret key. This prevents token forgery while ensuring integrity. HMAC is preferred over simple hashing in all cases as it protects against various cryptographic attacks. For scenarios requiring confidentiality of token contents, use authenticated encryption instead.
如果令牌包含敏感信息(如会话ID或声明),始终使用基于哈希的消息认证(HMAC)与服务器端密钥。这可以防止令牌伪造并确保完整性。在所有情况下,HMAC都优于简单哈希,因为它可以防止各种密码学攻击。对于需要令牌内容机密性的场景,请使用认证加密。
Employing HMAC CSRF Tokens¶
To generate HMAC CSRF tokens (with a session-dependent user value), the system must have:
- A session-dependent value that changes with each login session. This value should only be valid for the entirety of the users authenticated session. Avoid using static values like the user's email or ID, as they are not secure (1 | 2 | 3). It's worth noting that updating the CSRF token too frequently, such as for each request, is a misconception that assumes it adds substantial security while actually harming the user experience (1). For example, you could choose one, or a combination, of the following session-dependent values:
中文:一个依赖于会话的值,每次登录会话都会改变。此值应仅在整个用户认证会话期间有效。避免使用像用户的电子邮件或ID这样的静态值,因为它们不安全(1 | 2 | 3)。值得注意的是,过于频繁地更新CSRF令牌,例如每次请求,是一种错误观念,它假设这会增加安全性,而实际上会损害用户体验(1)。例如,您可以选择以下会话依赖值之一,或它们的组合: - A secret cryptographic key Not to be confused with the random value from the naive implementation. This value is used to generate the HMAC hash. Ideally, store this key as discussed in the Cryptographic Storage page.
中文:一个秘密的加密密钥 不要与天真实现中的随机值混淆。此值用于生成HMAC散列。理想情况下,将此密钥存储在加密存储页面中讨论的方式。 - A random value for anti-collision purposes. Generate a random value (preferably cryptographically random) to ensure that consecutive calls within the same second do not produce the same hash (1).
中文:用于防碰撞的随机值。生成一个随机值(最好是加密随机值),以确保在同一秒内的连续调用不会产生相同的哈希值(1).
Should Timestamps be Included in CSRF Tokens for Expiration?
It's a common misconception to include timestamps as a value to specify the CSRF token expiration time. A CSRF Token is not an access token. They are used to verify the authenticity of requests throughout a session, using session information. A new session should generate a new token (1).
HMAC CSRF Token Generation and Validation¶
Compute HMAC-SHA-256 over an unambiguous encoding of the session-dependent value and random value described above, such as length-prefixed fields. Include the HMAC and random value in the token, and set the token cookie with the Secure attribute.
For each protected request, read the token from a submitted header or form field, never from the cookie alone. Reject requests with missing tokens or tokens with an invalid component count, encoding, or length. Recompute the HMAC using the same encoding, the current session-dependent value, and the random value from the submitted token. Compare the submitted and expected HMACs in the same representation using a constant-time comparison function, and reject mismatches.
Naive Double-Submit Cookie Pattern (DISCOURAGED) / 简单的双重提交Cookie模式(不建议使用)¶
[!WARNING]
中文:[!警告] The Naive Double-Submit Cookie pattern is bypassable by an attacker who can write cookies on the target domain (e.g., via a vulnerable sibling subdomain, DNS takeover, or plaintext-HTTP cookie injection on a non-
__Host-cookie). For new code, use the Signed Double-Submit Cookie pattern above. The naive pattern is documented for reference only.中文:简单的双重提交Cookie模式可以被攻击者绕过,攻击者可以在目标域上写入Cookie(例如,通过易受攻击的兄弟子域、DNS劫持或在非
__Host-Cookie上注入明文HTTP Cookie)。对于新代码,请使用上面的签名双重提交Cookie模式。简单的模式仅作为参考记录。
The Naive Double-Submit Cookie method is a scalable and easy-to-implement technique which uses a cryptographically strong random value as a cookie and as a request parameter (even before user authentication). Then the server verifies if the cookie value and request value match.
简单的双重提交Cookie方法是一种可扩展且易于实现的技巧,它使用强加密的随机值作为Cookie和请求参数(甚至在用户认证之前)。然后服务器验证Cookie值和请求值是否匹配。
The site must require that every transaction request from the user includes this random value as a custom request header or form parameter ONLY. Cookie validation is INSECURE.
网站必须要求每个用户事务请求都包含这个随机值,作为自定义请求头或表单参数。Cookie验证是不安全的。
Why? Browsers auto-send cookies on cross-site requests. Attackers can trigger this automatically. Security requires explicit client submission (header/param) proving user intent.
为什么? 浏览器会在跨站请求中自动发送Cookie。攻击者可以自动触发这一行为。安全需要客户端明确提交(头/参数)以证明用户意图。
If the value matches at server side, the server accepts it as a legitimate request and if they don't, it rejects the request.
如果服务器端值匹配,服务器将其接受为合法请求;如果不匹配,则拒绝请求。
Since an attacker is unable to access the cookie value during a cross-site request, they cannot include a matching value in the hidden form value or as a request parameter/header.
由于攻击者无法在跨站请求期间访问Cookie值,因此他们无法在隐藏表单值或请求参数/头中包含匹配的值。
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 details these vulnerabilities. Therefore, always prefer the Signed Double-Submit Cookie pattern with session-bound HMAC tokens to mitigate these threats.
尽管简单的双重提交Cookie方法简单且可扩展,但它仍然容易受到Cookie注入攻击,尤其是在攻击者控制子域或允许他们种植或覆盖Cookie的网络环境中。例如,攻击者控制的子域(例如,通过DNS劫持)可以注入匹配的Cookie,从而伪造有效的请求令牌。此资源详细说明了这些漏洞。因此,始终优先选择带有会话绑定HMAC令牌的_签名双重提交Cookie_模式以减轻这些威胁。
Fetch Metadata headers / 获取元数据头部¶
Fetch Metadata request headers provide extra information about the context from which an HTTP request was made. 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 for details.
获取元数据请求头部提供了关于HTTP请求上下文的额外信息。服务器可以使用这些头部——最重要的是Sec-Fetch-Site——作为一个轻量级且可靠的机制来阻止明显的跨站请求。有关详细信息,请参阅获取元数据规范。
Because some legacy browsers do not send Sec-Fetch-* headers, a fallback to standard origin verification headers is a mandatory requirement for any Fetch Metadata implementation. Sec-Fetch-* is supported in all major browsers since March 2023.
由于一些旧版浏览器不发送Sec-Fetch-*头部,对于任何获取元数据实现来说,回退到标准源验证头部是一个强制性的要求。Sec-Fetch-*自2023年3月起在所有主流浏览器中得到支持。
The Fetch Metadata request headers are:
获取元数据请求头部包括:
- Sec-Fetch-Site — the primary signal for CSRF protection. It indicates the relationship between the request initiator’s origin and its target's origin:
same-origin,same-site,cross-site, ornone.
中文:Sec-Fetch-Site——CSRF保护的主要信号。它表示请求发起者的源与其目标源之间的关系:same-origin、same-site、cross-site或none。 - Sec-Fetch-Mode, Sec-Fetch-Dest, Sec-Fetch-User — additional headers that provide context about the request (such as the request mode, destination type, or whether it was triggered by a user navigation). More details are available in the MDN documentation.
中文:Sec-Fetch-Mode, Sec-Fetch-Dest, Sec-Fetch-User — 提供关于请求上下文(如请求模式、目标类型或是否由用户导航触发)的额外头信息。更多详情请参阅MDN文档。
If any of the 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 or double-submit patterns — which require additional client/server coordination and are difficult to implement correctly — Fetch Metadata checks are much more straightforward. 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.
与同步令牌或双重提交模式——这些模式需要额外的客户端/服务器协调且难以正确实现——相比,Fetch Metadata检查要简单得多。它们通常只需要少量服务器端逻辑(检查Sec-Fetch-Site,可选地使用Sec-Fetch-Mode/Sec-Fetch-Dest进行细化)且无需客户端更改。这种简单性降低了复杂性,使得这种方法对许多应用程序都具有吸引力。
Browser compatibility / 浏览器兼容性¶
Fetch Metadata request headers are supported in all modern browsers on both desktop and mobile (Chrome, Edge, Firefox, Safari 16.4+, and even in webviews on both iOS and Android), with over 98% global coverage. For compatibility detail, see the browser support table.
Fetch Metadata 请求头在所有现代桌面和移动浏览器(Chrome、Edge、Firefox、Safari 16.4+ 以及 iOS 和 Android 的 Webview)中均受支持,全球覆盖率达到 超过 98%。有关兼容性详细信息,请参阅 浏览器支持表。
For the rare cases of outdated or embedded browsers that lack Sec-Fetch-* support, a fallback to standard origin verification should provide the required coverage. If this is acceptable for your project, consider prompting users to update their browsers, as they are running on outdated and potentially insecure versions.
对于少数过时或嵌入的浏览器,它们可能缺少 Sec-Fetch-* 支持,此时应回退到 标准源验证 以提供所需的覆盖。如果这对您的项目来说是可接受的,考虑提示用户更新他们的浏览器,因为他们正在运行过时且可能不安全的版本。
How to treat Fetch Metadata headers on the server-side / 如何在服务器端处理 Fetch Metadata 头部¶
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's needs (for example, allowing top-level navigation requests or permitting specific Dest values for resource endpoints).
Policy (high level)
Sec-Fetch-Site 是用于阻止 CSRF 类跨源请求最有用的 Fetch Metadata 头部,应作为基于 Fetch-Metadata 的策略中的主要信号。使用其他 Fetch Metadata 头部(Sec-Fetch-Mode、Sec-Fetch-Dest、Sec-Fetch-User)来进一步细化或定制策略以满足您应用程序的需求(例如,允许顶级导航请求或允许资源端点的特定 Dest 值)。策略(高级)
- If
Sec-Fetch-Siteis present
中文:如果存在Sec-Fetch-Site
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.
1.1. 将跨站视为不受信任的状态更改操作。默认情况下,当 Sec-Fetch-Site: cross-site 时拒绝非安全方法(POST / PUT / PATCH / DELETE)。
const SAFE_METHODS = 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_METHODS.has(req.method)) {
return false; // forbid this request
}
1.2 If your application relies on safe HTTP methods (GET, HEAD, or OPTIONS) for state‑changing actions, you should explicitly reflect that in your policy – e.g., by requiring a Fetch‑Metadata header review for requests to those endpoints. This can be enforced with a policy rule like:
1.2. 如果您的应用程序依赖于 安全 HTTP 方法(GET、HEAD 或 OPTIONS)进行状态更改操作,您应在策略中明确反映这一点——例如,通过要求对这些端点的请求进行 Fetch-Metadata 头部审查。这可以通过以下策略规则强制执行:
const SAFE_METHODS = new Set(['GET','HEAD','OPTIONS']);
const SENSITIVE_ENDPOINTS = new Set([
'/user/profile',
'/account/details',
]);
const site = req.get('Sec-Fetch-Site');
const path = req.path;
// Block if cross-site + unsafe method OR cross-site + sensitive endpoint
if (site === 'cross-site' && (!SAFE_METHODS.has(req.method) || SENSITIVE_ENDPOINTS.has(path))) {
return false; // forbid this request
}
1.3. 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).
1.3. 允许 same-origin。如果您的威胁模型信任兄弟子域,则将 same-site 视为允许;否则,对 same-site 采取保守处理(例如,要求额外的验证)。
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_METHODS.has(req.method)) {
return false; // treat same-site as untrusted for state-changing methods
}
return true;
}
1.4. Allow none for user-driven top-level navigations (bookmarks, typed URLs, explicit form submits) where appropriate.
1.4. 在适当的情况下,对于用户驱动的顶级导航(书签、输入的URL、显式表单提交)允许无限制。
-
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 (standard origin verification, CSRF tokens, and/or require additional validation).
中文:如果Sec-Fetch-*头信息不存在:根据风险和兼容性要求选择一个回退方案:2.1. 安全模式(推荐用于敏感端点):将不存在视为未知并阻止请求。2.2. 兼容模式(优先考虑兼容性):回退到(标准源验证、CSRF令牌和/或需要额外的验证)。 -
Additional 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.
3.1. 为了确保您的站点仍可以从其他站点链接,您必须允许简单的(HTTP GET)顶级导航。
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.
3.2. 白名单显式跨源请求。如果某些端点有意接受跨源请求(CORS JSON API,第三方集成,webhooks),则明确豁免这些端点从全局Sec-Fetch拒绝策略,并使用适当的CORS配置、身份验证和日志记录来保护它们。
Requirements / 要求¶
- Your application must be served over trustworthy URLs. Fetch Metadata request headers are only sent to potentially trustworthy URLs. In practice, this includes
https,wss,file, andlocalhost(including127.0.0.0/8and::1/128). See the W3C Secure Contexts spec for full details.
中文:您的应用程序必须通过可信赖的URL提供服务。Fetch Metadata请求头信息仅发送到可能可信赖的URL。在实践中,这包括https、wss、file和localhost(包括127.0.0.0/8和::1/128)。有关完整详情,请参阅W3C安全上下文规范。 - HTTPS must be enforced across the entire application. This ensures consistent inclusion of Fetch Metadata headers. Enabling HTTP Strict Transport Security (HSTS) helps achieve this by automatically upgrading all HTTP requests to HTTPS.
中文:HTTPS必须在整个应用程序中强制执行。这确保了Fetch Metadata头信息的一致性。启用HTTP严格传输安全(HSTS)有助于实现这一点,因为它会自动将所有HTTP请求升级到HTTPS。 - Safe HTTP methods) (安全的HTTP方法) should not be used for state-changing requests.
Concerns / 关注点¶
- Prerender/prefetch and other speculative navigation may send
Sec-Fetch-*values that don’t match the final navigation, and browser-initiated flows (e.g., PaymentRequest) could generate requests without predictable fetch-metadata headers. These behaviors are still being refined, so header propagation isn’t fully stable across all navigation types.
中文:Prerender/prefetch和其他投机性导航可能会发送与最终导航不匹配的Sec-Fetch-*值,并且浏览器发起的流程(例如,支付请求)可能会生成没有可预测的fetch-metadata头信息的请求。这些行为仍在完善中,因此头信息传播在所有导航类型中并不完全稳定。 - Intermediaries (proxies, gateways, load balancers) may remove or modify
OriginandSec-*headers — whether due to privacy filters, network optimizations, or simple misconfiguration — which can break fetch-metadata-based protections. This kind of header stripping is problematic, but common.
中文:中间件(代理、网关、负载均衡器)可能会移除或修改Origin和Sec-*头部——无论是由于隐私过滤器、网络优化还是简单的配置错误——这可能会破坏基于获取元数据的保护。这种头部剥离是有问题的,但很常见。
Rollout & testing recommendations / 部署与测试建议¶
- Include an appropriate
Varyheader, in order to ensure that caches handle the response appropriately. For example,Vary: Sec-Fetch-Site, Origin. See more Fetch Metadata specification.- Note that the
Varyheader does not impact CSRF defenses in any way. It is a response header, so it is applied after the server has already made its allow/deny decision based on CSRF protections. Its purpose is operational rather than defensive. - If the server responds differently based on HTTP headers (e.g.,
Sec-Fetch-Site,Origin), caches must vary on those headers. Without this, CDNs or proxies may reuse a response generated for a different context, causing broken behavior or contributing to cache-poisoning scenarios. Adding the appropriateVaryheader ensures caches keep these responses separate.
中文:如果服务器根据HTTP头部(例如,Sec-Fetch-Site、Origin)响应不同,缓存必须在那些头部上变化。没有这个,CDN或代理可能会重用为不同上下文生成的响应,导致行为损坏或导致缓存中毒场景。添加适当的Vary头部确保缓存将这些响应分开。
- Note that the
- 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.
中文:监控UA覆盖率。跟踪哪些用户代理包括Sec-Fetch-*,哪些不包括;确保您的回退逻辑覆盖了缺失头部的情况。使用指标来决定何时执行更严格的政策。 - 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 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.
当使用 <form> 标签提交数据时,它会发送一个 “简单”请求,浏览器不会将其指定为“需要预检”。这些“简单”请求引入了 CSRF 的风险,因为浏览器允许它们发送到任何来源。如果您的应用程序在客户端的任何位置使用 <form> 标签提交数据,您仍然需要使用本文件中描述的替代方法(如令牌)来保护它们。
Caveat: Should a browser bug allow custom HTTP headers, or not enforce preflight on non-simple content types, it could compromise your security. Although unlikely, it is prudent to consider this in your threat model. Implementing CSRF tokens adds additional layer of defense and gives developers more control over security of the application.
Disallowing simple content types / 禁止简单内容类型¶
For a request to be deemed simple, it must have one of the following content types - application/x-www-form-urlencoded, multipart/form-data or text/plain. Many modern web applications use JSON APIs so would naturally require CORS, however they may accept text/plain which would be vulnerable to CSRF. Therefore a simple mitigation is for the server or API to disallow these simple content types.
要被视为简单请求,它必须具有以下内容类型之一 - application/x-www-form-urlencoded、multipart/form-data 或 text/plain。许多现代 Web 应用程序使用 JSON API,因此自然需要 CORS,但它们可能接受 text/plain,这将容易受到 CSRF 的攻击。因此,一种简单的缓解措施是服务器或 API 禁止这些简单内容类型。
Employing Custom Request Headers for AJAX/API¶
Both the synchronizer token and the double-submit cookie are used to prevent forgery of form data, but they can be tricky to implement and degrade usability. Many modern web applications do not use <form> tags to submit data. A user-friendly defense that is particularly well suited for AJAX or API endpoints is the use of a custom request header. No token is needed for this approach.
同步令牌和双重提交cookie都用于防止表单数据的伪造,但它们的实现可能很复杂,并且会降低可用性。许多现代 Web 应用程序不使用 <form> 标签提交数据。对于 AJAX 或 API 端点,使用 自定义请求头部 是一种用户友好的防御措施。这种方法不需要令牌。
In this pattern, the client appends a custom header to requests that require CSRF protection. The header can be any arbitrary key-value pair, as long as it does not conflict with existing headers.
在这个模式中,客户端将自定义头部添加到需要 CSRF 保护的请求中。该头部可以是任何任意的键值对,只要它不与现有头部冲突。
X-CSRF-Token: RANDOM-TOKEN-VALUE
Many popular frameworks use standardized header names for CSRF protection:
许多流行的框架使用标准化的头部名称来提供 CSRF 保护:
X-CSRF-Token- Ruby on Rails, Laravel, DjangoX-XSRF-Token- AngularJSCSRF-Token- Express.js (csurf middleware)
中文:CSRF-Token- Express.js (csurf 中间件)X-CSRFToken- Django
While any arbitrary header name will work, using one of these standard names can improve compatibility with existing tools and developer expectations.
虽然任何任意的头名称都可以工作,但使用这些标准名称可以提高与现有工具和开发者预期的兼容性。
When handling the request, the API checks for the existence of this header. If the header does not exist, the backend rejects the request as potential forgery. This approach has several advantages:
在处理请求时,API 会检查该头是否存在。如果头不存在,后端会拒绝该请求,视为潜在的伪造。这种方法具有几个优点:
- UI changes are not required
中文:不需要 UI 变更 - no server state is introduced to track tokens
中文:不会引入服务器状态来跟踪令牌
This defense relies on the CORS preflight mechanism which sends an OPTIONS request to verify CORS compliance with the destination server. All modern browsers designate requests with custom headers as "to be preflighted". When the API verifies that the custom header is there, you know that the request must have been preflighted if it came from a browser.
此防御依赖于 CORS 预检机制,该机制通过发送一个 OPTIONS 请求来验证与目标服务器的 CORS 兼容性。所有现代浏览器都将带有自定义头的请求标记为“需要预检”。当 API 验证自定义头存在时,如果你知道请求来自浏览器,那么可以确定该请求已经进行了预检。
Custom Headers and CORS / 自定义头和 CORS¶
Cookies are not set on cross-origin requests (CORS) by default. To enable cookies on an API, you will set Access-Control-Allow-Credentials=true. The browser will reject any response that includes Access-Control-Allow-Origin=* if credentials are allowed. To allow CORS requests, but protect against CSRF, you need to make sure the server only allows a few select origins that you definitively control via the Access-Control-Allow-Origin header. Any cross-origin request from an allowed domain will be able to set custom headers.
默认情况下,跨源请求(CORS)不会设置 Cookie。要在一个 API 中启用 Cookie,你需要设置 Access-Control-Allow-Credentials=true。如果允许凭证,浏览器将拒绝任何包含 Access-Control-Allow-Origin=* 的响应。为了允许 CORS 请求但防止 CSRF,你需要确保服务器只允许一些你明确控制的选定的源通过 Access-Control-Allow-Origin 头。任何来自允许域的跨源请求都将能够设置自定义头。
As an example, you might configure your backend to allow CORS with cookies from http://www.yoursite.com and http://mobile.yoursite.com, so that the only possible preflight responses are:
例如,你可能配置你的后端以允许来自 http://www.yoursite.com 和 http://mobile.yoursite.com 的 CORS 和 Cookie,这样唯一的可能预检响应是:
Access-Control-Allow-Origin=http://mobile.yoursite.com
Access-Control-Allow-Credentials=true
or
或者
Access-Control-Allow-Origin=http://www.yoursite.com
Access-Control-Allow-Credentials=true
A less secure configuration would be to configure your backend server to allow CORS from all subdomains of your site using a regular expression. If an attacker is able to take over a subdomain (not uncommon with cloud services) your CORS configuration would allow them to bypass the same origin policy and forge a request with your custom header.
一个不太安全的配置是将你的后端服务器配置为使用正则表达式允许来自你网站所有子域的 CORS。如果攻击者能够 接管子域(在云服务中并不罕见),你的 CORS 配置将允许他们绕过同源策略并伪造带有你自定义头的请求。
Dealing with Client-Side CSRF Attacks (IMPORTANT) / 处理客户端 CSRF 攻击(重要)¶
Client-side CSRF) (客户端 CSRF) is a new variant of CSRF attacks where the attacker tricks the client-side JavaScript code to send a forged HTTP request to a vulnerable target site by manipulating the program's input parameters. Client-side CSRF originates when the JavaScript program uses attacker-controlled inputs, such as the URL, for the generation of asynchronous HTTP requests.
Note: These variants of CSRF are particularly important as they can bypass some of the common anti-CSRF countermeasures like token-based mitigations and SameSite cookies. For example, when synchronizer tokens or custom HTTP request headers are used, the JavaScript program will include them in the asynchronous requests. Also, web browsers will include cookies in same-site request contexts initiated by JavaScript programs, circumventing the SameSite cookie policies.
Client-Side vs. Classical CSRF: In the classical CSRF model, the server-side program is the most vulnerable component, because it cannot distinguish whether the incoming authenticated request was performed intentionally, also known as the confused deputy problem. In the client-side CSR model, the most vulnerable component is the client-side JavaScript program because an attacker can use it to generate arbitrary asynchronous requests by manipulating the request endpoint and/or its parameters. Client-side CSRF is due to an input validation problem and it reintroduces the confused deputy flaw, that is, the server-side won't, again, be able to distinguish if the request was performed intentionally or not.
For more information about client-side CSRF vulnerabilities, see Sections 2 and 5 of this paper, the CSRF chapter of the SameSite wiki, and this post by the Meta Bug Bounty Program.
Client-side CSRF Example¶
The following code snippet demonstrates a simple example of a client-side CSRF vulnerability.
<script type="text/javascript">
const csrf_token = document.querySelector("meta[name='csrf-token']").getAttribute("content");
const ajaxLoad = () => {
// process the URL hash fragment
const hashFragment = window.location.hash.slice(1);
// hash fragment should be of the format: /^(get|post);(.*)$/
// e.g., https://site.com/index/#post;/profile
if (hashFragment.length > 0 && hashFragment.includes(';')) {
const params = hashFragment.match(/^(get|post);(.*)$/);
if (params && params.length) {
const requestMethod = params[1];
const requestEndpoint = params[2];
fetch(requestEndpoint, {
method: requestMethod,
headers: {
'X-CSRF-Token': csrf_token,
// [...]
},
// [...]
})
.then(response => { /* [...] */ })
.catch(error => console.error('Request failed:', error));
}
}
};
// trigger the async request on page load - better practice is to use event listeners
window.addEventListener('DOMContentLoaded', ajaxLoad);
</script>
Vulnerability: In this snippet, the program invokes a function ajaxLoad() upon the page load, which is responsible for loading various webpage elements. The function reads the value of the URL hash fragment (line 4), and extracts two pieces of information from it (i.e., request method and endpoint) to generate an asynchronous HTTP request (lines 11-13). The vulnerability occurs in lines 15-22, when the JavaScript program uses URL fragments to obtain the server-side endpoint for the asynchronous HTTP request (line 15) and the request method. However, both inputs can be controlled by web attackers, who can pick the value of their choosing, and craft a malicious URL containing the attack payload.
Attack: Usually, attackers share a malicious URL with the victim (through elements such as spear-phishing emails) and because the malicious URL appears to be from an honest, reputable (but vulnerable) website, the user often clicks on it. Alternatively, the attackers can create an attack page to abuse browser APIs (e.g., the window.open() API) and trick the vulnerable JavaScript of the target page to send the HTTP request, which closely resembles the attack model of the classical CSRF attacks.
For more examples of client-side CSRF, see this post by the Meta Bug Bounty Program and this USENIX Security paper.
Client-side CSRF Mitigation Techniques / 客户端CSRF缓解技术¶
Independent Requests: Client-side CSRF can be prevented when asynchronous requests cannot be generated via attacker controllable inputs, such as the URL, window name, document referrer, and postMessages, to name only a few examples.
独立请求:当无法通过攻击者可控的输入生成异步请求时,可以防止客户端CSRF,例如URL、窗口名称、文档来源和postMessages,仅举几个例子。
Input Validation: Achieving complete isolation between inputs and request parameters may not always be possible depending on the context and functionality. In these cases, input validation checks has to be implemented. These checks should strictly assess the format and choice of the values of the request parameters and decide whether they can only be used in non-state-changing operations (e.g., only allow GET requests and endpoints starting with a predefined prefix).
输入验证:根据上下文和功能,实现输入和请求参数之间的完全隔离可能并不总是可能的。在这些情况下,必须实现输入验证检查。这些检查应严格评估请求参数值的格式和选择,并决定它们是否只能用于非状态改变的操作(例如,仅允许GET请求和以预定义前缀开始的端点)。
Predefined Request Data: Another mitigation technique is to store a list of predefined, safe request data in the JavaScript code (e.g., combinations of endpoints, request methods and other parameters that are safe to be replayed). The program can then use a switch parameter in the URL fragment to decide which entry of the list should each JavaScript function use.
预定义请求数据:另一种缓解技术是在JavaScript代码中存储预定义的安全请求数据列表(例如,端点、请求方法和其他安全重放的参数组合)。然后程序可以使用URL片段中的switch参数来决定每个JavaScript函数应使用列表中的哪个条目。
Defense In Depth Techniques / 多层次防御技术¶
SameSite (Cookie Attribute) / SameSite(Cookie属性)¶
SameSite is a cookie attribute that controls whether browsers send cookies with cross-site requests. It is defined in RFC6265bis and takes one of three values:
SameSite是一个控制浏览器是否在跨站请求中发送cookie的cookie属性。它在RFC6265bis中定义,并取三个值之一:
Strictprevents cookies from being sent with cross-site requests, including top-level navigations from external links. This can disrupt legitimate navigation into an application.
中文:Strict阻止cookie与跨站请求一起发送,包括来自外部链接的顶级导航。这可能会破坏对应用程序的合法导航。Laxsends cookies on cross-site top-level navigations that use safe HTTP methods, such as following a link, and withholds them from other cross-site requests. This balances security and usability.
中文:Lax模式在跨站顶级导航中使用安全的 HTTP 方法(如跟随链接)时发送 cookies,而对于其他跨站请求则保留它们。这平衡了安全性和可用性。Nonepermits cookies in cross-site contexts and requires theSecureattribute.
中文:None模式允许在跨站上下文中使用 cookies,并要求设置Secure属性。
Example:
示例:
Set-Cookie: JSESSIONID=xxxxx; SameSite=Strict; Secure
Set-Cookie: JSESSIONID=xxxxx; SameSite=Lax; Secure
Limitations of SameSite / SameSite 属性的限制¶
SameSite is useful as a defense-in-depth control but it does not replace a proper CSRF defense in most deployments. Treat the following as known gaps when reasoning about how much protection it actually provides:
SameSite 属性作为多层次防御控制是有用的,但它并不能替代大多数部署中的正确 CSRF 防御。在考虑它实际提供的保护程度时,以下情况应被视为已知漏洞:
Laxonly blocks unsafe methods. The defaultLaxbehavior still allows the cookie on top-level navigations that use safe methods (GET,HEAD,OPTIONS,TRACE). If any state-changing operation in the application is reachable via aGETrequest,SameSite=Laxwill not stop it. This is the single most common waySameSite-based defenses fail in practice. Review everyGETendpoint and ensure that none of them mutate server-side state.SameSiteis scoped to the registrable domain, not the origin. A cookie set onapp.example.comwith anySameSitevalue is still considered "same-site" when the request originates fromanything.example.com. If your application shares a registrable domain with content you do not fully control (multi-tenant SaaS on a shared parent domain, subdomain-hosted user content, legacy subdomains, an acquired product running on the same parent domain, third-party services on subdomains), a vulnerability or malicious actor on any of those sibling hosts can issue requests that the browser will treat as same-site. This also amplifies the impact of subdomain takeovers: an attacker who claims a dangling subdomain can issue requests that yourSameSite-protected cookies will accompany.
中文:SameSite属性的作用域是注册域,而不是源。 在app.example.com上设置的任何SameSite值,当请求来自anything.example.com时,仍然被视为 "同一站"。如果你的应用程序与你不完全控制的内容共享注册域(如共享父域上的多租户 SaaS、子域托管用户内容、遗留子域、在同一父域上运行的收购产品、子域上的第三方服务),那么任何这些同级主机上的漏洞或恶意行为者都可以发出浏览器将其视为同一站的请求。这也放大了 子域接管 的影响:声称悬挂子域的攻击者可以发出你的SameSite受保护的 cookies 将伴随的请求。- Top-level navigation and window-opening tricks. An attacker who can get a victim to perform a top-level navigation or open a new window targeting your site (including through prerendering hints,
window.open, or clicking a crafted link) can generate a request the browser treats as same-site.SameSite=Strictblocks most of these at the cost of breaking legitimate cross-site links into the app.
中文:顶级导航和窗口打开技巧。 能够使受害者执行顶级导航或打开针对你的网站的新窗口的攻击者(包括通过预渲染提示、window.open或点击精心制作的链接)可以生成浏览器将其视为同一站的请求。SameSite=Strict会阻止其中大部分,但代价是破坏了合法的跨站链接到应用。 - Browser coverage is not universal. While current mainstream browsers enforce
SameSite=Laxby default, users on older browsers, embedded browsers, or non-mainstream clients may receive cookies that behave as if noSameSitevalue were set. Do not assume all traffic enjoys the protection.
中文:浏览器覆盖面不全面。 虽然当前主流浏览器默认强制执行SameSite=Lax,但使用旧浏览器、嵌入式浏览器或非主流客户端的用户可能会收到行为类似于未设置SameSite值的 cookies。不要假设所有流量都享有保护。 - Client-side CSRF is unaffected.
SameSiteoperates on cross-site requests. It does not protect against client-side CSRF (see the earlier section on Dealing with Client-Side CSRF Attacks) where malicious input causes same-origin JavaScript in your own application to issue a state-changing request.
中文:客户端CSRF不受影响。SameSite作用于跨站请求。它不能防止客户端CSRF(参见之前关于_处理客户端CSRF攻击_的部分),恶意输入会导致您的应用程序中的同源JavaScript发出状态更改请求。
When SameSite May Be Sufficient On Its Own / 当SameSite单独足够时¶
In narrow deployments SameSite alone can provide a reasonable CSRF defense, provided every one of the following holds:
在有限的部署中,SameSite本身可以提供合理的CSRF防御,前提是以下所有条件都成立:
- The application does not share a registrable domain with any host, subdomain, or service you do not fully control.
中文:应用程序不与任何您不完全控制的域名、子域名或服务共享注册域名。 - No
GET(or other safe-method) endpoint in the application performs a state-changing action. All state changes requirePOST,PUT,PATCH, orDELETE.
中文:应用程序中没有GET(或其他安全方法)端点执行状态更改操作。所有状态更改都需要POST、PUT、PATCH或DELETE。 - The session cookie is set with
SameSite=Strict, orSameSite=Laxcombined with the__Host-prefix and a strict audit of everyGEThandler.
中文:会话cookie设置为SameSite=Strict,或SameSite=Lax结合__Host-前缀以及每个GET处理器的严格审计。 - Origin or Referer verification (see below) is in place for defense in depth on state-changing endpoints.
中文:Origin或Referer验证(见下文)已实施,以在状态更改端点上提供深度防御。 - You are comfortable excluding users whose browsers do not enforce
SameSite, or you accept the residual risk that those users face.
中文:您愿意排除那些浏览器不强制执行SameSite的用户,或者您接受这些用户面临的风险。
For any application that does not meet all of the above, SameSite should be treated as a defense-in-depth layer and combined with a CSRF token or a double-submit pattern rather than relied on alone.
对于不符合上述所有条件的任何应用程序,SameSite 应被视为一种多层次防御措施,并结合 CSRF 令牌或双重提交模式,而不是单独依赖它。
Using Standard Headers to Verify Origin / 使用标准头部验证来源¶
There are two steps to this mitigation method, both of which examine an HTTP request header value:
此缓解方法有两个步骤,这两个步骤都检查 HTTP 请求头部的值:
- Determine the origin that the request is coming from (source origin). Can be done via Origin or Referer headers.
中文:确定请求的来源(源来源)。可以通过 Origin 或 Referer 头部完成。 - Determining the origin that the request is going to (target origin).
中文:确定请求将要到达的来源(目标来源)。
At server-side, we verify if both of them match. If they do, we accept the request as legitimate (meaning it's the same origin request) and if they don't, we discard the request (meaning that the request originated from cross-domain). Reliability on these headers comes from the fact that they cannot be altered programmatically as they fall under forbidden headers list, meaning that only the browser can set them.
在服务器端,我们验证这两个来源是否匹配。如果它们匹配,我们接受请求为合法(意味着它是同源请求)并且如果它们不匹配,我们丢弃请求(意味着请求来自跨域)。这些头部的可靠性来自于它们不能被程序更改,因为它们属于禁止头部列表,这意味着只有浏览器可以设置它们。
Identifying Source Origin (via Origin/Referer Header)¶
Checking the Origin Header / 检查 Origin 头部¶
If the Origin header is present, verify that its value matches the target origin. HTTPS alone does not guarantee that the header is present; the Fetch Standard conditions its inclusion on the request context and method.
如果存在 Origin 头部,验证其值是否与目标来源匹配。仅 HTTPS 不能保证头部存在;Fetch 标准将头部的包含条件设置为请求上下文和方法。
Checking the Referer Header if Origin Header Is Not Present / 检查 Referer 头部(当 Origin 头部不存在时)¶
If the Origin header is not present, verify that the hostname in the Referer header matches the target origin. This method of CSRF mitigation is also commonly used with unauthenticated requests, such as requests made prior to establishing a session state, which is required to keep track of a synchronization token.
如果 Origin 头部不存在,请验证 Referer 头部中的主机名是否与目标源匹配。这种 CSRF 防御方法也常用于未经身份验证的请求,例如在建立会话状态之前发出的请求,这是跟踪同步令牌所必需的。
In both cases, make sure the target origin check is strong. For example, if your site is example.org make sure example.org.attacker.com does not pass your origin check (i.e, match through the trailing / after the origin to make sure you are matching against the entire origin).
在这两种情况下,请确保目标源检查是严格的。例如,如果你的网站是 example.org,请确保 example.org.attacker.com 不能通过你的源检查(即,通过源后面的斜杠来匹配整个源)。
If neither header establishes a trusted source origin, reject the protected state-changing request unless another configured CSRF defense, such as a valid CSRF token, succeeds. Missing headers and Origin: null are not evidence of a same-origin request.
如果任一头部没有建立受信任的源,除非另一个配置的 CSRF 防御措施,如有效的 CSRF 令牌成功,否则拒绝受保护的更改状态请求。缺少头部和 Origin: null 并不是同源请求的证据。
Identifying the Target Origin / 识别目标源¶
Generally, it's not always easy to determine the target origin. You are not always able to simply grab the target origin (i.e., its hostname and port #) from the URL in the request, because the application server is frequently sitting behind one or more proxies. This means that the original URL can be different from the URL the app server actually receives. However, if your application server is directly accessed by its users, then using the origin in the URL is fine and you're all set.
通常,确定目标源并不总是容易的。你并不总是能够简单地从请求的 URL 中获取目标源(即,其主机名和端口 #),因为应用服务器通常位于一个或多个代理后面。这意味着原始 URL 可能与应用服务器实际接收到的 URL 不同。然而,如果你的应用服务器直接由用户访问,那么使用 URL 中的源是可行的,并且你已经设置好了。
If you are behind a proxy, there are a number of options to consider.
如果你位于代理后面,有几种选项可以考虑。
- Configure your application to simply know its target origin: Since it is your application, you can find its target origin and set that value in some server configuration entry. This would be the most secure approach as its defined server side, so it is a trusted value. However, this might be problematic to maintain if your application is deployed in many places, e.g., dev, test, QA, production, and possibly multiple production instances. Setting the correct value for each of these situations might be difficult, but if you can do it via some central configuration and provide your instances the ability to grab the value from it, that's great! (Note: Make sure the centralized configuration store is maintained securely because major part of your CSRF defense depends on it.)
中文:配置你的应用程序以简单地知道其目标源:由于它是你的应用程序,你可以找到其目标源并在某些服务器配置条目中设置该值。这将是最高安全的方法,因为它是定义在服务器端的,所以是一个可信的值。然而,如果你的应用程序部署在多个地方,例如开发、测试、QA、生产以及可能的多生产实例,这可能会有问题。为这些情况中的每一个设置正确的值可能很困难,但如果你可以通过某种中央配置来完成,并为你的实例提供从其中获取值的能力,那就太好了!(注意:请确保中央配置存储库得到安全维护,因为你的 CSRF 防御措施的大部分都依赖于它。) - Use the Host header value: If you want your application to find its own target so it doesn't have to be configured for each deployed instance, we recommend using the Host family of headers. The Host header is meant to contain the target origin of the request. But, if your app server is sitting behind a proxy, the Host header value is most likely changed by the proxy to the target origin of the URL behind the proxy, which is different than the original URL. This modified Host header origin won't match the source origin in the original Origin or Referer headers.
中文:使用Host头值: 如果您希望您的应用程序能够找到自己的目标,而无需为每个部署实例进行配置,我们建议使用Host头系列。Host头旨在包含请求的目标源。但是,如果您的应用服务器位于代理后面,Host头值很可能被代理修改为代理后面的URL的目标源,这与原始URL不同。这个修改后的Host头源不会与原始的Origin或Referer头中的源匹配。 - Use the X-Forwarded-Host header value only from trusted proxies: A reverse proxy can preserve the original host in this header. Configure the application to trust only known proxy connections, and ensure the trusted proxy removes or overwrites client-supplied forwarded headers, as described in the Express proxy guidance. Do not trust the header merely because it is present. Validate this configuration on every ingress path, including any direct access to the application server.
A target origin derived from proxy headers is trustworthy only when these proxy requirements are met; source-origin validation still requires the Origin or Referer header. Though these headers are included the majority of the time, there are few use cases where they are not included (most of them are for legitimate reasons to safeguard users privacy/to tune to browsers ecosystem).
只有当这些代理要求得到满足时,从代理头中派生的目标源才是可信的;源源验证仍然需要Origin或Referer头。尽管这些头大多数时候都会包含,但也有一些用例中它们不会被包含(其中大多数是为了保护用户隐私或调整浏览器生态系统而采取的合法措施)。
Cases where source-origin information is unavailable:
源源信息不可用的情况:
- Cross-origin redirects can produce
Origin: null) (跨源重定向可以产生Origin: null). - Opaque origins, including sandboxed iframes without
allow-same-origin, can also produceOrigin: null. An attacker can create such a context, so do not allowlist the literalnullvalue.
中文:不透明的源,包括没有allow-same-origin的沙箱iframe,也可以产生Origin: null。攻击者可以创建这样的上下文,因此不要将字面值null列入白名单。 - Header inclusion depends on request context and method. For example, cross-origin
GETorHEADrequests inno-corsmode can omitOrigin. Keep safe methods free of state changes.
中文:头包含取决于请求上下文和方法。例如,在no-cors模式下,跨源的GET或HEAD请求可以省略Origin。保持安全方法不涉及状态变化。 - Referer header is no exception. There are multiple use cases where referrer header is omitted as well (1, 2, 3, 4 and 5). Load balancers, proxies and embedded network devices are also well known to strip the referrer header due to privacy reasons in logging them.
Review legitimate requests that lack usable origin information and provide an explicit fallback, such as server-validated CSRF tokens. Do not bypass CSRF validation merely to preserve compatibility with missing or null source origins.
审查缺少可用源信息的合法请求,并提供显式的回退,例如服务器验证的CSRF令牌。不要仅仅为了与缺失或null源源保持兼容而绕过CSRF验证。
Using Cookies with Host Prefixes to Identify Origins / 使用带有Host前缀的Cookies来识别源¶
While the SameSite and Secure attributes mentioned earlier restrict the sending of already set cookies
and HttpOnly restricts the reading of a set cookie,
an attacker may still try to inject or overwrite otherwise secured cookies
(cf. session fixation attacks).
Using Cookie Prefixes for cookies with CSRF tokens extends security protections against this kind of attacks as well.
If cookies have __Host- prefixes e.g. Set-Cookie: __Host-token=RANDOM; path=/; Secure then each cookie:
虽然之前提到的 SameSite 和 Secure 属性限制了已设置 cookie 的发送,而 HttpOnly 属性限制了已设置 cookie 的读取,但攻击者仍然可能尝试注入或覆盖其他受保护的 cookie(参看会话固定攻击)。对于带有 CSRF 令牌的 cookie 使用 Cookie Prefixes 可以扩展对此类攻击的安全保护。如果 cookie 有 __Host- 前缀,例如 Set-Cookie: __Host-token=RANDOM; path=/; Secure,那么每个 cookie:
- Cannot be (over)written from another subdomain and
中文:不能被(覆盖)从另一个子域写入,并且 - cannot have a
Domainattribute.
中文:不能有Domain属性。 - Must have the path of
/. - Must be marked as Secure (i.e, cannot be sent over unencrypted HTTP).
中文:必须标记为 Secure(即不能通过未加密的 HTTP 发送)。
In addition to the __Host- prefix, the weaker __Secure- prefix is also supported by browser vendors.
It relaxes the restrictions on domain overwrites, i.e., they
除了 __Host- 前缀之外,浏览器厂商还支持较弱的 __Secure- 前缀。它放宽了对域覆盖的限制,即它们
- Can have
Domainattributes and
中文:可以有Domain属性,并且 - can be overwritten by subdomains.
中文:可以被子域覆盖。 - Can have a
Pathother than/.
中文:可以有一个除/之外的Path。
This relaxed variant can be used as an alternative to the "domain locked" __Host- prefix,
if authenticated users would need to visit different (sub-)domains.
In all other cases, using the __Host- prefix in addition to the SameSite attribute is recommended.
这种放宽的变体可以用作 "域锁定" 的 __Host- 前缀的替代,如果认证用户需要访问不同的(子)域。在其他所有情况下,建议使用 __Host- 前缀加上 SameSite 属性。
Cookie prefixes are supported by all major browsers.
Cookie前缀被所有主流浏览器支持。
See the Mozilla Developer Network and IETF Draft for further information about cookie prefixes.
查看Mozilla开发者网络和IETF草案以获取有关cookie前缀的更多信息。
User Interaction-Based CSRF Defense / 基于用户交互的CSRF防御¶
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.
虽然这里提到的所有技术都不需要用户交互,但有时让用户参与到交易中更容易或更合适,以防止未经授权的操作(通过CSRF或其他方式伪造)。以下是一些在正确实施时可以作为强大CSRF防御的技术示例。
- Re-Authentication mechanisms
中文:重新认证机制 - One-time Tokens
中文:一次性令牌
Do NOT use CAPTCHA because it is specifically designed to protect against bots. It is possible, and still valid in some implementations of CAPTCHA, to obtain proof of human interaction/presence from a different user session. Although this makes the CSRF exploit more complex, it does not protect against it.
不要使用CAPTCHA,因为它专门设计用来保护免受机器人的攻击。在某些CAPTCHA的实现中,从不同的用户会话中获取人类交互/存在的证据是可能的,并且仍然是有效的。尽管这使CSRF攻击变得更加复杂,但它并不能保护免受攻击。
While these are very strong CSRF defenses, it can create a significant impact on the user experience. As such, they would generally only be used for security critical operations (such as password changes, money transfers, etc.), alongside the other defenses discussed in this cheat sheet.
虽然这些是非常强大的CSRF防御措施,但它们可能会对用户体验产生重大影响。因此,它们通常只用于安全关键操作(如密码更改、资金转账等),并与其他在此备忘单中讨论的防御措施一起使用。
Possible CSRF Vulnerabilities in Login Forms / 登录表单中的潜在CSRF漏洞¶
Most developers tend to ignore CSRF vulnerabilities on login forms as they assume that CSRF would not be applicable on login forms because user is not authenticated at that stage, however this assumption is not always true. CSRF vulnerabilities can still occur on login forms where the user is not authenticated, but the impact and risk is different.
大多数开发者倾向于忽略登录表单中的CSRF漏洞,因为他们认为CSRF在这种情况下不适用,因为用户在该阶段尚未认证。然而,这种假设并不总是正确的。即使在用户未认证的情况下,登录表单也可能存在CSRF漏洞,但其影响和风险是不同的。
For example, if an attacker uses CSRF to assume an authenticated identity of a target victim on a shopping website using the attacker's account, and the victim then enters their credit card information, an attacker may be able to purchase items using the victim's stored card details. For more information about login CSRF and other risks, see section 3 of this paper.
例如,如果攻击者使用CSRF在购物网站上冒充目标受害者的认证身份(使用攻击者的账户),然后受害者输入他们的信用卡信息,攻击者可能能够使用受害者的存储卡详情购买商品。有关登录CSRF和其他风险的更多信息,请参阅这篇论文的第3节。
Login CSRF can be mitigated by creating pre-sessions (sessions before a user is authenticated) and including tokens in login form. You can use any of the techniques mentioned above to generate tokens. Remember that pre-sessions cannot be transitioned to real sessions once the user is authenticated - the session should be destroyed and a new one should be made to avoid session fixation attacks. This technique is described in Robust Defenses for Cross-Site Request Forgery section 4.1. Login CSRF can also be mitigated by including a custom request headers in AJAX request as described above.
可以通过创建预会话(用户认证之前的会话)并在登录表单中包含令牌来减轻登录CSRF。您可以使用上述任何技术生成令牌。请记住,一旦用户认证,预会话就不能转换为真实会话 - 应该销毁会话并创建一个新的会话,以避免会话固定攻击。该技术描述在Robust Defenses for Cross-Site Request Forgery的第4.1节。登录CSRF还可以通过在AJAX请求中包含自定义请求头(如上所述)来减轻。
JavaScript: Automatically Including CSRF Tokens as an AJAX Request Header / JavaScript:自动将CSRF令牌包含在AJAX请求头中¶
JavaScript clients can send CSRF tokens in a custom request header on protected state-changing requests. Prefer maintained framework or HTTP client integrations where available. Do not add CSRF tokens through global header defaults or by globally overriding XMLHttpRequest.prototype.open, because unrelated requests could receive the token.
JavaScript客户端可以在受保护的状态更改请求中通过自定义请求头发送CSRF令牌。如果可用,请优先使用维护良好的框架或HTTP客户端集成。不要通过全局头默认值或全局覆盖XMLHttpRequest.prototype.open来添加CSRF令牌,因为无关的请求可能会接收到令牌。
Client-side CSRF handling does not replace server-side token validation. Keep GET, HEAD, and OPTIONS free of state changes, and validate tokens on every protected state-changing request.
客户端的CSRF处理不能替代服务器端的令牌验证。保持GET、HEAD和OPTIONS无状态更改,并在每个受保护的状态更改请求中验证令牌。
For the synchronizer token pattern, the server can include the token in the page, such as in a <meta> element, for the client to read when constructing protected requests. Use your template's attribute encoder when rendering the value. Do not put tokens in URLs or logs.
对于同步令牌模式,服务器可以将令牌包含在页面中,例如在<meta>元素中,以便客户端在构造受保护请求时读取。在渲染值时使用您的模板的属性编码器。不要将令牌放在URL或日志中。
For a cookie-to-header integration, configure the server to issue and validate the token according to the Signed Double-Submit Cookie pattern. The token cookie must be readable by the client, while the authentication cookie should remain HttpOnly.
对于cookie到header的集成,配置服务器根据签名双提交cookie模式颁发和验证令牌。令牌cookie必须可由客户端读取,而认证cookie应保持HttpOnly状态。
Restrict token attachment to explicitly trusted application endpoints. Validate the resolved request URL, not just the HTTP method or a presumed baseURL. Cross-origin API integrations require an explicit list of trusted destinations and a matching server-side CSRF policy. CORS preflight does not keep a manually attached token secret from a destination that allows the request.
限制令牌附加到显式信任的应用程序端点。验证解析后的请求URL,而不仅仅是HTTP方法或假设的baseURL。跨源API集成需要显式信任的目的地列表和匹配的服务器端CSRF策略。CORS预检请求不会将手动附加的令牌秘密保留在允许请求的目的地。
Use the official documentation for client-specific behavior and configuration:
使用官方文档了解客户端特定的行为和配置:
- Angular
HttpClientXSRF protection) (AngularHttpClientXSRF保护) - Axios request configuration) (Axios请求配置)
Other JavaScript clients, including fetch, XMLHttpRequest, and jQuery, should follow the same trusted-destination restrictions and server-side validation requirements.
其他JavaScript客户端,包括fetch、XMLHttpRequest和jQuery,应遵循相同的信任目的地限制和服务器端验证要求。
References / 参考¶
- W3C: Fetch Metadata Request Headers) (W3C:Fetch元数据请求头)
- Barth, Jackson, and Mitchell: Robust Defenses for Cross-Site Request Forgery) (Barth, Jackson, and Mitchell:针对跨站请求伪造的强大防御)
- OWASP WSTG: Testing for Cross-Site Request Forgery) (OWASP WSTG: 测试跨站请求伪造)