由于之前打算发布的mustache.js的源码分析中发现了一些严重的错误,所以临时将这一次的更新改成了之前一直没搞明白的web安全中的同源策略分析。

0x00 同源策略的含义

1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页”同源”。

所谓”同源”指的是”三个相同”:

举例来说http://www.example.com/dir/page.html这个网址协议是http://,域名是www.example.com,端口是80(默认端口可以省略)。

它的同源情况如下:

Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。但是两个网页一级域名相同,只是二级域名不同,则可以通过设置Cookie的domain段来共享。

假设现在有A网页http://w1.example.com/a.html,B网页http://w2.example.com/b.html。那么要想在两个网页共享Cookie的就必须将Cookie中的domain设置为两者的上层域名example.com或者.example.com(不推荐)。

domain(可选):例如 ’example.com’, ’.example.com’ (包括所有域名),’subdomain.example.com’。如果没有定义,默认为当前文档位置的路径的域名部分 (string或null)。

此外需要注意的是在浏览器的同源策略下,浏览器所有请求都会带上其作用域内的Cookie

注1:子域名都能访问所有的上层 Cookie。
注2:这种方法只适用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 无法通过这种方法规避(子域名)同源政策。

0x02 XSS

XSS(Cross Site Scripting)攻击全称跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。比如这些代码包括HTML代码和客户端脚本。

XSS攻击可以分为两种:

反射型主要是通过不完善的API进行攻击,如某些后端实现对用户的请求并没有过滤,然后将请求结果直接加入渲染结果中返回。

举个列子:
现有以下请求

和模板:

cat <<-EOF
<html>
...
${name}
...
</html>
EOF

那么请求中的代码将会最终出现在用户的渲染结果之中,从而达到执行跨域脚本的目的。

存储型反射型类似,不同之处在于前者将代码存储在服务器上,当用户浏览时才会触发。较为常见的比如在某一段评论之中插入代码,这一段代码可以用来获取用户的Cookie,token,用户密码等。

此处的示例略……

0x03 CSRF

因为同源策略的限制,当正常用户通过账号密码等方式登陆网站A后,在不注销账号或当前COOKIE失效之前,再次访问(如GET,POST等)网站A时(协议、IP、端口号相同则属于同源)浏览器会自动在HTTP请求包中带上该网站用户登陆后的COOKIE信息(参考0x01)。

也就是说我们对CSRF的理解应为:攻击者借用浏览器的特性,用用户登录后的COOKIE发送请求,执行非用户本意的操作。

如已知用户在www.mybank.com登录,且已经被我们诱导至我们加入特殊代码<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>后的网站A,那么在用户毫不知情的情况下打开网页后就会向www.mybank.com发送一个GET请求,从而实现我们的目的。

当然目前因为安全原因,目前大多数的服务器API很少有用GET方法实现的,因此我们必须学会如何实现使用POST方法实现:

<form action="http://www.mybank.com/Transfer.php" method="POST" name="transfer">
    <input type="hidden" name="toBankId" value="11" />
    <input type="hidden" name="money" value="1000" />
</form>

<script>
    (function() {
        var transfer = document.forms["transfer"];
        transfer.submit();
    })();
</script>

但AJAX因为JS的安全限制则不能实现这一点(注3)

在此攻击过程中用户COOKIE对于攻击者来说是未知的、不可见的,攻击者能做到仅仅是借用COOKIE,而COOKIE里面具体写了什么,攻击者是不知道的。又因为COOKIE里的信息对于攻击者来说是不可预知的,无法伪造的,所以将CSRF-TOKEN写在COOKIE中符合就CSRF防御思想中的不可预知原则。

将CSRF-TOKEN写在COOKIE中可抵御CSRF攻击,但前提是网站不存在XSS漏洞或者CSRF-Token具备HttpOnly属性(HttpOnly不是必须的,视具体js逻辑)。

当用户发送新的请求后,服务器会对比请求中的CSRF-Token和对应的Cookie中的Token。如果两者数值相同,用户的请求操作才是被允许执行的,否则用户的请求就是不合法的,即CSRF。

防范示例
我们以之前的转账模板为例,在www.mybank.com的网页代码中加入csrftoken

<form action="http://www.mybank.com/Transfer.php" method="POST" name="transfer">
    <input type="hidden" name="csrftoken" value="xxxxxx" />
    <input type="text" name="toBankId" value="11" />
    <input type="textt" name="money" value="1000" />
    <input type="submit" value="提交" />
</form>

这里的csrftoken可以是任何随机值,但必须保证与Cookie中的值一样。

第三方网站中访问www.mybank.com时无法得到正确的csrftoken,当然也就无法构造有效的请求(如GET,POST等)。

注1:攻击时将form标签嵌套在iframe标签里会有更好的隐蔽性。
注2:服务器后端实现时后端的检测csrftoken的代码中首先要排除csrftoken为空时的情况。
注3:AJAX会有跨域问题是因为浏览器对JS的安全限制,而form和其他标签则没有这样的问题是因为兼容性。
注4:检测请求HEADER里的referer也能够防御CSRF攻击。

Refs: