将数据从快速后端传递到客户端(JavaScript)的最佳方法是什么?这样可以使用某种客户端呈现在DOM中呈现数据,同时允许已清理的白名单HTML并仍然阻止XSS?
比方说,节点响应呈现方法如下所示:
res.render('index', {
data : {
foo: '<a href="myhomepage">foo</p>'
}
});
无论出于何种原因,它都包括未转义的角色。通常将它包含在html / ejs模板中将是微不足道的,例如:
<script>
myVar = JSON.parse('<%- JSON.stringify(data) %>');
</script>
但它在锚标记的第一个双引号上扼杀:Unexpected token h in JSON at position 18
我们绝对希望允许带有HTML字符的字符串(粗体,锚链接等等)但是想要删除脚本标记和其他此类危险标记。
有没有简单的方法来实现这一目标?或者是否必须遍历传递给render方法的所有数据级别,并通过某种XSS清理程序引擎运行所有字符串键?
答案 0 :(得分:0)
如果您需要允许某些不受信任的html,则可以使用外部库对其进行清理。我没有使用它,但评论中提到的DOMPurify看起来不错。在将myVar
的内容添加到DOM之前,可以在服务器或客户端上完成此操作。
另一个问题是将数据从服务器传输到客户端。这有一个单独的问题。 JSON.stringify
生成一个JavaScript字符串,但会解析两次。一次通过浏览器中的JavaScript引擎,加载脚本时,再由JSON.parse
加载。这导致转义引号在到达JSON.parse
之前未被转义,并且它以错误的方式解释它们。
如果data
是:
{ foo: '<a href="myhomepage">foo</p>' }
然后JSON.stringify
会产生:
{"foo":"<a href=\"myhomepage\">foo</p>"}
这将插入到html页面中,成为:
myVar = JSON.parse('{"foo":"<a href=\"myhomepage\">foo</p>"}');
此处\"
序列被JavaScript解析器解释为引号,因此它与以下内容相同:
myVar = JSON.parse('{"foo":"<a href="myhomepage">foo</p>"}');
现在,href引号被解释为属性的紧密引号,而不是属性的开放引号。
你可以解决这个问题,但JSON.parse
还有另一个问题。它不考虑任何周围的HTML上下文。因为它是
在<script>
标记内,如果数据是:
{ foo: '</script><script>alert(1)</script>' }
它将生成HTML:
<script>
myVar = JSON.parse('{"foo":"</script><script>alert(1)</script>"}');
</script>
这包含有效的JavaScript字符串。它不会从字符串中转义,而是直接从脚本标记中转义,并使用新脚本重新进入,从而导致XSS问题。
相反,你需要一些东西来逃避HTML和JS元字符。这样的事情应该做:
myVar = JSON.parse(unescape('<%- escape(JSON.stringify(data)) %>'));