Экранировать строки для JavaScript с помощью Jinja2?

Как мне избежать HTML с помощью Jinja2, чтобы его можно было использовать как строку в JavaScript (jQuery)?

Если бы я использовал систему шаблонов Django, я мог бы написать:

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

Django |escapejs filter ускользнет от вещей в html_string ( например, кавычки, специальные символы), которые могут нарушить предполагаемое использование этого блока кода, но Jinja2, похоже, не имеет эквивалентного фильтра (я ошибаюсь?).

Есть ли более чистое решение, чем копирование/вставка кода из Django?


person meshy    schedule 09.09.2012    source источник
comment
См. здесь: jinja.pocoo.org/docs/templates/#escaping   -  person JAR.JAR.beans    schedule 09.09.2012
comment
Мне не нужно экранировать сам текст тега jinja, мне нужно убедиться, что html_string не содержит вредных символов.   -  person meshy    schedule 09.09.2012
comment
Возможно, вам нужен безопасный фильтр, чем: flask.pocoo.org/docs /templating/#стандартные фильтры   -  person JAR.JAR.beans    schedule 09.09.2012
comment
возможный дубликат Как лучше всего передать список из python в js с помощью бутылки?   -  person Martijn Pieters    schedule 09.09.2012
comment
@MartijnPieters Решение похожее, но вопрос в конечном итоге предназначался для pywebkitgtk, а не для бутылки ... Поскольку ответы на этот вопрос относятся к этой структуре, я не думаю, что это дубликат.   -  person meshy    schedule 09.09.2012
comment
@meshy: применяется тот же принцип. Включите литералы JavaScript с помощью функции json.dumps().   -  person Martijn Pieters    schedule 09.09.2012
comment
Jinja теперь имеет встроенный tojson фильтр. Кажется, это выводит его как строку с экранированием JS, а не как правильный объект JSON, поэтому вам нужно будет вызвать var my_data = JSON.parse({{ my_string|tojson }})   -  person Hartley Brody    schedule 21.03.2018
comment
@MartijnPieters На самом деле, теперь (начиная с версии 2.9) есть собственные решения для jinja2 (фильтр tojson).   -  person user202729    schedule 02.12.2018


Ответы (6)


Столкнулся с похожей проблемой в прошлом году. Не уверен, что вы используете бутылку, но мое решение выглядело примерно так.

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, вы бы не хотели использовать его на рабочем сервере с выходом в Интернет. Рекомендуется написать более безопасную процедуру выхода из json. (или украсть django - извините, OP, я знаю, что вы надеялись избежать этого) и вызовите это вместо использования json.dumps.

person ron rothman    schedule 09.09.2012
comment
Идеальный! Я не использую бутылку, я использую pywebkitgtk для создания настольного приложения, но json.dumps — это именно то, что мне нужно. Гораздо проще, чем я надеялся! Спасибо! - person meshy; 09.09.2012
comment
О, и спасибо за ссылку на бутылку! Такого еще не видел, может пригодится ;) - person meshy; 09.09.2012
comment
Я не думаю, что json.dumps() избавляет вас от всего, о чем вам нужно беспокоиться. Например, как написано в настоящее время, escapejs("</script>") возвращает "</script>", что, похоже, может привести к тому, что закрывающий тег script (и все, что после него!) просочится в ваш html. (Фильтр Django escapejs выполняет escape-последовательности Unicode для символов ‹ и ›, что позволяет избежать проблемы.) - person medmunds; 09.03.2013
comment
... да, похоже, что этот ответ определенно оставляет уязвимость XSS. в Академии Хана есть несколько примеров и предоставляет свои собственные фильтры escapejs и jsonify. Похоже, они позаимствовали свою реализацию escapejs от Джанго. - person medmunds; 09.03.2013
comment
@medmunds, спасибо за указание на уязвимость XSS. Ты прав; Я обновлю запись, чтобы указать на это. (FWIW, в моем случае мой сервис является внутренним, за брандмауэром, поэтому XSS-атака не была главной проблемой. Тем не менее я исправлю свой код, просто чтобы выработать правильную привычку.) - person ron rothman; 09.03.2013

Jinja2 имеет хороший фильтр tojson. Если вы сделаете json из строки, он сгенерирует строку, заключенную в двойные кавычки "". Вы можете безопасно использовать его в javascript. И вам не нужно ставить кавычки самостоятельно.

$("#mydiv").append({{ html_string|tojson }});
person Alexander Chzhen    schedule 18.11.2018

Это фильтр 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 является str или unicode.

person Tometzky    schedule 19.09.2013
comment
Уже требуется, чтобы {{ variableName | espacejs }} был в кавычках (одинарных или двойных), поэтому трюки с квадратными скобками невозможны. В противном случае даже пространство может быть опасным. - person Tometzky; 21.09.2013
comment
Этот ответ помог мне в этой теме. Настоящая головоломка для экранирования символов внутри строки, которая будет напечатана из Python в HTML, которая будет находиться внутри тегов кавычек внутри JavaScript, который будет анализироваться как JSON. Спасибо за помощь! - person Matthisk; 18.03.2016

Я только что исследовал эту проблему, мое решение - определить фильтр:

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.

person Mike Richardson    schedule 09.10.2013
comment
Не делайте этого с пользовательским контентом, так как пользователи смогут выполнять JS. Например, когда myvar = </script><script>alert('XSS'); - person Blaise; 15.07.2016

На основе @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))

Использование точно такое же.

person Sebastian Wagner    schedule 27.06.2019

Вы также можете использовать autoescape из jinja2. Так, например, вы можете добавить автоэкранирование в свою среду jinja2 в Python:

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

В качестве альтернативы вы можете использовать расширение Autoescape, добавленное в Jinja 2.4, чтобы иметь больший контроль над тем, где используется автоматическое экранирование в HTML. Подробнее об этом здесь и пример (в Google App Engine) здесь.

Питон:

JINJA_ENVIRONMENT = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
    extensions=['jinja2.ext.autoescape'])

HTML:

{% autoescape true %}
    <html>
        <body>
            {{ IWillBeEscaped }}
        </body>
    </html>
{% endautoescape %}
person Tony Wickham    schedule 06.07.2013
comment
Разве это не просто стандартное экранирование HTML? Вопрос касается Javascript? - person pip; 28.11.2014
comment
Нет, это определенно экранирование только HTML. - person Simon Steinberger; 02.01.2015