XSS防御相关—HTML和JavaScript的自解码次序

来源:互联网 发布:精灵虚拟光驱 mac 编辑:程序博客网 时间:2024/05/18 04:01

我们对XSS的防御办法一般会采取针对关键敏感字符进行编码的方式,被称为XSS终结者的CSP先暂且不提,大部分人都会采用htmlencode的方式来限制危险字符输出,但这真的足够了吗?

在web2py(python的Web框架)的安全文档中说将所有变量在视图层进行eacape,可以防御XSS,看似好像并没有什么不对,但如果出现以下情况呢?

<a href=# onclick="alert('$var');">XSS Test</a>

$var是获取用户的输入,在进行渲染到浏览器之前,web2py框架会对它进行htmlencode编码,假设用户输入为:

$var = ');alert(666

编码之后,结果为:

&#x27;&#x29;&#x3b;alert&#x28;&#x27;666

此时,DOM的渲染结果是这样的:

<a href=# onclick="alert('&#x27;&#x29;&#x3b;alert&#x28;&#x27;666');">XSS Test</a>

这样转码照常理来说应该肯定不会有问题了吧?人家都输出实体编号了,怎么XSS。但是,你点一下试试,看会不会弹两个窗。

结果弹了!而且HTML编码分三种,实体编号十进制表示,实体编号十六进制表示,实体名称表示,上面的就是十六进制表示方式。其中任何一种编码都可以弹窗,为什么?

因为浏览器在JavaScript执行之前对它进行自解码了,并且,在这里解码的方式还有很大区别。

这里的关键就是上下文语境,什么意思?就是变量的位置附近对应的是HTML标签还是JavaScript代码,如果是像上文一样的HTML标签(a标签),那么浏览器在执行JavaScript语句之前会优先执行HTML解码操作,所以上面web2py框架的所谓escape防御根本没用。

那什么样的语境是优先进行JavaScript解码呢?类似下面这样:

function $(name){    return document.getElementByTagName(name);}$('a').onclick = function(){    alert('&#x27;&#x29;&#x3b;alert&#x28;&#x27;666');}

这种情况上下文语境就会优先进行JS解码,所以上面点击以后不会弹两次窗。

如果你把上面的十六进制HTML编码换成JS的编码,会弹两次窗吗?不会。因为虽然自解码,但是被当成了字符串,同时这也是防御用户输入变量出现在js环境中被XSS的办法,进行JS编码。

那怎么才能弹呢?像下面这样,JS的自解码会成为帮凶:

document.write('\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x23\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29\x3e');

以上部分被解码后是:

<img src=# onerror=alert('xss')>

之后被渲染到页面,完成弹窗。

JS有哪些编码呢?

  1. 四位Unicode 形式:\u ,不足补零
  2. 十六进制:\xH
  3. 八进制:\
  4. 纯转义:\’、\”、\<、>等

所以,单纯的htmlencode并不能彻底防御XSS,要根据实际的语境来决定编码方式。

与之类似的,在CSS中也有这种自解码次序引发的问题,下面两个CSS语句在IE中效果是相同的:

color: expression(alert(1))
color: expression\028 alert \028 1 \029 \029

参考:
道哥 《白帽子讲Web安全》
余弦 《Web前端黑客技术揭秘》

0 0
原创粉丝点击