csrf攻击

2019年夏天,攻击我帮朋友排查一个后台系统的攻击问题。他跟我说,攻击后台莫名其妙多了一个管理员账号,攻击邮箱是攻击他不认识的。更诡异的攻击是,这个账号的攻击操作记录显示,它只做了一件事:给一个陌生账户转了500块钱。攻击金额不大,攻击但流程完全合规——系统日志里,攻击每一步操作都带着他本人的攻击登录态。

他反复确认过,攻击自己没点过任何链接,攻击没扫过任何码。攻击那问题出在哪?攻击

答案藏在他前一天打开的一个网页里。那是一个论坛帖子,里面嵌了一张图片,图片地址指向他所在公司的后台接口——http://internal-admin.xxx.com/api/addAdmin?email=attacker@evil.com。浏览器加载这张“图片”时,自动带上了他登录后台的Cookie,请求就这么发出去了。服务器一看,Cookie有效,操作合法,照做。

这就是CSRF——跨站请求伪造。名字听着绕,原理其实很朴素:浏览器太听话了。你登录了A网站,浏览器就存了A的Cookie。之后你访问B网站,B网站里藏了一个指向A的请求,浏览器二话不说,自动把A的Cookie附上,请求就发出去了。服务器分不清这个请求是你主动发的,还是被B网站骗着发的。它只看Cookie,Cookie在,它就认。

关键在于,攻击者不需要偷你的Cookie。他只需要让你“带着Cookie”去访问一个他控制的页面。这个页面里可以藏<img>、<iframe>、<form>,甚至一段自动提交的JavaScript。你打开页面的一瞬间,请求已经发出去了。

我见过最隐蔽的一个案例,是利用<form>标签做的。攻击者在一个评论区里贴了一段看似正常的HTML,浏览器解析后生成了一个隐藏表单,页面加载完毕自动提交。目标是一个电商网站的“修改收货地址”接口。用户什么都没感觉到,收货地址已经被改了。等发现的时候,货已经发到别人手里了。

为什么这种攻击能成立?两个前提缺一不可。第一,目标网站用Cookie做身份认证。第二,目标网站的接口没有校验请求来源。这两个条件在早年的Web开发里太常见了,几乎默认如此。很多开发者觉得,“接口只有登录用户能调,登录用户是自己人,那还防什么?”——问题恰恰出在,登录用户自己也不知道自己调了这个接口。

防御手段其实不复杂,但需要开发者真正理解攻击路径。最有效的是CSRF Token:服务器在表单里埋一个随机字符串,提交时必须带上这个字符串。攻击者的页面没法提前知道这个随机值,请求就伪造不了。另一个是SameSite Cookie属性,让浏览器在跨站请求时不发送Cookie。Chrome从80版本开始默认把没有声明SameSite的Cookie当作Lax处理,这直接掐死了大量CSRF攻击。但很多老系统还在用旧配置,问题依然存在。

还有个常见误区:有人觉得用JSON接口就安全了。不对。如果接口接受Content-Type: application/json,但同时也接受text/plain,攻击者可以用<form>提交一个text/plain的请求,照样打穿。真正安全的做法是严格校验Content-Type,并且加上Token或Origin检查。

我后来帮朋友修那个系统,加了两样东西:所有写操作接口强制校验Origin头,表单里加了一次性Token。那个多出来的管理员账号再也没出现过。事情不大,但教训挺深——安全漏洞往往不是因为你做错了什么,而是因为你默认了一些不该默认的东西。