本文目录导读:

- 核心防御:使用 CSRF Token(最广泛使用)
- 现代防御:SameSite Cookie 属性(浏览器原生支持)
- 验证 Referer 或 Origin 请求头(辅助防御)
- 增加二次验证(针对高风险操作)
- 前端辅助:禁止跨站提交(如 Fetch 设置 credentials)
- 最佳实践建议
CSRF(跨站请求伪造,Cross-Site Request Forgery)是一种常见的 Web 安全漏洞,攻击者利用用户已登录的身份,诱使用户在不知情的情况下向目标网站发送恶意请求(如修改密码、转账等)。
要有效规避 CSRF 攻击,通常从后端防御为主,前端辅助配合的原则出发,以下是目前业界最主流、最有效的几种规避方法:
核心防御:使用 CSRF Token(最广泛使用)
这是最经典且有效的防御方式。
- 原理:服务器在渲染页面或返回表单时,生成一个随机的、不可预测的 Token(令牌)并存储在 Session 中,当用户提交请求时,必须携带这个 Token,服务器验证 Token 是否匹配。
- 为什么能防御:攻击者构造的恶意页面无法获取到存储在用户浏览器中、属于目标网站的 Token(受同源策略限制)。
- 实现步骤:
- 用户访问页面时,服务器生成 Token 并嵌入表单的隐藏字段:
<input type="hidden" name="_csrf_token" value="随机字符串">。 - 用户提交请求时,后端比较请求中的 Token 与 Session 中的 Token 是否一致。
- 用户访问页面时,服务器生成 Token 并嵌入表单的隐藏字段:
- 注意:Token 不能通过 Cookie 传递(因为攻击者可以伪造 cookie 或利用某些漏洞),必须放在请求体(POST)或自定义请求头中。
现代防御:SameSite Cookie 属性(浏览器原生支持)
这是目前最省力、且非常有效的防御手段,现代浏览器(Chrome 80+, Firefox 60+, Safari 12+)已经原生支持。
- 原理:在设置 Cookie 时,添加
SameSite属性,告诉浏览器不要在跨站请求中发送该 Cookie。 - 三种模式:
Strict:最严格,任何跨站请求(例如从evil.com向bank.com发请求)都不会携带该 Cookie。缺点:用户从第三方网站点击链接跳转到bank.com时,也会丢失登录状态,体验较差。Lax(推荐):大部分场景下的最佳平衡,只允许部分“安全”的跨站请求携带 Cookie(如<a>链接、<link rel="prerender">、GET 表单提交)。不允许跨站 POST 请求、XMLHttpRequest、<img>、<script>等标签的请求携带 Cookie,这可以防御绝大多数 CSRF 攻击,又不影响正常的导航。None:关闭 SameSite 保护,但必须同时设置Secure(仅 HTTPS),否则浏览器会拒绝该 Cookie。
示例代码(Set-Cookie 响应头):
Set-Cookie: sessionid=abc123; SameSite=Lax; Secure; HttpOnly
验证 Referer 或 Origin 请求头(辅助防御)
- 原理:检查 HTTP 请求头中的
Referer(来源页面地址)或Origin(来源站点域名)是否合法。 - 怎么做:后端判断
Origin或Referer的值是否为你的网站域名(如https://www.yourbank.com)。 - 为什么是辅助:
Referer可能因用户隐私设置、浏览器扩展、HTTPS 降级等原因缺失或不完整。Origin更可靠,但在某些老版本浏览器或特定请求中可能不存在。- 不建议完全依赖这个,但作为“纵深防御”中的一层是很好的。
增加二次验证(针对高风险操作)
对于修改密码、转账、删除账号等高风险操作,强制要求用户进行二次验证。
- 方式:输入密码、输入短信验证码、使用 TOTP(基于时间的一次性密码,如 Google Authenticator)或扫码。
- 效果:即使攻击者构造了伪造请求,由于无法提供验证码,攻击无法生效。
前端辅助:禁止跨站提交(如 Fetch 设置 credentials)
现代前端开发中,使用 fetch 或 XMLHttpRequest 时,可以明确设置凭据的发送策略。
-
对于
fetch:默认情况下,fetch在跨域时不会发送 Cookie,除非你显式设置credentials: 'include',请检查你的前端代码,确保没有在主站之外随意附带 Cookie。// 默认安全:跨域不会发 Cookie fetch('https://api.yourbank.com/transfer'); // 显式设置时才发,需要后端配合 CORS(跨域资源共享) fetch('https://api.yourbank.com/transfer', { credentials: 'include' });
最佳实践建议
对于现代 Web 应用,推荐的防御组合是:
- 首要方案:后端设置
SameSite=Lax,这是最低成本、最基础且覆盖面最广的防御。 - 重要补充:关键接口(如登录、转账、支付)使用 CSRF Token,因为
SameSite=Lax无法防御基于 GET 链接的 CSRF(虽然 GET 请求不应产生副作用,但现实中存在)。 - 纵深防御:后端验证
Origin头。 - 超级安全:对高风险操作实施二次验证(密码/验证码)。
一个简单的检查清单:
- [ ] 所有涉及状态变更的 POST/PUT/DELETE 接口,是否验证了 CSRF Token?
- [ ] 登录 Session 的 Cookie 是否设置了
SameSite=Lax或SameSite=Strict? - [ ] 是否开启了
HttpOnly和Secure属性(防止 XSS 窃取 Cookie)? - [ ] 后端是否对
Origin头进行了白名单校验?
特别注意: 不要禁用 GET 请求的副作用,即使你自认为“不会”,也要确保 GET 请求永远不会导致数据修改,攻击者可以通过 <img>、<script> 等标签轻易构造 GET 请求。