【问题标题】:Escape strings for JavaScript using Jinja2?使用 Jinja2 为 JavaScript 转义字符串?
【发布时间】:2012-09-09 14:12:57
【问题描述】:

如何使用 Jinja2 转义 HTML 以便它可以用作 JavaScript (jQuery) 中的字符串?

如果我使用 Django 的模板系统,我可以这样写:

$("#mydiv").append("{{ html_string|escapejs }}");

Django 的|escapejs filter 会转义html_string 中的内容(例如引号、特殊字符),这可能会破坏此代码块的预期用途,但Jinja2 似乎没有等效的过滤器(我错了吗?)。

有没有比从 Django 复制/粘贴代码更简洁的解决方案?

【问题讨论】:

标签: javascript python jinja2


【解决方案1】:

Jinja2 有很好的过滤器tojson。如果从字符串生成 json,它将生成用双引号“”括起来的字符串。您可以在 javascript 中安全地使用它。而且你不需要自己加上引号。

$("#mydiv").append({{ html_string|tojson }});

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

【讨论】:

    【解决方案2】:

    这是一个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

    【讨论】:

    • 已经要求{{ variableName | espacejs }} 在引号中(单引号或双引号),因此方括号技巧是不可能的。否则即使是空间也可能是危险的。
    • 这个答案帮助我解决了这个问题。真正的头脑弯曲者将字符串中的字符转义,该字符串将从 Python 打印到 HTML,该 HTML 将位于 JavaScript 内的引号标签内,该标签将解析为 JSON。感谢您的帮助!
    【解决方案3】:

    去年我也遇到过类似的问题。不确定您是否使用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 dict 包装在一个辅助函数中,因为我到处都使用它,但在这个例子中我保持简单。)

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

    重要提示:感谢 @medmunds 在下面的精明评论,提醒我们 json.dumps 不是 XSS 安全的。 IOW,您不希望在面向 Internet 的生产服务器中使用它。建议是写一个safer json escape routine(或者偷 django 的——抱歉 OP,我知道你希望避免这种情况)并调用它而不是使用 json.dumps。

    【讨论】:

    • 完美!我没有使用瓶子,我正在使用 pywebkitgtk 构建桌面应用程序,但 json.dumps 正是我所需要的。比我希望的要简单得多!谢谢!
    • 哦,感谢您提供瓶子的链接!我以前没见过,可以派上用场;)
    • 我认为 json.dumps() 不会逃避您需要担心的一切。例如,正如目前所写的那样,escapejs("&lt;/script&gt;") 返回"&lt;/script&gt;"——这似乎允许结束脚本标记(以及它之后的任何内容!)泄漏到您的 html 中。 (Django escapejs 过滤器对 字符进行 unicode 转义,从而避免了该问题。)
    • ...是的,似乎这个答案肯定会留下 XSS 漏洞。可汗学院has some examples here 并提供他们自己的 escapejs 和 jsonify 过滤器。看起来他们从 Django 借来了their implementation of escapejs
    • @medmunds,感谢您指出 XSS 漏洞。你是对的;我将更新条目以指出这一点。 (FWIW,在我的情况下,我的服务是内部的,在防火墙后面,所以 XSS 攻击不是最重要的问题。不过我会修复我的代码,只是为了养成正确的习惯。)
    【解决方案4】:

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

    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 序列化的东西

    【讨论】:

    • 不要对用户生成的内容执行此操作,因为用户将能够执行 JS。例如当myvar = &lt;/script&gt;&lt;script&gt;alert('XSS');
    【解决方案5】:

    基于@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))
    

    用法完全一样。

    【讨论】:

      【解决方案6】:

      你也可以使用jinja2的autoescape。因此,例如,您可以在 Python 中将自动转义添加到您的 jinja2 环境中:

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

      或者,您可以使用 Jinja 2.4 中添加的 Autoescape 扩展来更好地控制在 HTML 中使用自动转义的位置。有关此 here 和示例(在 Google App Engine 中)here 的更多信息。

      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 转义吗?这个问题是关于 Javascript 的?
      • 不,那肯定只是 HTML 转义。
      猜你喜欢
      • 2014-01-27
      • 2013-12-03
      • 2015-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多