XSS攻击的原理及防范

作者:JAY 2020-06-14

攻击原理

Cross Site Scripting(跨域脚本攻击),简称XSS,是web应用最见的攻击方式之一。攻击的前提有两个:

1. 浏览器可以执行JavaScript代码(这不是废话吗)。

2. 网页可以显示用户输入的内容。包括但不限于:根据url中的参数渲染网页、预览输入框写好的内容、留言板等其他用户提交的内容等。

基于以上两条前提,一旦用户输入的内容带有JavaScript代码(比如含有<script>标签),这些代码就有可能被浏览器执行,这就是XSS攻击的原理。除了JavaScript,还有Java、VBScript、ActiveX、Flash甚至是HTML,任何可以在客户端运行的脚本,都有可能遭到XSS攻击。


黑客通常使用XSS攻击来做以下坏事:

以上任何一条,都是手段残忍危害极大,会给网站造成难以估量的损失。以新浪微博的的惨痛教训为例,2011年6月28日,黑客把攻击脚本隐藏在短链接之中,无知的用户傻傻点开链接,就会继续发送带有攻击链接的消息。于是微博被大量攻击链接刷屏:



攻击方式

储存型

常见于论坛、私信、商品评论等场景,需要在数据库中储存用户输入的内容,并展示给其他用户看。举例,某个调皮的用户在留言板写了这么一行:

<script>alert('XSS')</script>

其他用户打开留言板,就会看到一个XSS的弹框。


反射型

有时候需要在页面上显示url传入的内容。举例,搜索页面的关键字从url里传入,PHP代码大概会这么写:

<?php 
  $query_str = $_SERVER['QUERY_STRING'];
  parse_str($query_str);
  echo '搜索关键字:'.$keyword;
?>

此时黑客构造这么一个链接发给用户:

http://test/search?keyword=<script>alert('XSS')</script>

无知的用户打开了链接,藏在url中的代码就自动执行了。


Dom型

还是上面那个例子,搜索页面的关键字从url里传入,但这次的页面不是由后台渲染的,而是前端JS渲染的:

let index=document.URL.indexOf("keyword=")+8;
document.write(decodeURI(document.URL.substring(index,document.URL.length)));

黑客还是发上面的那个链接给用户,打开url,照样会看到XSS弹框。

严格来讲,DOM型XSS也属于反射型XSS。只不过它不经过服务器,完全在客户端执行,因此也只能在客户端去防范。


过滤与反过滤

防范XSS攻击,从源头开始,要对用户输入的内容,过滤危险代码。比如,过滤掉<script>标签,过滤掉javascript:开头的内容等。狡猾的黑客也会用各种方式绕开过滤规则,下面举几个例子。


大小写

如果网站只过滤了“<script>”,那么黑客使用“<sCrIpT>”,不会被过滤,攻击依然生效。

为防范这种情况,过滤危险内容时,也要考虑大小写的情况。


嵌套标签

如果只是对内容过滤一遍“<script>”,那么请看下面这行代码:

<scr<script>ipt>alert("XSS")</scr</script>ipt>

过滤掉<script>标签后,剩下的内容,正好能再次构成攻击语句。

为防范这种情况,对内容过滤后要再次判断是否含有危险代码。


不同的编码

比如利用url编码,“%3Cscript%3E”其实等效于<script>。

又比如javascript里的eval(),实际上可以运行unicode编码的内容,如:

eval("\u0061\u006c\u0065\u0072\u0074('XSS')")

为防范这种情况,对内容过滤还得考虑各种编码的情况。


特殊的HTML属性

<img>标签的src字段和<a>标签的href字段,都可以执行javascript代码,如:

<a href="javascript:alert('XSS')">某链接</a>

除此之外,还有onerror、onclick、onmouseover等,甚至IE8以前CSS的expression属性,都可以用于执行恶意代码。这些都要防范。


主动闭合

如果代码里本来就带有<script>标签,像是这样:

<body>
  <script>
    <?php 
      echo "var a='".$keyword."'";
    ?>;
  </script>
</body>

此时黑客制造一个主动闭合的攻击,链接如下:

http://test/search?keyword=';alert("XSS");'

这时候执行的代码就会变成:

var a=''; alert("XSS");

这就被黑客钻了空子。

同理,也可以直接用</script>标签来闭合,比如以下代码:

<script>
  var initData = <%= data.toJSON() %>
</script>

只要攻击者在json数据中包含</script>,就可以闭合当前的<script>,后面的内容就会被当作HTML解析。此时再使用一个<script>,就可以开始植入攻击代码了。

为了防范这种情况,在代码里使用<script>时,要格外小心。


其他防范措施

控制内容长度

如上文所述,网站通常会对用户输入的内容进行过滤。有很多流行的库,如Java的xssprotect、Node.js的node-validator等,都能过滤大部分的危险内容。黑客想绕开过滤机制,通常需要排列组合、不断尝试,构造复杂的注入脚本。因此,对用户输入的内容,做最大长度的限制,可以增加XSS攻击的难度。


任何用户输入等都要过滤

同SQL注入一样,储存型XSS攻击的写入方式,可不只有网页端提交内容。任何来自系统外部的内容,都应该进行过滤,如导入数据、扫码、OCR识别等等。


转义HTML字符

把HTML标签转义成字符,既能保证浏览器上的显示效果不变,其中的代码又不会被执行。主流的语言都提供转义的方法,如PHP的htmlentities()、htmlspecialchars(),ASP和ASP.NET的Server.HtmlEncode(),Python的cgi.escape(),JAVA的escapeHTML()、escapeEmbedJSON()等。


指定内容类型

使用Content-Type指定内容的类型,避免被作为HTML解析,一定程度上可以避免XSS攻击。


保护cookie和token的安全

参考我之前的博客《全面了解 Http Cookie》和《什么是Token》。建议登录态token使用cookie储存,并设置HttpOnly,可以避免被javascript脚本直接获取到。同时,任何重要操作(如修改密码),都要使用短信验证码等二次确认。


Content Security Policy

配置严格的CSP策略,可以禁止浏览器运行危险的脚本。下一篇博客有详细介绍。



总结

本文介绍了XSS攻击原理、攻击方式、常见的过滤和反过滤策略,以及其他常用的防范措施。




本文未经许可禁止转载,如需转载请联系 JAY@oonne.com


TOP