SameSite Cookie 属性是防御 CSRF 攻击的重要机制,它控制 Cookie 在跨站请求中的发送行为。
SameSite 属性概述
SameSite 是 Cookie 的一个属性,用于指示浏览器是否应该在跨站请求中发送该 Cookie。它有三个可选值:Strict、Lax 和 None。
属性值详解
1. SameSite=Strict
行为:
- 只在同站请求中发送 Cookie
- 跨站请求(包括导航)都不会发送 Cookie
适用场景:
- 银行、支付等高安全性应用
- 敏感操作(如转账、修改密码)
- 不需要跨站功能的应用
示例:
javascript// 设置 SameSite=Strict document.cookie = 'sessionid=abc123; SameSite=Strict; Secure; HttpOnly';
优点:
- 提供最强的 CSRF 防护
- 完全阻止跨站请求携带 Cookie
缺点:
- 用户从外部链接进入时需要重新登录
- 可能影响用户体验
2. SameSite=Lax(推荐)
行为:
- 允许某些跨站请求发送 Cookie
- 阻止大多数 CSRF 攻击
允许的跨站请求:
- 顶级导航(GET 请求)
- 链接跳转(
<a>标签) - 表单 GET 请求
阻止的跨站请求:
- POST 请求(表单提交)
- AJAX 请求
<iframe>、<img>、<script>等资源加载
示例:
javascript// 设置 SameSite=Lax document.cookie = 'sessionid=abc123; SameSite=Lax; Secure; HttpOnly';
适用场景:
- 大多数 Web 应用
- 需要外部链接跳转的应用
- 平衡安全性和用户体验
优点:
- 提供良好的 CSRF 防护
- 用户体验较好
- 现代浏览器默认值
缺点:
- 某些跨站 POST 请求可能受影响
- 需要确保应用兼容性
3. SameSite=None
行为:
- 允许所有跨站请求发送 Cookie
- 必须配合
Secure属性使用
示例:
javascript// 设置 SameSite=None document.cookie = 'sessionid=abc123; SameSite=None; Secure; HttpOnly';
适用场景:
- 需要跨站功能的应用
- 第三方登录(如 OAuth)
- 嵌入式内容
优点:
- 不影响现有跨站功能
- 兼容旧应用
缺点:
- 无法防御 CSRF 攻击
- 需要其他防护措施
浏览器支持情况
主流浏览器支持
- Chrome:51+ 版本支持,80+ 版本默认 Lax
- Firefox:60+ 版本支持
- Safari:12+ 版本支持
- Edge:79+ 版本支持
- Opera:39+ 版本支持
兼容性处理
javascript// 检测浏览器是否支持 SameSite function setCookie(name, value, days) { let expires = ''; if (days) { const date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); expires = '; expires=' + date.toUTCString(); } // 现代浏览器 let sameSite = '; SameSite=Lax'; // 旧版浏览器不支持 SameSite const cookieString = name + '=' + value + expires + sameSite + '; path=/; Secure; HttpOnly'; document.cookie = cookieString; }
同站与跨站的定义
同站(Same-Site)
- 相同的顶级域名(eTLD+1)
- 例如:
https://example.com和https://www.example.com是同站https://app.example.com和https://api.example.com是同站
跨站(Cross-Site)
- 不同的顶级域名
- 例如:
https://example.com和https://evil.com是跨站https://example.com和https://example.net是跨站
实际应用示例
1. 银行应用(Strict)
javascript// 高安全性要求 document.cookie = 'sessionid=abc123; SameSite=Strict; Secure; HttpOnly; Max-Age=3600';
2. 电商网站(Lax)
javascript// 平衡安全性和用户体验 document.cookie = 'sessionid=abc123; SameSite=Lax; Secure; HttpOnly; Max-Age=86400';
3. 第三方登录(None)
javascript// 需要跨站功能 document.cookie = 'oauth_token=xyz789; SameSite=None; Secure; HttpOnly; Max-Age=3600';
框架集成
Express.js
javascriptapp.use(session({ secret: 'your-secret', cookie: { secure: true, httpOnly: true, sameSite: 'lax' } }));
Spring Boot
java@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and() .csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); } }
Django
python# settings.py SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = 'Lax' CSRF_COOKIE_SECURE = True CSRF_COOKIE_HTTPONLY = True CSRF_COOKIE_SAMESITE = 'Lax'
常见问题
1. SameSite=None 不生效
原因:缺少 Secure 属性
解决:必须同时设置 Secure 属性
javascript// 错误 document.cookie = 'sessionid=abc123; SameSite=None'; // 正确 document.cookie = 'sessionid=abc123; SameSite=None; Secure';
2. 跨站 POST 请求失败
原因:SameSite=Lax 阻止了跨站 POST 请求 解决:
- 使用 SameSite=None(需要其他 CSRF 防护)
- 改用同站请求
- 使用 CSRF Token
3. 第三方登录失败
原因:SameSite 属性阻止了跨站 Cookie 解决:
- 为特定 Cookie 设置 SameSite=None
- 使用 OAuth 2.0 的授权码模式
- 使用 PostMessage 通信
最佳实践
1. 默认使用 SameSite=Lax
javascript// 大多数应用的最佳选择 document.cookie = 'sessionid=abc123; SameSite=Lax; Secure; HttpOnly';
2. 敏感操作使用 SameSite=Strict
javascript// 高安全性要求 document.cookie = 'admin_session=xyz789; SameSite=Strict; Secure; HttpOnly';
3. 避免 SameSite=None
javascript// 尽量避免,除非必要 // 如果必须使用,配合其他防护措施 document.cookie = 'third_party_token=abc123; SameSite=None; Secure; HttpOnly';
4. 配合其他防护措施
- CSRF Token
- Origin/Referer 验证
- 自定义请求头
5. 测试兼容性
- 在不同浏览器中测试
- 测试跨站场景
- 测试第三方集成
总结
SameSite Cookie 属性是防御 CSRF 攻击的有效手段。推荐使用 SameSite=Lax 作为默认配置,它在提供良好 CSRF 防护的同时保持良好的用户体验。对于高安全性要求的应用,可以使用 SameSite=Strict。尽量避免使用 SameSite=None,除非确实需要跨站功能,并配合其他防护措施。