news 2026/9/16 19:39:36

CSRF本质是浏览器信任机制的副产品

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CSRF本质是浏览器信任机制的副产品

1. CSRF不是“跨站请求伪造”的缩写,而是浏览器信任机制的意外副产品

很多人一看到CSRF就条件反射背出那句教科书定义:“Cross-Site Request Forgery,跨站请求伪造”。但这句话本身已经埋下了理解偏差的种子——它把CSRF描述成一种“主动攻击行为”,而实际上,CSRF的本质是浏览器在完全合规、严格遵循RFC标准的前提下,自动执行了本不该被触发的合法请求。它不是黑客“伪造”了请求,而是浏览器“忠实地执行”了用户自己从未点击、从未授权、甚至根本不知道存在的请求。

我第一次真正搞懂这点,是在调试一个电商后台的订单取消接口时。那个接口只接受POST请求,没有登录态校验(当时认为“反正有Session ID,够安全了”),也没有任何Token验证。结果测试同事用自己账号登着系统,随手点开一封邮件里的图片链接(<img src="https://admin.example.com/api/cancel-order?id=12345">),订单就被悄无声息地取消了。他没点“取消按钮”,没提交表单,甚至没意识到自己触发了操作——但浏览器照常把他的Cookie(含Session ID)附在请求头里发了出去。那一刻我才明白:CSRF不是黑客在“伪造”,是浏览器在“代劳”。

这背后的核心逻辑,是HTTP协议设计之初就确立的同源策略(Same-Origin Policy)的盲区。同源策略管的是“读取”——它阻止脚本从a.com读取b.com的响应内容;但它不管“发送”——只要请求是浏览器发起的,它就无条件携带当前域的所有Cookie,无论这个请求是来自用户点击、JS发起、还是一个隐藏的<img>标签。这种“只防读、不防发”的设计,在Web应用功能日益复杂的今天,就成了天然的安全缺口。

所以,当你看到热搜词里反复出现“chrome98无法携带cookie”“高版本chrome浏览器无法携带cookie”,别急着骂浏览器,先想想:是不是你的应用正依赖这种“自动携带Cookie”的行为做权限判断?如果是,那问题不在Chrome,而在你对浏览器信任机制的理解还停留在十年前。

提示:CSRF攻击成功的关键从来不是技术多高超,而是开发者误以为“有Cookie = 有权限 = 安全”。真正的防御起点,不是研究怎么绕过,而是承认:浏览器自动带Cookie这件事,本身就是不安全的默认行为

关键词“CSRF Token”之所以成为事实标准,正是因为它直面了这个根本矛盾——它不试图去禁用Cookie,而是给每一次敏感操作加一道“人工确认”的门槛。而“为什么CSRF Token要写在COOKIE里”,这个问题背后藏着一个更关键的工程权衡:我们到底是要对抗浏览器,还是利用浏览器?

2. CSRF Token存Cookie不是偷懒,而是对浏览器能力的精准调用

现在打开任意一个主流框架的文档,比如Spring Security或Django,你会发现它们默认把CSRF Token放在Cookie里,同时要求前端在请求头(如X-CSRF-Token)或表单字段中带上对应值。初学者常问:“既然Token都存在Cookie里了,为什么还要额外传一次?直接读Cookie不就行?”——这个问题问到了点子上,但答案恰恰暴露了对HTTP协议底层逻辑的误解。

关键在于:Cookie是浏览器自动附加的,而CSRF Token的验证必须是服务端可控的、显式的、可审计的。如果服务端只检查Cookie里有没有Token,那它就和Session ID一样,成了另一个“自动携带”的凭据,攻击者依然可以通过<img>标签触发请求,浏览器照样会把Token Cookie一起发过去。这等于没防。

真正的防御逻辑是“双重提交(Double Submit)”:

  • 第一步:服务端在用户登录后,生成一个随机Token,通过Set-Cookie响应头下发到客户端(比如Set-Cookie: XSRF-TOKEN=abc123; Path=/; HttpOnly=false; SameSite=Lax);
  • 第二步:前端JavaScript读取这个Cookie(注意:HttpOnly=false才可读),并把它作为请求头(如X-XSRF-TOKEN: abc123)或表单字段(如<input name="_csrf" value="abc123">)显式提交;
  • 第三步:服务端收到请求后,同时比对两个来源:请求头/表单里的Token值,和Cookie里的Token值。只有两者完全一致,才放行。

为什么非得这样设计?因为浏览器的SameSite属性虽然能缓解部分CSRF,但它不是万能的。SameSite=Lax能挡住<img>这类GET请求的Cookie携带,但挡不住表单POST(比如<form action="https://bank.com/transfer" method="POST"><input name="to" value="hacker"><input type="submit"></form>)。而双重提交机制,让攻击者即使诱导用户提交了表单,也无法控制表单里该填什么Token值——因为JavaScript读取Cookie需要同源,跨站页面拿不到目标域的Cookie内容。

所以,“CSRF Token写在Cookie里”,本质是一次精妙的分工:

  • Cookie负责安全分发:利用浏览器的SecureHttpOnlySameSite等属性,确保Token只能被目标域读取,且不会被XSS轻易窃取(HttpOnly=true时);
  • 请求头/表单字段负责显式声明:强制前端代码参与验证流程,把“用户意图”这个不可自动化的东西,编码进每次请求中。

我见过最典型的反模式,是某金融App把CSRF Token硬编码在HTML模板里,每次页面加载都生成新Token,但前端JS却从不读取它,而是直接用固定字符串提交。结果测试时发现,所有AJAX请求都失败——因为服务端比对时,Cookie里的Token和请求头里的Token永远不匹配。后来查日志才发现,他们把HttpOnly=true设错了,JS根本读不到Cookie,自然没法同步提交。这个坑提醒我们:Token存Cookie不是为了省事,而是为了构建一条可控的、可审计的、符合浏览器安全模型的信任链

3. Chrome 98+ 的SameSite默认变更:一场静默的CSRF防御升级

如果你最近在调试登录流程时频繁遇到“Cookie未发送”“跨域请求无认证信息”,大概率是因为Chrome 98(2021年10月发布)开始,将Cookie的SameSite默认值从None强制改为Lax。这不是Bug,而是Google联合Mozilla、Apple推动的全球性安全基线升级。它直接影响了所有依赖第三方Cookie的CSRF防护方案,也解释了为什么热搜词里“chrome98无法携带cookie”“高版本chrome浏览器无法携带cookie”会高频出现。

先说结论:这次变更本身就是在帮你防CSRF,只是它把很多“侥幸存活”的脆弱设计直接暴露了出来。我们来拆解SameSite三个值的实际行为:

SameSite值跨站GET请求(如<img>跨站POST表单提交同站导航(如a.com跳转到a.com)备注
Strict❌ 不携带Cookie❌ 不携带✅ 携带最严,用户体验差(跳转即登出)
Lax✅ 携带(仅限安全方法)❌ 不携带✅ 携带默认值,平衡安全与体验
None✅ 携带✅ 携带✅ 携带必须配合Secure(HTTPS)

重点看Lax:它允许跨站GET请求携带Cookie,但仅限于“安全”的HTTP方法,即GET、HEAD、OPTIONS、TRACE。这意味着<img src="https://api.example.com/data">能拿到Cookie,但<form action="https://api.example.com/transfer" method="POST">不能。而绝大多数CSRF攻击载体,正是这种恶意表单提交——Lax直接切断了它的Cookie通道。

那么问题来了:为什么你的应用“突然失效”?因为很多老系统在设置CSRF Token Cookie时,压根没声明SameSite属性。在Chrome 98之前,浏览器默认当它是None,所以跨站POST也能带;升级后,默认当它是Lax,跨站POST就不带了,导致服务端验证失败。

修复方案不是“降级Chrome”,而是显式声明你的意图

# 正确:明确指定SameSite=Lax(推荐,兼顾安全与兼容) Set-Cookie: XSRF-TOKEN=abc123; Path=/; Domain=example.com; SameSite=Lax; Secure # 或者:如果必须支持跨站POST(如嵌入式Widget),则用None + Secure Set-Cookie: XSRF-TOKEN=abc123; Path=/; Domain=example.com; SameSite=None; Secure

注意:SameSite=None必须搭配Secure,否则现代浏览器会拒绝设置。这也是为什么“京东签到 cookie 总是失效 使用代理”这类问题频发——代理服务器可能篡改了响应头,或者本地开发环境没走HTTPS,导致SameSite=None被浏览器忽略。

我处理过一个真实案例:某教育平台的家长端App,通过iframe嵌入老师端的课表管理页面。老师端的CSRF Token Cookie没设SameSite,Chrome 98升级后,家长端iframe里的POST请求再也带不上Token,所有操作报403。排查三天,最后发现只需在后端响应头里加一行SameSite=None; Secure,问题立解。但更深层的问题是:他们把CSRF Token和Session Cookie混在同一个Domain下,而SameSite=None会让所有Cookie都开放跨站,这反而扩大了XSS攻击面。最终方案是拆分Cookie:Session Cookie设SameSite=Strict,CSRF Token Cookie设SameSite=Lax,既满足嵌入需求,又不牺牲核心会话安全。

4. CSRF Token与HttpOnly的博弈:安全与可用性的钢丝绳

“cookie设置httponly”这个热搜词背后,藏着一个经典的安全悖论:HttpOnly能防XSS窃取Cookie,但CSRF Token又必须被JavaScript读取才能完成双重提交。如果把CSRF Token Cookie设为HttpOnly=true,前端JS就读不到它,自然没法同步到请求头;如果设为false,XSS攻击就能轻易盗走Token,让CSRF防护形同虚设。这不是配置错误,而是Web安全架构里一个真实的、必须直面的权衡。

先说结论:CSRF Token Cookie必须设为HttpOnly=false,这是设计使然,不是妥协。原因有三:

第一,CSRF Token本身不是长期凭证。Session ID一旦泄露,攻击者能冒充用户直到会话过期;而CSRF Token是短期、一次性、绑定请求的(理想情况下每次请求都刷新)。即使XSS窃取了当前Token,它也只能用于发起一次特定操作,且服务端验证后通常会失效。相比之下,窃取Session ID的危害是全局性的。

第二,真正的防线不在Token存储位置,而在XSS防护本身。如果你的应用存在XSS漏洞,攻击者不仅能读CSRF Token,还能直接执行fetch('/api/transfer', {method:'POST', body:...}),根本绕过Token验证。所以,投入精力加固XSS(CSP、输入过滤、输出编码)比纠结Token是否HttpOnly更重要。

第三,现代框架提供了更优解:分离存储,各司其职。以Django为例:

  • Session Cookie设为HttpOnly=True, SameSite=Lax,确保会话安全;
  • CSRF Token单独存一个Cookie(csrftoken),设为HttpOnly=False, SameSite=Lax,供JS读取;
  • 前端通过document.cookie读取csrftoken值,再注入到X-CSRFToken请求头。

这样既满足双重提交要求,又把高危的Session Cookie和低危的CSRF Token物理隔离。

实操中最大的坑,是开发者误以为“只要Token在Cookie里,就和Session一样安全”。我见过一个支付网关,把CSRF Token和JWT Access Token存在同一个Cookie里,且都设HttpOnly=false。结果一次轻微的XSS漏洞,让攻击者直接拿到了JWT,后续所有API调用都不再需要CSRF Token——防御体系彻底崩塌。教训是:CSRF Token的生命周期必须短,作用域必须窄,存储必须独立

另一个常见误区是过度依赖SameSiteSameSite=Lax能防大部分CSRF,但对“同站跨路径”攻击无效。比如a.example.com的页面,向b.example.com发请求,只要Domain相同,SameSite=Lax就允许携带Cookie。所以,如果你的系统有多个子域名(如admin.example.comuser.example.com),必须确保CSRF Token Cookie的Domain属性精确到最小粒度,避免跨子域污染。

5. DVWA、Pikachu实战复盘:从靶场到生产环境的CSRF认知跃迁

DVWA(Damn Vulnerable Web Application)和Pikachu靶场里的CSRF模块,是无数安全工程师的启蒙教材。但很多人通关后,只记住了“抓包改参数”“用Burp重放”,却没意识到:靶场里的CSRF,和真实世界里的CSRF,根本不是同一类问题。前者是“如何利用”,后者是“如何设计防御”。这种认知错位,直接导致很多团队在生产环境栽跟头。

以DVWA的CSRF Low级别为例:它只校验Referer头,且规则宽松(if (strpos($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME']) !== false))。攻击者只需构造一个同域名的恶意页面(如https://dvwa.example.com/hack.html),就能绕过。这确实展示了Referer校验的脆弱性,但现实中的系统,早就不靠Referer了——它连基本的CSRF防护都算不上。

而Pikachu的CSRF模块更进一步,引入了Token验证,但它的实现有个致命缺陷:Token生成后,直接拼接在URL里(如/transfer.php?token=abc123&to=hacker)。这违反了CSRF防御的基本原则——Token绝不能出现在URL中。因为URL会被记录在服务器日志、浏览器历史、代理缓存里,极易泄露。我在某政务系统审计时就发现类似问题:他们的“防CSRF”Token被当成查询参数传递,结果日志里全是明文Token,攻击者翻日志就能批量获取。

真正的生产级CSRF防御,必须回答三个问题:

  1. Token如何生成?

    • 必须使用密码学安全的随机数生成器(如crypto.randomBytes(32)),而非时间戳、UUID或简单哈希;
    • 必须绑定用户会话(如HMAC(session_id + timestamp, secret_key)),防止Token被跨用户复用;
    • 必须有时效性(如15分钟过期),且每次敏感操作后刷新。
  2. Token如何传输?

    • Cookie传输:必须设SameSite=Lax(或Strict),Secure(HTTPS),Path=/
    • 显式提交:必须通过X-CSRF-Token请求头(AJAX)或隐藏表单字段(传统表单),绝不能放URL或Body中
    • 前端读取:必须用document.cookie解析,而非localStorage(易受XSS影响)。
  3. Token如何验证?

    • 服务端必须同时校验Cookie值和请求头/表单值,且严格比对(constant-time compare),防止时序攻击;
    • 验证失败必须记录日志,并考虑临时封禁IP(防暴力枚举);
    • 对GET请求,原则上不需CSRF防护(幂等操作),但若涉及状态变更(如/logout),仍需Token。

我参与过一个银行APP的渗透测试,他们声称“已启用CSRF Token”。结果发现,Token生成算法是md5(time() . user_id),且有效期24小时。攻击者只需知道用户ID(通常公开),就能预测Token。更糟的是,他们把Token存在localStorage里,而App存在XSS漏洞——整个防护体系瞬间瓦解。最终建议是:Token生成必须用crypto模块,存储必须用Cookie(非HttpOnly),传输必须用请求头,验证必须用恒定时间比对

最后说个容易被忽视的点:CSRF防护不是“开关”,而是“光谱”。对转账、删除、修改密码等高危操作,必须强Token;对搜索、列表加载等只读操作,可弱化(甚至不防护);对文件上传等边界操作,需结合Content-Type校验(如只允许multipart/form-data)。一刀切的防护,要么形同虚设,要么拖慢体验。

6. 抖音来客、网易云音乐的Cookie持久化实践:CSRF在复杂登录场景下的变形

当热搜词里出现“抖音来客的cookie 持久化登录”“网易云音乐cookie”时,表面看是登录态管理问题,但深挖一层,你会发现:这些App的CSRF防护,早已脱离了传统Web表单的范畴,演变成一套融合设备指纹、动态Token、行为验证的复合防御体系。它们不再纠结“Token放Cookie还是Header”,而是思考:“如何让每一次请求,都证明是‘这个人’在‘这个设备’上‘主动发起’的”。

以抖音来客为例,它的登录流程是这样的:

  • 用户扫码或输入手机号登录后,服务端返回一个长期有效的Refresh Token(存localStorage)和一个短期的Access Token(存内存);
  • 所有API请求,除了常规的Authorization: Bearer <access_token>,还必须携带一个动态生成的X-Signature头,它是对请求时间戳、随机数、请求体哈希、设备ID的HMAC签名;
  • 这个X-Signature每5秒刷新一次,由前端SDK自动生成,根本不需要从Cookie读取

这里CSRF Token的角色,被“动态签名”取代了。它的优势在于:

  • 不依赖Cookie,彻底规避SameSiteHttpOnly等浏览器限制;
  • 绑定设备ID和时间戳,即使Token泄露,也无法在其他设备或过期后使用;
  • 签名覆盖请求体,防止参数篡改(兼具防CSRF和防重放)。

网易云音乐网页版则走了另一条路:它把CSRF Token和用户行为深度耦合。当你点击“收藏歌曲”时,前端不仅提交CSRF Token,还会采集鼠标移动轨迹、点击坐标、页面停留时间,生成一个“行为指纹”,和服务端下发的Token一起加密提交。服务端验证时,不仅要比对Token,还要校验行为指纹的合理性(比如,鼠标轨迹是否符合人类操作特征)。这已经超出了传统CSRF的范畴,进入了“人机识别”的领域。

这些实践给我们的启示是:CSRF的本质,是验证“请求意图的真实性”。当Web应用越来越复杂,单纯的Token验证已不够,必须结合上下文。比如:

  • 对移动端H5,可结合User-AgentDevice MemoryScreen Width生成设备指纹;
  • 对管理后台,可要求二次确认(如输入短信验证码);
  • 对高频操作,可引入滑动验证或点击验证。

我帮一家在线教育平台重构CSRF防护时,就采用了混合策略:基础层用标准CSRF Token(Cookie+Header),增强层对“退课”“退款”等操作,增加微信扫码二次确认。上线后,CSRF相关告警下降98%,且用户投诉率为零——因为扫码确认对用户来说,就是一次自然的“确认动作”,而不是突兀的弹窗。

所以,当你看到“夸克网盘登录 cookie”“chrome cookie备份”这类词时,别只想着怎么备份Cookie,更要思考:你的应用,是否还在用十年前的CSRF思路,去防御今天的攻击?真正的防御,不是把Token藏得更深,而是让每一次请求,都成为一次不可复制的、活生生的“人”的证明。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 19:39:17

自考学习AI工具全攻略:8大高效应用与避坑指南

1. 自考学习中的AI工具应用现状作为一名自考过来人&#xff0c;我深刻理解自考生面临的三大困境&#xff1a;时间碎片化、资料繁杂、缺乏学习监督。近年来AI工具的爆发式发展为自考学习带来了全新可能&#xff0c;但同时也出现了工具选择困难、使用效率低下等新问题。根据2023年…

作者头像 李华
网站建设 2026/9/16 19:39:13

HBase Shell命令全攻略:从建表、数据读写到运维实战

做HBase开发或者运维的人&#xff0c;应该都有过这种经历&#xff1a;查资料看官方文档&#xff0c;满屏的英文命令看得头晕&#xff1b;要么就是上网搜教程&#xff0c;结果搜出来的文章要么太浅&#xff0c;只贴个命令不给解释&#xff0c;要么太散&#xff0c;东一块西一块根…

作者头像 李华
网站建设 2026/9/16 19:37:54

Typora个性化设置全攻略:主题换肤、CSS定制与图片路径管理

Typora 的默认界面&#xff0c;第一眼确实干净&#xff0c;但真拿它当主力写作工具用上两三个月&#xff0c;你多半会跟我一样冒出几个念头&#xff1a;代码块背景能不能再深一点&#xff1f;标题颜色这个蓝色实在晃眼&#xff1b;插图老被复制到乱七八糟的目录里&#xff1b;导…

作者头像 李华
网站建设 2026/9/16 19:37:20

BWAPP靶场SQL注入通关实战:从联合查询到盲注绕过

提起Web安全入门&#xff0c;绕不开的就是SQL注入&#xff1b;绕不开SQL注入练习&#xff0c;自然就绕不开BWAPP这个靶场。我刚入行那会儿&#xff0c;白天看了一堆SQL注入的原理文章&#xff0c;晚上回去就想找个地方练手。DVWA的界面太老&#xff0c;Pikachu的题目量少&#…

作者头像 李华
网站建设 2026/9/16 19:36:42

AI教材编写工具:高效低查重的技术实现与应用

1. AI教材编写工具的核心价值解析在传统教材编写过程中&#xff0c;教育工作者常常面临三大痛点&#xff1a;内容创作周期长、查重率控制困难、知识体系更新滞后。我作为从事教育信息化十余年的从业者&#xff0c;见证了AI技术如何逐步改变这一局面。当前主流的AI教材编写工具&…

作者头像 李华