04 November 2016

问题示例代码


出现xss注入的视图页面部分代码:

<script>
window.onload = function () {
    var url = '<?php echo htmlspecialchars($_GET['url']); ?>';
    div = document.createElement('div');
    div.innerHTML = '<iframe src="' + url + '"></iframe>';
    document.body.appendChild(div);
}
</script>

如果请求的url是:

http://test.com?url=\42\40\157\156\154\157\141\144\75\42\141\154\145\162\164\50\47\170\163\163\47\51\42\76\74\57\151\146\162\141\155\145\76\74\151\146\162\141\155\145\40\163\162\143\75\42

其中,url参数的值为八进制转义序列,转义后为:

" onload="alert('xss')"></iframe><iframe src="

那么就会产生一个alert弹框,内容为xss。

在线编码解码:http://www.jb51.net/tools/zhuanhuan.htm

分析


首先查看浏览器渲染出来的html是什么样子的:

php中输出的是原样的八进制转义序列,在JS中,这样的字符串会被转义(无论是单引号包裹还是双引号包裹着,而PHP对于单引号包裹的字符串不会做转义处理),如下在控制台可以看到转义结果:

为何php的htmlspecialchars函数没法对$_GET['url']进行编码?难道PHP不支持八进制转义序列,从PHP文档中Escape sequences可知道是支持的,只是转义序列是在字符串字面量(String literal)或者正则表达式的模式中才生效。所以$_GET['url']的内容不会被PHP进行转义,但是输出到前端时,在JS中,$_GET['url']的内容就变为JS的字符串字面量了,会被JS进行转义。

问题解决


当将PHP输出的内容直接输出为JS代码的一部分时,JS最好做一层encode或者过滤/校验操作,比如解决上面的xss问题代码如下:

<script>
window.onload = function () {
    var url = encodeURI('<?php echo htmlspecialchars($_GET['url']); ?>');
    div = document.createElement('div');
    div.innerHTML = '<iframe src="' + url + '"></iframe>';
    document.body.appendChild(div);
}
</script>

浏览器渲染出来的html: