CSRF漏洞的漏洞本质是浏览器自动携带Cookie,服务器却分不清请求是解决谁发起的。攻击者构造一个恶意页面,漏洞诱导受害者点击,解决浏览器就会带着受害者的漏洞登录态去请求目标网站,完成转账、解决改密等操作。漏洞我见过最典型的解决案例是某论坛的退出登录接口被植入到图片链接里,用户只要浏览帖子,漏洞账号就被强制登出,解决体验极差。漏洞而真正危险的解决场景是银行或管理后台,一个伪造的漏洞标签就能转走钱。
解决办法里,解决最直接有效的漏洞是校验Referer字段。服务器检查HTTP请求头中的Referer,如果来源域名不是自己站内的,直接拒绝。这个方法实现成本极低,但有个坑:某些浏览器或隐私插件会去掉Referer,导致正常用户被误伤。我实际测试过,Chrome的严格模式会隐藏跨域Referer,这时候你得做好兼容,比如允许空Referer但拒绝非白名单域名。
更稳妥的是用CSRF Token。服务器生成一个随机token,塞进表单的隐藏字段,同时存在用户Session里。提交请求时,服务器比对两者是否一致。攻击者没法读取同源响应,自然拿不到token。这个方案几乎能堵死所有CSRF,但要注意token不能放在URL参数里,否则会被Referer泄露。我见过某电商把token写进查询字符串,结果用户从站内跳到外部链接时,token跟着Referer一起送了出去,等于白做。
还有个容易被忽略的细节——自定义请求头。比如要求所有敏感操作必须带X-Requested-With: XMLHttpRequest,因为跨域请求默认无法自定义这个头。但这个方法依赖浏览器对跨域请求的限制,如果目标站点存在CORS配置错误,或者攻击者用表单提交(表单不支持自定义头),那就防不住。所以它更适合作为辅助手段,配合token使用。
再讲个实战中的坑:很多人以为用POST就安全了,实际上攻击者可以自动提交一个隐藏表单,用JS触发submit,照样是POST。所以别把方法绑定在请求方式上,关键看数据是否包含服务器才知道的秘密。另外,如果网站有JSON接口,记得检查Content-Type,攻击者用text/plain也能发JSON payload,有些框架会自动解析,得手动校验。
最后说个偏门但有效的方案——双重提交Cookie。服务器不存session,只生成一个随机值,同时写入Cookie和请求参数,校验两者是否一致。因为攻击者无法修改Cookie(除非有XSS),所以能防住。但前提是你得确保没有子域名能设置Cookie,否则还是能绕过。
说到底,没有银弹。最稳妥的组合是:敏感操作一律用POST + 随机token + 校验Referer来源。token放表单里,别放URL。Referer校验别太严格,允许空值,但拒绝非白名单。再给关键接口加个自定义头,三层防护下来,基本能杜绝CSRF。别偷懒,别只搞一个token就完事,现实中攻击者会组合利用漏洞,多一层校验就多一分安全。