国际惯例,先来一波名词解释(来自维基百科):
CSRF: 跨站请求伪造(corss-site request forgery) 也被称为 one-click 或 session riding,也可称为 XSRF。是一种挟持用户在当前已登录的 web 应用程序上执行非本意的操作的攻击方法。跟 XSS(跨站脚本攻击) 相比,XSS 利用的是用户对指定网站的信任,而 CSRF 利用的是网站对用户浏览器的信任。
先来谈谈我为什么要写这篇文章。主要原因有二:
1) 内容过旧;
2) 关键点讲的不够详细,比如:禁用 CORS 可以防范 CSRF。初看有一种一脸懵逼的感觉(也有可能我太菜鸡???);
另外,起初了解时,跟朋友聊到这个话题,发现朋友对 CSRF 存在很大的误解:
某不愿透露姓名的朋友对 CSRF 的认知这里强调一下:CSRF 的目的并非窃取用户的身份信息!因为 CSRF 攻击者根本拿不到受害者的任何信息。
接下来切入正题。
如何确认应用是否存在 CSRF 漏洞?
在谈如何确认是否存在该漏洞之前,我们先来了解一次成功的 CSRF 攻击成功所必要的前提条件:
1) 已在攻击目标站点登录授权,且应用中使用了 form 表单发起会造成数据更改的请求;
2) 不规范的使用 HTTP 方法,如何定义不规范使用? 如:使用 get 请求修改数据;
当然, 还依赖你的登录环境以及你没有做 CSRF 防御这个前提。
有了这两个前提条件,那我们去定位可能存在问题的代码就简单很多了。我们只需要应用中是否存在 form 提交数据(注意,是提交数据,不是加载数据。即该类请求会造成数据库数据的的更改);再者,检查应用中是否有 GET、HEAD 请求造成数据库数据更改的接口。
如何防御 CSRF?
先来讲几个网上普遍说的几种方案:
1. 验证 HTTP Referer
这算是最简单快速的防范 CSRF 的方法,原理很简单:
即在处理请求之前,检查 referer 值是否站点本身的 hosts, 如果不是,则拒绝此次请求。你可能会说在一些低版本的浏览器中是存在可以篡改 referer 的方法的。 在这里我并不打算谈 IE6、firefox2 存在篡改 referer 方法, 毕竟这些都是过去式, 在当今的 web 环境下,根本不需要考虑这个问题。那是不是可以认为这是个既简单又完美的方案呢?答案是否定的:
1) 在 firefox 中,可以设置禁用 referer,具体原因与操作方法如果你愿意了解,可以点我
2) 嗯...并且 Opera 也有这个功能, 操作方法点我,并搜索文本 “Referrer logging” 检索到内容
3) chrome 也允许以修改启动项参数的方式禁用 referer,当然,这么干的人也不是什么电脑文盲。具体操作方式点我
看到上面的 1 2 3,你还敢使用这 简单、“完美”作为你的应用的防范 CSRF 的方案吗?反正我不敢。
2. CSRF token
这种防范机制, 结合我自己的理解, 大概可以描述为(本想画成图的,无奈作图水平太差,只好以文字表达):
每次页面渲染时,往页面某个元素上写入 token, 比如 <input id="J_csrfToken" type="hidden" value="{{csrfToken}}" />,并为页面上所有的 a.href都加上 csrfToken 的 qs 参数,而对于 form, 则在表单域内创建 <input type="hidden" name="csrf-token" value="{{csrfToken}}" /> ,如此一来,当出发 a.href 或 form.submit 时, csrfToken 都以 qs 的方式发送给服务端, 服务端根据该 token 进行校验确定是否为合法请求。 而对于动态生成的 html 片段, 则通过 document.getElementById('J_csrfToken').value 的方式手动编码传输给服务端。
那我们何时需要采用这种方法来防御 CSRF?你只要需要确认:
1) 是否采用有副作用的 GET、HEAD 请求
2) 是否有采用 form.submit 的方式修改数据库数据
如果有的话,那你可以考虑采用这种方式来防御
3. 禁用 CORS
实验证明,简单的禁用 CORS 并不能有效防御 CSRF,它需要一些前提:
1)正确的使用了 HTTP 方法, 不要使 GET、 HEAD 有副作用;
2)如果你使用了有副作用的 GET, 攻击者仍然可以通过创建 script 标签再指定 src 为对应的请求即可完成攻击;
3)如果你使用了有副作用的 GET且没有做登录验证,那么使用 xhr 也能轻松完成攻击,毕竟 CORS 属于浏览器的安全策略, 它只拦截响应而非请求;
其他的一些文章中, 也有讲到一些其他的方案, 但我觉得没有实践价值,就没必要赘述(其实我觉得这里列出来的,也没啥实践价值。只是整体消化下来,感觉总有一些点没讲清楚,就作为补充来描述一下)。接下来谈谈个人理解的防范之道:
1. 正确使用 HTTP 方法;
2. 不要使 GET、HEAD 有副作用;
3. 存在 form 表单提交数据的场景时,可以使用 csrfToken 方案;
做到以上几点, 我相信基本可以将 CSRF 拒之门外。
如何发起一次 CSRF 攻击?
根据一次成功的 CSRF 攻击所需要的必备条件, 首先当然是需要攻击目标在站点登录。
ok, 接下来脑补一下:作为攻击者的你,做了一个精心准备的算命网站,上面一个醒目大按钮——“测一测”,点击时,其实是发起了一个 form.submit ,将必要的字段都放置其中,坐等受害者上钩。一旦用户点击, 你将以受害者的名义以你想要的结果修改了受害者的数据,达成攻击目标。
以上,希望对你有帮助!另,也欢迎勘误!
网友评论