如何使用Jinja2转义HTML,以便在JavaScript(jQuery)中将其用作字符串?
如果我使用Django的模板系统,我可以编写:
$("#mydiv").append("{{ html_string|escapejs }}");
Django的|escapejs
过滤器会转义html_string
中可能破坏代码块预期使用的引号、特殊字符等内容,但Jinja2似乎没有相应的过滤器(我是不是错了?)。
除了从Django复制/粘贴代码外,是否有更简洁的解决方案?
如何使用Jinja2转义HTML,以便在JavaScript(jQuery)中将其用作字符串?
如果我使用Django的模板系统,我可以编写:
$("#mydiv").append("{{ html_string|escapejs }}");
Django的|escapejs
过滤器会转义html_string
中可能破坏代码块预期使用的引号、特殊字符等内容,但Jinja2似乎没有相应的过滤器(我是不是错了?)。
除了从Django复制/粘贴代码外,是否有更简洁的解决方案?
Jinja2有一个很好的过滤器tojson。如果您从字符串生成JSON,则会生成用双引号""括起来的字符串。这在JavaScript中是安全的。您无需手动添加引号。
$("#mydiv").append({{ html_string|tojson }});
它还转义了<>&
符号。因此,即使字符串包含像<script>
这样的XSS危险内容,它也是安全的。
这是一个基于Django的escapejs
过滤器,我为在Jinja2模板中使用而编写的:
_js_escapes = {
'\\': '\\u005C',
'\'': '\\u0027',
'"': '\\u0022',
'>': '\\u003E',
'<': '\\u003C',
'&': '\\u0026',
'=': '\\u003D',
'-': '\\u002D',
';': '\\u003B',
u'\u2028': '\\u2028',
u'\u2029': '\\u2029'
}
# Escape every ASCII character with a value less than 32.
_js_escapes.update(('%c' % z, '\\u%04X' % z) for z in xrange(32))
def jinja2_escapejs_filter(value):
retval = []
for letter in value:
if _js_escapes.has_key(letter):
retval.append(_js_escapes[letter])
else:
retval.append(letter)
return jinja2.Markup("".join(retval))
JINJA_ENVIRONMENT.filters['escapejs'] = jinja2_escapejs_filter
在模板中安全使用的示例:
<script type="text/javascript">
<!--
var variableName = "{{ variableName | escapejs }}";
…
//-->
</script>
当变量variableName是str
或unicode
类型时。
{{ variableName | espacejs }}
括起来,因此无法使用方括号技巧。否则,即使是一个空格也可能是危险的。 - Tometzky我去年也遇到过类似的问题。不确定你是否使用了bottle,但我的解决方案大致如下。
import json
def escapejs(val):
return json.dumps(str(val)) # *but see [Important Note] below to be safe
@app.route('/foo')
def foo():
return bottle.jinja2_template('foo', template_settings={'filters': {'escapejs': escapejs}})
(我将template_settings
字典包装在一个辅助函数中,因为我在各处都使用它,但在这个示例中,我保持它简单。)
不幸的是,它不像内置的jinja2过滤器那样简单,但我很高兴能够快乐地生活——特别是考虑到我还有其他几个自定义过滤器要添加。
重要提示:向@medmunds致敬,感谢他在下面的评论中提醒我们,json.dumps不安全。换句话说,在生产的、面向互联网的服务器上不应该使用它。建议编写更安全的json转义例程(或者窃取django的——对不起OP,我知道你希望避免这种情况),并调用它来代替使用json.dumps。
json.dumps
正是我需要的。比我想象的简单得多!谢谢! - meshyescapejs("</script>")
会返回 "</script>"
-- 这似乎可能允许一个闭合的脚本标记(和其后的任何内容!)泄漏到你的HTML中去。(Django的escapejs过滤器对<和>字符进行Unicode转义,可以避免这个问题。) - medmunds我刚刚研究了这个问题,我的解决方案是定义一个过滤器:
from flask import Flask, Markup
app = Flask(__name__)
app.jinja_env.filters['json'] = lambda v: Markup(json.dumps(v))
<script>
var myvar = {{myvar|json}} ;
</script>
myvar
= </script><script>alert('XSS');
时。 - Blaise基于 @tometzky 的代码,这是我的 Python 3 版本:
_js_escapes = {
'\\': '\\u005C',
'\'': '\\u0027',
'"': '\\u0022',
'>': '\\u003E',
'<': '\\u003C',
'&': '\\u0026',
'=': '\\u003D',
'-': '\\u002D',
';': '\\u003B',
u'\u2028': '\\u2028',
u'\u2029': '\\u2029'
}
# Escape every ASCII character with a value less than 32.
_js_escapes.update(('%c' % z, '\\u%04X' % z) for z in range(32))
@register.filter
def escapejs(value):
return jinja2.Markup("".join(_js_escapes.get(l, l) for l in value))
使用方法完全相同。
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
autoescape=True)
或者,你可以使用在Jinja 2.4中添加的Autoescape扩展,来更好地控制自动转义在HTML中的使用位置。有关此内容的更多信息在此处和示例(在Google应用引擎中)在此处。
Python:
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.autoescape'])
HTML:
{% autoescape true %}
<html>
<body>
{{ IWillBeEscaped }}
</body>
</html>
{% endautoescape %}
html_string
不包含任何有害字符。 - meshyjson.dumps()
函数将JavaScript字面量合并进去。 - Martijn Pieters