CSRF必知必会

CSRF的全称是cross-site request forgery,中文名是跨站请求伪造。一句话来说就是,恶意网站A利用受害者的浏览器,在受害者不知情的情况下,向网站B发出请求。在好几年前CSRF是一个排名很高的安全问题,近几年已经受到重视,很多web框架都有相应的措施,但是依然值得web开发人员重视。

CSRF常见案例

这里就说说OWASP给出的例子。假如有一个银行网站叫做bank.com,如果Alice要通过bank.com转账100元给Bob,那么她在bank.com的网站页面的操作其实就是发送以下的http请求给bank.com的服务器:

GET http://bank.com/transfer.do?acct=BOB&amount=100 HTTP/1.1

假如我想偷偷地、非法地将Alice账户下的10000元转给一个叫Maria的人,那我只需要假借Alice的名义,发如下的http请求给bank.com的服务器:

GET http://bank.com/transfer.do?acct=MARIA&amount=10000 HTTP/1.1

总不能拿支枪指着Alice的头让她发送这条请求吧,要偷偷地在她不知情的情况下由她本人来发出这个http请求,具体要怎么做呢?我只需要随便弄一个网页,引诱Alice来访问(引诱方法不在CSRF的范畴),甚至Alice不需要在这个网页上输入或者是点击任何内容,都可以完成CSRF。这个页面只需要包含以下一行代码:

<img src="http://bank.com/transfer.do?acct=MARIA&amount=10000" width="0" height="0" border="0">

这行html代码在页面嵌入了一个宽度和高度都是0的图片(就是说这图片什么都没有),img标签是自动加载的,因而Alice浏览该页面时,就自动发了上文说的那个http请求了。 为什么能够冒充Alice发出转账请求?因为bank.com的服务器是通过cookie的内容来判断这条转账请求是由谁来发起,是通过cookie来判断发起该请求的人是否输入正确的密码来登录的(当然现在的银行网站不会那么弱智)。而如果Alice最近登录过bank.com,那么她的浏览器是将cookie保存一段时间的。Alice访问我的网页加载那个img标签的资源时,就会发送那个http请求,并且浏览器会帮忙带上保存在本地的cookie。然后bank.com查看cookie信息后,就会认为这笔转账请求是由Alice发起的,最后Alice的钱就被转走了。 有人会觉得,既然GET请求会被利用,那么如果改用POST方法,真可以了吧?too youg,too navie。直接看代码:

<html>
<body>

  <form name="csrf_form" action="http://VULNERABLE_APP/csrf.php" method="POST">
    <input type="hidden" name="csrf_param" value="POST_ATTACK">
  </form>

  <script type="text/javascript">document.csrf_form.submit();</script>
</body>
</html>

由此可见,改成POST方法也不能保证安全。

以上简单地展示了CSRF的案例,现实中可能有更复杂的手段,但本质都是一样的,就不赘述了。下面来看看CSRF的本质。

CSRF的本质

这里需要引用Bill Zeller的一段文字:

CSRF vulnerabilities occur when a website allows an authenticated user to perform a sensitive action but does not verify that the user herself is invoking that action.The > key to understanding CSRF attacks is to recognize that websites typically don’t verify that a request came from an authorized user. Instead they verify only that the request came from the browser of an authorized user. Because browsers run code sent by multiple sites, there is a danger that one site will (unbeknownst to the user) send a request to a second site, and the second site will mistakenly think that the user authorized the request.

CSRF能够发挥作用需要几个条件:

  • 服务器端处理某些敏感的请求时,使用cookie来验证
  • 相关的cookie被用户的浏览器保存了下来
  • 服务器端没有进一步仔细验证带上这个cookie的请求的发起源是哪里

看清CSRF的本质后,就可以针对性地进行防范了。

如何防御CSRF

CSRF的防御手段有很多种,有些手段比较有效,有些手段太弱,这里只谈效果甚好应用广泛的一个种。它叫做“double-submitted cookie”。这种方法简单来说就是想办法验证cookie是否合法地被利用。 不同编程语言的web app框架采用的方法都大同小异,本质上就是想办法在html页面添加类似以下的一行代码

<input id="fkey" name="fkey" type="hidden" value="df8652852f139" />

以上代码的作用是添加一个不在页面上显示的input标签,使得在表单提交POST请求时带上一个随机字符串(这个字符串是由后端web app渲染页面时生成的),而在此次post请求过程中带上的cookie中也包含同一个随机字符串,后端web app接收到该POST请求时就会对它们进行比对,如果一致则证明该请求不是伪造的。 由于即便在同一个浏览器,不同网站之间是无法跨域读取cookie的,所以想发动CSRF的恶意网站无法知道cookie中的随机字符串值,自然也无法伪造请求了。

这次先简单写到这里,以后再写关于RESTFUL API的安全验证机制。


参考资料: