CSRF漏洞防护
CSRF漏洞的漏洞本质,是防护浏览器把用户的身份凭证当成了万能钥匙。攻击者不需要偷密码,漏洞不需要破解会话,防护只需要诱导用户点开一个恶意链接,漏洞就能以用户的防护名义执行任意操作——转账、改邮箱、漏洞发帖,防护甚至删库。漏洞我曾见过一个电商后台,防护所有管理接口都只校验Cookie,漏洞结果一个标签的防护src指向删除商品接口,就能让管理员辛辛苦苦维护的漏洞商品目录在瞬间清空。这不是防护理论,这是漏洞真实发生过的灾难。
防护CSRF的思路,核心就一句话:让服务器能分辨出,这个请求到底是不是用户主动发出的。Cookie会自动附带,但请求里的额外校验值不会。于是就有了CSRF Token的经典方案。服务器在渲染表单时生成一个随机Token,存在Session里,用户提交时带上这个Token,服务器比对一致才放行。攻击者的恶意页面拿不到这个Token,自然伪造不了合法请求。
但Token方案有个坑——它依赖Session存储。如果应用是无状态的,用JWT做认证,Token往哪儿存?存客户端再回传,那等于没防。有人把CSRF Token放在自定义Header里,配合CORS严格限制跨域读取,这招确实有效。因为跨域请求要带自定义Header,必须先发OPTIONS预检,而预检请求会被服务器拒绝——攻击者根本没法构造出带正确Header的请求。不过,这要求所有写操作都必须走XHR或fetch,老旧的表单提交就彻底废了。你得掂量下,项目里有没有那种无法改造的遗留页面。
还有一招更简单粗暴——SameSite Cookie属性。把Cookie设为SameSite=Strict或Lax,浏览器就会在跨站请求时自动不携带Cookie。这从根源上掐断了CSRF的命脉。但你要清楚,SameSite只对“跨站”有效,如果攻击者能把恶意代码注入到同站页面(比如XSS),那这层防护形同虚设。而且,老版本浏览器不支持SameSite,你得有降级方案。
说个我踩过的坑。之前给一个内部系统做防护,加了Token校验,但漏掉了文件上传接口。攻击者构造一个multipart/form-data请求,把Token放在URL参数里,而文件内容里塞了恶意脚本。服务器只校验了POST body里的Token,没校验URL参数——结果Token校验被绕过,恶意文件成功上传。后来我学乖了,所有写操作统一从Header取Token,不再区分参数位置。这件事给我的教训是:防护方案必须统一,不能有的接口用Token,有的用Referer校验,有的干脆裸奔。攻击者专挑最弱的那条路走。
另外,别迷信Referer检查。Referer可以被浏览器扩展、代理服务器甚至某些隐私模式剥离。我见过一个系统,Referer校验写得太死,结果用户从办公网访问时Referer带了内网IP,被服务器拒绝,误伤了一堆正常请求。后来改成校验Referer域名是否在允许列表里,才平衡了安全和可用性。
最后提一句,防CSRF不是一劳永逸的事。你堵住了转账接口,攻击者可能转头去试改密码接口。最好的做法是,对所有改变状态的请求(POST、PUT、DELETE)一律强制校验Token,Get请求只做查询,不产生副作用。然后定期用自动扫描工具测一遍,别等出了事再补。
(责任编辑:热点)














