关于 XSS 防范的一些思考

标签: xss 思考 | 发表时间:2016-02-25 04:35 | 作者:keakon
出处:https://www.keakon.net/
最近在看一些 web 安全相关的文章,大部分都有系统和完善的解决方案,然而 XSS(Cross-site scripting)攻击相关的资料却很杂乱,甚至连 HTML 实体转义能解决哪些地方的 XSS 攻击都说不清。
于是在翻了一堆资料后,觉得还是把自己对它的一些思考记录下来吧。

先要说明的是,不同的地方,有不同的方式避免 XSS:
  • HTML 标签的文本部分,例如:
        <div>{user_input}</div>
    如果 user_data 里包含了 HTML 标签,那么展示的外观(可以加个 img 标签)和逻辑(可以加个 script 标签)都可能被篡改。所以这里至少要把 < 字符转义成 &lt;,也就不会开启和关闭任何标签了。
    单独的 > 字符并不会关闭标签,所以它可以不处理。但是输出成 XML(如 RSS)或 XHTML 格式时,单独的 > 会造成页面解析失败,所以这种情况下需要转义。
    此外,用户在输入 &amp; 的时候,是希望它显示成 &amp; 的,而结果却显示成了 &,这也是不符合预期的。所以 & 字符也应该被转义成 &amp;
    另一个小问题是连续的空格和回车可能都被当成了一个空格,这可以用 white-space: pre-wrap 的样式来处理,也可以替换成 &nbsp;<br>,或者直接不管。
    综上,这里的解决方案是至少转义 <& 字符:
        def escape_html_text(string):
    	return string.replace('&', '&amp;').replace('<', '&lt;')
        function escape_html_text(string) {
    	return string.replace('&', '&amp;').replace('<', '&lt;');
    }
  • HTML 标签的属性部分,例如:
        <input value="{user_input}">
    这里自然也不能允许用户输入 <& 字符。
    此外,如果能输入 > 的话,就可以轻松地关闭 input 标签了,所以 > 需要被转义成 &gt;
    而如果允许输入引号的话,这个属性就能被关闭,然后用户可以接着插入其他属性,所以 "' 需要被转义成 &quot;&#x27;&apos; 是 XML 里的 entity,在 HTML 里没有定义,所以更推荐用前者)。
    此外,IE 允许用 ` 字符作为属性的分割符:
        <input value=`{user_input}`>
    所以有需要的话,也可以转义 ` 字符。但如果你能保证你的的代码里只使用 "' 作为属性分割符,那就可以不处理。
    另一个更重要的事是属性一定要用引号包围起来,虽然写成下面的格式也是可以的,但很难保证不被 XSS 攻击:
        <input value={user_input}>
    如果非要这么做的话, OWASP 建议将属性值中所有的 ASCII 字符都编码成 &#xHH; 的形式。
    回到我们的解决方案,考虑到需要替换的次数太多,所以改用正则表达式效率更高些:
        import re
    
    escape_pattern = re.compile(r'[&<>"\']')
    escape_map = {
    	'&': '&amp;',
    	'<': '&lt;',
    	'>': '&gt;',
    	'"': '&quot;',
    	"'": '&apos;'
    }
    
    def replacer(match):
        return escape_map[match.group(0)]
    
    def escape_html_attr(string):
        return escape_pattern.sub(replacer, string)
        var escape_map = {
    	'&': '&amp;',
    	'<': '&lt;',
    	'>': '&gt;',
    	'"': '&quot;',
    	"'": '&#x27;'
    };
    function replacer(char) {
    	return escape_map[char];
    }
    function escape_html_attr(string) {
    	return string.replace(/[&<>"']/g, replacer);
    }
    其实,Python 也可以直接用 cgi.escape 函数,至少我懒得写这些代码。
    另外,这种替换也适用于 HTML 标签的文本部分,只是输出的字节数可能会变多而已,没别的副作用。
  • HTML 标签的网址属性部分,例如:
        <img src="{user_input}">
    <a href="{user_input}">{user_input}</a>
    <button onclick="{user_input}">
    <link ref="{user_input}" href="{user_input}">
    <script src="{user_input}">
    它们的处理规则其实并不一样:
    • 图片的网址需要对 HTML 属性做编码。
    • 链接的地址需要判断是否是合法的网址(最好是用户输入时就提示),否则用户可以输入 javascript:alert(0) 之类的代码。
    • 后面几个应该不允许用户输入。
  • URL 的参数部分,例如:
        <a href="/path?key={user_input}">...</a>
    需要对参数进行百分号编码( percent-encoding),JavaScript 有内置的 encodeURIComponent 函数(会忽略字母、数字和 - _ . ! ~ * ' ( ) 这些字符),Python 可以用 urllib.quote(url, '-_.!~*()')(我觉得 ' 字符还是比较危险的,不应该忽略)。
  • script 标签的文本部分:
        <script>
    var value = {user_input};
    var value = "{user_input}";
    </script>
    第一种情况应该不允许用户输入。
    第二种情况要考虑 value 之后怎么使用(比如有没有用来 eval,有没有用来生成 HTML 等)。即使后续使用没有在任何有危险的地方,它的处理也很麻烦。 OWASP 的建议是把所有 ASCII 字符,除字母和数字以外,都转成 \xHH 的形式。
    代码我就懒得写了,避免出现这种情况才是正道。有兴趣的可以看 ESAPI 的实现,有很多种语言的版本。
  • style 标签的文本部分:
        <style>
    {user_input}
    body {background-url: "{user_input}"}
    </style>
    都不应该允许用户输入。某些允许用户自定义样式的网站,如果会对别人有效,应该提供模板和可选的值,因为这里面能 XSS 攻击的地方太多了。
以上的 XSS 大都还只涉及一种环境,而浏览器不但能解析 HTML,还能解析 JavaScript 和 CSS,同时还得保证 URL 的正确性。所以有时候既要进行 HTML entity 编码,又要进行 JavaScript Hex 编码,还得考虑百分号编码。

此外,还有一些比较省事的解决方案,例如使用久经考验的模板引擎。但建议在已经熟悉 XSS 解决方案的基础上再使用,否则很可能即使用了也还是有 XSS 漏洞。
很多 XSS 风险其实也是来自图省事的心态。如果创建 HTML 元素时,用 document.createElement 方法,然后手动设置各个属性和子元素,自然会安全很多;但拼成字符串,然后直接传给 jQuery()innerHTML 无疑会方便很多,可是风险也就随之突显出来了。

最后,本文还只是粗浅地介绍了一些常见的解决方案,而 XSS 的种类却远非这么几种。
所有输出了不可信数据的地方,都有被 XSS 攻击的可能性。多思考一下用户的输入是否能达到意想不到的结果,并且随着时间的推移,长期地去检视这些可能的隐患点,因为未来很可能出现之前没有发现的攻击方式(例如浏览器的 bug 和没有正确地配置 web 服务器等)。

相关 [xss 思考] 推荐:

关于 XSS 防范的一些思考

- - keakon的涂鸦馆
最近在看一些 web 安全相关的文章,大部分都有系统和完善的解决方案,然而 XSS(Cross-site scripting)攻击相关的资料却很杂乱,甚至连 HTML 实体转义能解决哪些地方的 XSS 攻击都说不清. 于是在翻了一堆资料后,觉得还是把自己对它的一些思考记录下来吧. 先要说明的是,不同的地方,有不同的方式避免 XSS:.

深掘XSS漏洞场景之XSS Rootkit

- jyf1987 - 80sec
深掘XSS漏洞场景之XSS Rootkit[完整修订版]. 众所周知XSS漏洞的风险定义一直比较模糊,XSS漏洞属于高危漏洞还是低风险漏洞一直以来都有所争议. XSS漏洞类型主要分为持久型和非持久型两种:. 非持久型XSS漏洞一般存在于URL参数中,需要访问黑客构造好的特定URL才能触发漏洞. 持久型XSS漏洞一般存在于富文本等交互功能,如发帖留言等,黑客使用的XSS内容经正常功能进入数据库持久保存.

XSS 探索 - big-brother

- - 博客园_首页
正常的页面被渗出了攻击者的js脚本,这些脚本可以非法地获取用户信息,然后将信息发送到attacked的服务端. XSS是需要充分利用输出环境来构造攻击脚本的. 非法获取用户cookie、ip等内容. 劫持浏览器,形成DDOS攻击. Reflected XSS:可以理解为参数型XSS攻击,攻击的切入点是url后面的参数.

前端xss攻击

- - SegmentFault 最新的文章
实习的时候在项目中有接触过关于xss攻击的内容,并且使用了项目组中推荐的一些常用的防xss攻击的方法对项目进行了防攻击的完善. 但一直没有时间深入了解这东西,在此,做一个简单的梳理. xss跨站脚本攻击(Cross Site Scripting),是一种经常出现在web应用中的计算机安全漏洞,它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入的恶意html代码会被执行,从而达到恶意用户的特殊目的.

百度知道XSS漏洞

- - 博客园_首页
事情的起因是我一同学在百度知道上看到一个很奇怪的,正文带有连接的提问( 这里),正常来说,这种情况是不可能出现的. 我条件反射的想到了:XSS漏洞. 通过查看源代码,我马上发现了问题的根源:未结束的标签.
帮我写一个能提取

pentesterlab xss漏洞分析

- - JavaScript - Web前端 - ITeye博客
pentesterlab简介. pentesterlab官方定义自己是一个简单又十分有效学习渗透测试的演练平台. pentesterlab环境搭建. 官方提供了一个基于debian6的镜像,官网下载镜像,使用vmware建立一个虚拟机,启动即可. ps:官方文档建议做一个host绑定,方便后面使用.

XSS攻击及防御

- - BlogJava-qileilove
XSS又称CSS,全称Cross SiteScript,跨站脚本攻击,是Web程序中常见的漏洞,XSS属于被动式且用于客户端的攻击方式,所以容易被忽略其危害性. 其原理是攻击者向有XSS漏洞的网站中输入(传入)恶意的HTML代码,当其它用户浏览该网站时,这段HTML代码会自动执行,从而达到攻击的目的.

XSS攻击技术详解

- - BlogJava-qileilove
  XSS攻击:跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆. web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中. 比如这些代码包括HTML代码和客户端脚本. 攻击者利用XSS漏洞旁路掉访问控制--例如同源策略(same origin policy).

XSS攻击及防御

- - 互联网 - ITeye博客
       本文来自: 高爽|Coder,原文地址: http://blog.csdn.net/ghsau/article/details/17027893,转载请注明.         XSS又称CSS,全称Cross SiteScript,跨站脚本攻击,是Web程序中常见的漏洞,XSS属于被动式且用于客户端的攻击方式,所以容易被忽略其危害性.

[xss学习]xss基础内容之抛砖引玉篇

- - 神刀网
1、最基本的xss漏洞成因  . XSS测试代码:   xss.php?name= xss.php?name= xss.php?name=