csrf漏洞
2008年,漏洞一个叫Bob的漏洞普通用户登录了某银行网站,查完余额后没点退出,漏洞顺手打开了另一个标签页去看论坛。漏洞就在那个论坛里,漏洞有人贴了一张图片,漏洞图片地址指向的漏洞是这家银行的转账接口—— 这就是漏洞CSRF——跨站请求伪造。它的漏洞核心逻辑简单到令人不安:浏览器会自动为同域请求附带Cookie,而服务器只看Cookie,漏洞不看请求从哪来。漏洞 Cookie的设计初衷是让网站记住你。你登录一次,之后每次请求都自动带上身份凭证。问题在于,这个“自动”没有判断力。不管请求是你主动点的,还是某个恶意页面偷偷发的,只要目标域名匹配,Cookie就照带不误。 攻击者利用的正是这一点。他不需要偷走你的Cookie,只需要让你“代替”他发一个请求。你登录着银行,他诱使你访问一个页面,页面里藏着一个自动提交的表单或者一个img标签,请求就发出去了。服务器收到请求,验证Cookie有效,执行操作。整个过程你不知情,服务器也分不清。 有人觉得GET请求才有这个问题,改成POST就安全了。不是。攻击者可以在自己的页面上放一个隐藏表单,用JavaScript自动提交。用户打开页面的一瞬间,POST请求已经发出去了。GET和POST在CSRF面前没有本质区别。 我见过一个后台管理系统,删除用户的接口是 这种设计在早年的Web应用里非常普遍。开发者觉得“管理员才能访问这个接口”,却忽略了管理员在登录状态下访问任何页面时,浏览器都会替他发请求。 既然服务器分不清请求来源,那就人为加一个攻击者猜不到的东西。 CSRF Token是目前最主流的方案。服务器在表单里埋一个随机字符串,提交时校验这个字符串。攻击者的页面无法读取这个Token(同源策略挡着),也就无法伪造完整请求。Token不需要加密,只需要足够随机、每次会话不同、且不通过Cookie传输。 SameSite Cookie是另一道防线。给Cookie加上 还有一种老办法:校验Referer头。服务器检查请求来源是不是自己的域名。简单粗暴,但Referer可能被隐私设置抹掉,也可能被伪造(在某些旧浏览器里)。所以它通常作为辅助手段,而不是唯一防线。 有些开发者把Token放在URL里,觉得这样也能防。但URL会出现在浏览器历史、服务器日志、Referer头里,反而增加了泄露风险。Token应该放在表单隐藏字段或自定义请求头里。 还有人只给敏感操作加Token,觉得查询接口无所谓。但如果查询接口能返回用户隐私数据,攻击者同样可以利用CSRF读取信息——虽然受同源策略限制,攻击者无法直接读取响应内容,但可以通过时序攻击等方式推断信息。所以最稳妥的做法是:所有改变状态的接口都加防护,查询接口至少加SameSite。 CSRF不像SQL注入那样能直接拖库,也不像XSS那样能盗取Cookie。它更像一个“隐形代理人”,用你的身份做你不想做的事。防御它不需要多复杂的技术,但需要开发者对浏览器的行为有清醒的认识——浏览器很忠诚,但忠诚不等于安全。https://bank.com/transfer?to=attacker&amount=10000。浏览器发这个请求时,漏洞自动带上了Bob的漏洞登录Cookie。钱就这么转走了。漏洞Bob全程什么都没察觉。漏洞浏览器的“忠诚”成了漏洞
一个真实场景
GET /admin/deleteUser?id=123。管理员登录后,如果有人给他发一个链接,页面里嵌一个<img src="https://admin.example.com/admin/deleteUser?id=123">,他点开链接的瞬间,用户就被删了。没有确认弹窗,没有二次验证。管理员甚至不知道发生了什么。防御的核心思路
SameSite=Lax或Strict属性,浏览器在跨站请求时就不会带上这个Cookie。Lax模式下,GET请求仍然会带Cookie,但POST、PUT、DELETE这些跨站请求不会。这能挡住大部分CSRF攻击,但对用户体验有细微影响——比如从外部链接跳转过来时,登录状态可能不生效。容易踩的坑