本文目录导读:

Cookie校验本身并不能直接抵御CSRF攻击,反而可能是CSRF攻击利用的对象,但通过正确设置Cookie的特定属性,可以显著增强对CSRF的防御能力。
以下是详细解释和具体的防御策略:
核心问题:为什么Cookie校验本身防不住CSRF?
- 自动发送:浏览器在发送请求时,会自动携带目标域名的Cookie(包括Session ID),无论请求来自合法页面还是恶意页面。
- 攻击原理:攻击者诱导用户访问恶意网站B,B的页面构造一个向目标网站A的请求(如图片、表单),由于浏览器会自动带上A的Cookie,服务器端(A)通过Cookie校验以为这是用户本人的合法请求,从而执行恶意操作(如转账、改密)。
Cookie相关的关键防御属性(重点)
通过设置以下Cookie属性,可以从根本上限制Cookie被用于CSRF攻击:
SameSite 属性(最核心、最推荐)
这是现代浏览器提供的最直接、最有效的CSRF防御机制,设置Cookie的SameSite属性可以控制Cookie是否在跨站请求中发送。
SameSite=Strict:最严格,Cookie仅在同站请求(即顶级域名、协议、端口完全相同的请求)中发送,任何跨站请求(包括点击链接、通过表单提交到本站)都不会带上这个Cookie。缺点:用户体验可能受影响,比如从第三方网站点击链接跳转到本站,用户会未登录。SameSite=Lax:平衡安全与体验,Cookie在跨站导航(如点击<a>链接、<link>、<form method="GET">)中会发送,但不会在跨站的POST请求(表单提交)、图片、<script>、<iframe>等请求中发送。这是Chrome等浏览器的默认行为,能阻止绝大部分CSRF攻击。SameSite=None:允许跨站,Cookie会在所有跨站请求中发送,但必须同时设置Secure属性(仅HTTPS),这是需要跨站共享Cookie场景的选项,但此时需要搭配其他CSRF防御措施。
示例(Java Servlet):
Cookie sessionCookie = new Cookie("JSESSIONID", sessionId);
// 推荐使用Lax,兼顾安全和用户体验
sessionCookie.setSecure(true); // 仅HTTPS
sessionCookie.setHttpOnly(true);
sessionCookie.setAttribute("SameSite", "Lax"); // 或 "Strict"
HttpOnly 属性(间接防御)
- 作用:标记为
HttpOnly的Cookie无法通过JavaScript(如document.cookie)访问。 - 对CSRF的贡献:虽然不能阻止CSRF攻击本身(因为浏览器自动发Cookie),但它可以防止攻击者通过XSS漏洞窃取Cookie,从而阻断攻击者冒充用户进行后续操作,这是一种纵深防御。
Secure 属性(基本安全)
- 作用:Cookie仅在HTTPS连接中发送。
- 对CSRF的贡献:防止在不安全的HTTP连接中Cookie被中间人窃取或篡改。
其他与Cookie配合的CSRF防御策略(即使Cookie设置正确,仍推荐)
仅靠Cookie属性并不完美(例如SameSite=Lax无法防御GET请求的CSRF,但Strict又太严)。主动防御措施依然必要:
CSRF Token(最经典、可靠)
- 原理:服务器生成一个随机且不可预测的Token,嵌入到表单的隐藏字段(或请求头)中,并同时将这个Token存储在用户Session中,当用户提交请求时,服务器验证提交的Token与Session中的Token是否一致。
- 与Cookie的关系:Token不应该通过Cookie传递(否则会被自动带上,失去防CSRF的意义),通常Token存储在服务端Session中,通过请求体(表单)或自定义HTTP头传递。
- 实现:
- 服务端生成Token:
session.setAttribute("csrfToken", randomString) - 前端表单:
<input type="hidden" name="csrfToken" value="${csrfToken}"> - 服务端校验:
if (!request.getParameter("csrfToken").equals(session.getAttribute("csrfToken"))) { reject; }
- 服务端生成Token:
自定义请求头(适用于AJAX/API)
- 原理:CSRF攻击通常无法构造自定义HTTP头(因为浏览器同源策略限制了跨域发送自定义头,除非服务端允许)。
- 实现:在所有AJAX请求中添加一个自定义头(如
X-CSRF-Token),服务端验证该头是否存在且有效,很多框架(如Spring Security)默认支持此项。 - 注意:对于普通的表单
POST(不是AJAX),这个方法不适用。
双重提交Cookie(Double Submit Cookie)
- 原理:在Cookie中设置一个随机值,同时在前端请求中(如图片、表单)也提交这个随机值,服务器比较两者是否一致。
- 优点:无状态,无需在服务端存储Token。
- 缺点:如果Cookie被攻击者通过XSS或子域漏洞获取,防御会失效。不如CSRF Token可靠。
最佳防御组合
| 防御措施 | 核心思想 | 可靠性 | 推荐度 |
|---|---|---|---|
| SameSite Cookie | 让浏览器不发Cookie | 极高(配合Lax/Strict) |
必须设置 |
| CSRF Token | 验证请求来源的意图 | 极高 | 必须实施(尤其处理写操作时) |
| 自定义请求头 | 利用同源策略 | 高 | 强烈推荐(AJAX场景) |
| 确认Referer/Origin | 检查请求来源 | 中(可能被伪造或缺失) | 辅助手段 |
| 验证码/二次确认 | 人机交互验证 | 最高(但影响体验) | 高风险操作(如转账)时必用 |
最终建议:
- 基础层:始终设置
SameSite=Lax(或Strict) +HttpOnly+Secure属性。 - 核心层:对于所有非幂等(修改数据)的请求(POST/PUT/DELETE),强制使用CSRF Token。
- API层:对于REST API,使用自定义请求头(如
X-CSRF-Token)代替表单Token。 - 敏感操作:加验证码或二次确认(如密码、短信验证)。
单靠Cookie校验无法防御CSRF,但正确设置Cookie的SameSite属性是最强大、最优雅的防御基石,之后再配合CSRF Token等主动验证机制,才能构建真正安全的系统。