xss

什么是xss

value = "<script>alert(1);</script>"
<p><%=value%></p>

生成的html:

<p><script>alert(1);</script></p>

这样就被执行了

解决办法

<p><%-value%></p>

生成的html:

<p>&lt;script&gt;alert(1);&lt;/script&gt;</p>

在浏览器上的表现:

<script>alert(1);</script>

html转义

为什么要转义

怎么转义

模板 转义 原样输出
很多老模板 <%-text%> <%=htmlString%>
react 默认转义 <div dangerouslySetInnerHTML={htmlString}>
vue 默认转义 <div v-html="htmlString">
artTemplate 默认转义 {{@htmlString}}
jade pug 默认转义 div!=htmlString

例:要输出一个</p>字符串到页面,那html就会是:

什么应该不转义

<div>
    <%=html%>
</div>

什么应该转移(剩下的全部情况)

<img src="<%-src%>">
<div>
    <%-text%>
</div>

script标签内

在script标签内,都是js代码,和 html 完全没有关系,浏览器不会把他们当做 html 代码解析。所以 script内 不使用 html 转义规则。

我们一般只将server数据 输出到script标签内供浏览器代码使用。比如我们将server端变量 pageData 传递给浏览器js使用:

<script>
var pageData = <%=json_dump(pageData)%>
</script>

在浏览器js中通过 window.pageDate 就可以获取到。

json_dump 就是 script标签 专用的转义方法, 它是一个 npm 库,其他语言可以参考源码:

module.exports = function(it){
    // handle with `undefined`
    if(it == null){
        return 'null';
    }
	return JSON.stringify(it).replace(/<\/(script)/ig,'<\\/$1');
};

错误的方法:

<script>
var a="<%-a%>"; 
// 当 a 为 "h&m" 时 将会变成
var a="h&amp;m" //原来的数据是 h&m

错误的方法2:

<script>
var a="<%=a%>"; 
// 当 a 为 'a";alert(1)"'
// 将会变成以下
var a="a";alert(1)"" //被注入
</script>

错误的方法3:

<script>
var a="<%=a%>"; 
// 当 a 为 "</script><script>alert(1);</script>"
// 将会变成以下
var a="</script><script>alert(1);</script>" //被注入 (script标签遇到 </script> 这几个字符就会结束)
</script>

js

写入:

只有涉及到操作html的时候才会有xss的问题:

这些情况需要转义或者使用.text(text)方法替代。

.text('<>')是不需要转义的,因为不是设置html所以不需要html解析器解析,修改属性(.prop('src',src))也一样

读取:

    <div id="d" data-text="&amp;">&lt;</div>
$('#d').text(); // "<"
$('#d').html(); // "&lt;" 

$('#d').data('text'); // "&"

Tips

javascript 转义代码

function encodeHtml(str){
    if(str==null){
        return '';
    }
    str+='';
    var nStr='';
    for (var i = 0; i < str.length; i++) {
        var c=str[i];
        switch(c){
            case '\'':nStr+='&#39;';break;
            case '"':nStr+='&#34;';break;
            case '<':nStr+='&lt;';break;
            case '>':nStr+='&gt;';break;
            case '&':nStr+='&amp;';break;
            default: nStr+=c;break;
        }
    }
    return nStr;
};

保留换行

强烈推荐使用 css : white-space: pre-wrap;

不推荐: <%=encodeHtml(text).replace(/\n/g, '<br/>')%>

其他方法都会有安全问题