使用Jinja2的JavaScript转义字符串

Escape strings for JavaScript using Jinja2?

本文关键字:转义字符 字符串 转义 JavaScript Jinja2 使用      更新时间:2023-09-26

如何使用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 }});

它还转义<>&符号。因此,即使sting包含类似<script> 的XSS危险内容,它也是安全的

这是一个escapejs过滤器,基于Django的过滤器,我为在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是strunicode时。

去年我也遇到过类似的问题。不确定你们是否在用瓶子,但我的解决方案看起来像这样。

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 dict封装在一个助手函数中,因为我在任何地方都使用它,但在这个例子中我保持了简单。)

不幸的是,它没有内置的jinja2过滤器那么简单,但我能够愉快地使用它——尤其是考虑到我还有其他几个自定义过滤器要添加。

重要提示:感谢@medmunds在下面的精明评论,提醒我们json.dumps不安全。IOW,你不会想在生产中使用它,面向互联网的服务器。建议编写一个更安全的json转义例程(或者窃取django的--抱歉,OP,我知道你希望避免这种情况)并调用它,而不是使用json.dumps。

我刚刚研究了这个问题,我的解决方案是定义一个过滤器:

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可以是任何可以是JSON序列化的

基于@mometzky,这里是我的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))

用法完全相同。

您还可以使用jinja2的autoescape。因此,例如,您可以在Python:中将autoescape添加到您的jinja2环境中

JINJA_ENVIRONMENT = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
    autoescape=True)

或者,您可以使用Jinja2.4中添加的自动转义扩展来更好地控制HTML中自动转义的使用位置。这里有更多关于这方面的信息,这里有示例(在谷歌应用引擎中)。

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 %}