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