Django的escapejs过滤器与XSS
我想用Django的模板语言来处理一些JavaScript变量。我的情况是这样的,foo
是一个用户定义的数据字符串(也就是不太可信的数据),我想把它转换成JavaScript字符串。
<!doctype html>
<html>
<head>
<script>
bar = '{{ foo|escapejs }}';
</script>
</head>
<body>
</body>
</html>
如果我没理解错的话,按照Django的文档,像这样使用escapejs
会有XSS攻击的风险。
我想到了一个可能的解决办法,就是使用HTML5的data-*
属性,像这样。
<!doctype html>
<html>
<head>
<script>
window.onload = function () {
bar = document.getElementById('data').getAttribute('data-bar');
};
</script>
</head>
<body>
<div id="data" style="display:none;" data-bar="{{ foo }}"></div>
</body>
</html>
不过,我在想有没有更简单或者更标准的方法来做到这一点。
1 个回答
escapejs
的输出在把 JavaScript 放在 HTML 文本里或者放在 HTML 属性值里时是安全的。但如果是放在没有引号的 HTML 属性值里,就不安全了(其实 escape
对于没有引号的 HTML 属性也不安全,所以无论如何都要加引号)。
这样做是可以的,因为它会把所有 HTML 特殊字符转换成 JavaScript 字符串的格式 \u
,这些格式里不包含 HTML 特殊字符。可以查看 django.utils.html
里的 _base_js_escapes
。它还会把输出标记为“安全”,所以你希望这不是个谎言。
至于这是否应该是安全的,以及未来的 Django 版本是否能保证它一直安全,这点不太清楚,因为你提到的文档并没有说明。它可能只是想说,这种转义方式不适合普通的 HTML:虽然现在的实现并不是说对普通 HTML 的转义不安全,但你肯定会得到错误的输出,里面会有多余的反斜杠。你觉得自己运气好吗?
我想到了一个可能的解决方案,使用 HTML5 的 data-* 属性,像这样。
无论如何,我都会这样做。把 JavaScript 嵌入到模板里很容易出错,而且直接在页面里写 JavaScript 有点乱,未来也不方便使用内容安全策略。
更好的做法是把所有页面数据放在 HTML 属性里,使用同样的安全 HTML 自动转义,然后让 JavaScript 从 DOM 中获取这些数据。当然,如果你想包含结构化数据而不仅仅是字符串,可以把它们 JSON 编码。
(不过我不会去弄一个手动的隐形 div……你可以把属性加到 <body>
或者其他与数据相关的元素上。)