跨站请求伪造 CSRF 不偷数据,而是借受害者在目标网站已登录的会话,让浏览器替攻击者发出一个“看起来像用户自愿”的请求,从而以受害者身份执行转账、改密、发帖等敏感操作。根子在服务器无法区分“来自用户浏览器的请求”与“用户的真实意愿”,防御靠防跨站令牌与 SameSite Cookie。
注入和 XSS 都在“送进系统”上做文章,CSRF 换了打法:它什么都不注入,只是把你已属于某个网站的身份拿来当道具。浏览器有个默认行为——访问任何新页面时,会自动带上用户已存的该域 Cookie。CSRF 正是利用这个“自动携带”的特性,让服务器误以为请求出自身份验证过的用户。
想象一个转账逻辑长这样(GET 是反例,只为了说明后果):
请求: GET /transfer?to=攻击者账号&amount=1000 效果: 直接执行转账, 不校验来源
攻击者在论坛或邮件里放一张图片:
<img src="/transfer?to=攻击者账号&amount=1000">
受害者已在银行站登录,浏览器带着会话 Cookie。当他打开这张图片所在的页面时,浏览器去加载图片,向银行站发出了带 Cookie 的转账请求;服务器看见“携带有效会话”,就执行了转账。用户全程毫不知情,钱已经没了。
服务器用“请求带上了有效会话 Cookie”来证明“是我这个用户在操作”。但这个证明是不充分的——Cookie 是浏览器自动带的,它说明“这个浏览器访问过该站”,并不能证明“用户此刻真心想执行这笔操作”。攻击者要做的只是诱导浏览器向目标站发一个请求,剩下的由自动携带完成。
这也是为什么根因是“服务器无法分辨请求到底是不是用户本期主动发起的”,而不是“密码泄露”或“会话被偷”。统计上,CSRF 更常排在“已经很高危”但“最容易忽略配置”的漏洞里,因为它常被误以为“加了登录框就没事”。
能用 GET 完成的敏感操作越多,CSRF 越危险:改绑手机、改密、转账、发帖、下单。攻击者甚至能把多个操作串成一个自动脚本,让受害者一进页面就被连环操作。防御它的重点从来不是“换个接口方法”,而是让服务器能验证“这个请求确实是用户当前页面上自愿发起的”。
<img>、<link> 这类无脚本跨站自动请求就直接命中。⚠️ 常见坑:把令牌放在 Cookie 里就以为安全。令牌必须是在表单体或自定义请求头里随请求提交、由服务器持久校验的随机值;放 Cookie 里等于让浏览器自动带它,攻击者的伪造请求也能带,等于没防。
背景:一个本地起的学生站,转账接口无令牌、未设 SameSite。操作:在另一个标签页构造一个会自动发 POST 的表单页(自提交),模拟攻击者页面。结果:用受害者会话访问该页,转账成功执行。解读:请求带上了受害者的会话 Cookie,服务器误判为自愿操作。变式:给表单加上随会话生成的令牌并在服务器端校验,同样的恶意页因没有合法令牌而被拒绝;再把 Cookie 设 SameSite=Lax,观察跨站 POST 请求不再携带会话从而也被挡。这套两措施叠加,CSRF 基本被堵死。
很多新手把 CSRF 和 XSS 当成一回事,排查时会走弯路。区分清楚:XSS 是“还能在受害者浏览器里跑脚本”,CSRF 是“借受害者会话替你发请求”。前者攻的是客户端执行权,后者攻的是服务器对请求的意愿判定。一个没有 XSS 漏洞的站也可能中 CSRF,因为 CSRF 根本不需要脚本——一张 <img> 或一个自动提交的表单就够。反过来,也常有站同时中两个漏洞,XSS 里取令牌、CSRF 里替执行,形成组合拳。所以排查时先问“这个动作是被脚本驱动的,还是被浏览器自动携带的 Cookie 驱动”,再决定走 XSS 的编码修复,还是走 CSRF 的令牌与 SameSite 修复。
实际工程里,CSRF 的难点常是“我得在多少地方加校验”。这里给一条简化的判定线:凡是不产生状态变化的请求(读详情、查列表),通常不必为 CSRF 过度防护;凡是会产生状态变化的敏感操作(改密、转账、下单、换绑),一律先过三种防线——入口是否用 POST 且语义正确、是否带随会话生成的令牌并服务端校验、会话 Cookie 是否设了 SameSite。若用自定义接口(如 App 端、前后端无关工具在调)而不是浏览器页面承载,再把令牌校验换成自定义请求头或每次请求的动态凭证。把这条线写进接口评审清单,新接口上线时先过一遍,比事后补漏省事得多。判线本身也能反推:如果一次功能变更新增了状态变更类操作,却没同步走这三问,那基本可以确认它埋了 CSRF 的雷。