Какой самый простой способ избежать HTML в Python?


137

Cgi.escape кажется одним из возможных вариантов. Это хорошо работает? Есть ли что-то, что считается лучше?

Ответы:


176

cgi.escapeЭто хорошо. Это ускользает от:

  • < в &lt;
  • > в &gt;
  • & в &amp;

Этого достаточно для всего HTML.

РЕДАКТИРОВАТЬ: Если у вас есть не ascii символы, которые вы также хотите экранировать, для включения в другой кодированный документ, который использует другую кодировку, как говорит Крейг , просто используйте:

data.encode('ascii', 'xmlcharrefreplace')

Не забудьте декодирование dataк unicodeпервому, используя любой кодирующим он был закодирован.

Однако, по моему опыту, такая кодировка бесполезна, если вы просто unicodeвсе время работаете с самого начала. Просто закодируйте в конце кодировку, указанную в заголовке документа ( utf-8для максимальной совместимости).

Пример:

>>> cgi.escape(u'<a>bá</a>').encode('ascii', 'xmlcharrefreplace')
'&lt;a&gt;b&#225;&lt;/a&gt;

Также стоит отметить (спасибо Грег) дополнительный quoteпараметр cgi.escapeпринимает. С ней установлена True, cgi.escapeтакже экранирует двойные кавычки символов ( ") , так что вы можете использовать полученное значение в атрибуте XML / HTML.

РЕДАКТИРОВАТЬ: Обратите внимание, что cgi.escape устарела в Python 3.2 в пользу html.escape, который делает то же самое, за исключением того, что по quoteумолчанию True.


7
Дополнительный логический параметр для cgi.escape также следует учитывать для экранирования кавычек, когда текст используется в значениях атрибутов HTML.
Грег Хьюгилл

Просто чтобы быть уверенным: если я запускаю все ненадежные данные через cgi.escapeфункцию, достаточно ли защиты от всех (известных) атак XSS?
Томас Седович

@Tomas Sedovic: Зависит от того, куда вы поместите текст после запуска в нем cgi.escape. Если поместить в корневой контекст HTML, тогда да, вы в полной безопасности.
Носкло

А как насчет ввода типа {{Меры 12 Ω "В x 17 5/8" Ш x 8 7/8 "D. Импортировано.}} Это не ascii, поэтому encode () выдаст вам исключение.
Андрей Колесников

@ Андрей Колесников: Вы пробовали это? cgi.escape(yourunicodeobj).encode('ascii', 'xmlcharrefreplace') == '{{Measures 12 &#937;"H x 17 5/8"W x 8 7/8"D. Imported.}}'- как вы можете видеть, выражение возвращает ascii bytestring со всеми не-ascii символами Unicode, закодированными с использованием справочной таблицы символов xml.
Носкло

112

В Python 3.2 htmlбыл представлен новый модуль, который используется для экранирования зарезервированных символов из разметки HTML.

Имеет одну функцию escape():

>>> import html
>>> html.escape('x > 2 && x < 7 single quote: \' double quote: "')
'x &gt; 2 &amp;&amp; x &lt; 7 single quote: &#x27; double quote: &quot;'

Как насчет quote=True?
2rs2ts

1
@SalmanAbbas Вы боитесь, что кавычки не экранированы Обратите внимание, что html.escape()по умолчанию экранирует кавычки (в отличие от cgi.quote()этого - не экранирует двойные кавычки, если так сказано). Таким образом, я должен явно установить необязательный параметр, чтобы внедрить что-либо в атрибут html.escape(), то есть сделать его небезопасным для атрибутов:t = '" onclick="alert()'; t = html.escape(t, quote=False); s = f'<a href="about.html" class="{t}">foo</a>'
maxschlepzig

@maxschlepzig Я думаю, что Салман говорит, escape()что недостаточно, чтобы сделать атрибуты безопасными. Другими словами, это небезопасно:<a href=" {{ html.escape(untrusted_text) }} ">
pianoJames

@pianoJames, я вижу. Я считаю, что проверка значений ссылок является предметно-ориентированной семантической проверкой. Не лексический как бегство. Помимо встроенного Java Script, вы действительно не хотите создавать ссылки из ненадежного пользовательского ввода без дальнейшей специфической проверки URL (например, из-за спаммеров). Простой способ защиты от встроенного Java Script в таких атрибутах, как, например, href - установить Политику безопасности контента, которая запрещает ее.
maxschlepzig

@pianoJames Это безопасно, потому html.escapeчто экранирует одинарные и двойные кавычки.
Flimm

11

Если вы хотите экранировать HTML в URL:

Вероятно, это НЕ то, что хотел ОП (вопрос не ясно указывает, в каком контексте предполагается использовать экранирование), но в родной библиотеке Python urllib есть метод для экранирования сущностей HTML, которые необходимо безопасно включать в URL.

Ниже приведен пример:

#!/usr/bin/python
from urllib import quote

x = '+<>^&'
print quote(x) # prints '%2B%3C%3E%5E%26'

Найти документы здесь


10
Это неправильный вид побега; мы ищем экранирование HTML , а не кодирование URL .
Chaosphere2112,

7
Тем не менее - это было то, что я действительно искал ;-)
Брэд

9

Существует также превосходный пакет markupsafe .

>>> from markupsafe import Markup, escape
>>> escape("<script>alert(document.cookie);</script>")
Markup(u'&lt;script&gt;alert(document.cookie);&lt;/script&gt;')

markupsafeПакет хорошо разработан, и , вероятно , самый универсальный и Pythonic путь о побеге, ИМХО, потому что:

  1. return ( Markup) - это класс, полученный из юникода (т.е.isinstance(escape('str'), unicode) == True
  2. он правильно обрабатывает ввод Unicode
  3. это работает в Python (2.6, 2.7, 3.3 и pypy)
  4. он учитывает пользовательские методы объектов (т. е. объекты со __html__свойством) и перегрузки шаблона ( __html_format__).

7

cgi.escape должно быть хорошо избегать HTML в ограниченном смысле, избегая тегов HTML и символьных сущностей.

Но вам, возможно, придется учитывать и проблемы с кодировкой: если HTML-код, который вы хотите процитировать, содержит символы, не входящие в ASCII, в определенной кодировке, вам также следует позаботиться о том, чтобы вы представляли их разумно при цитировании. Возможно, вы могли бы преобразовать их в сущности. В противном случае вы должны убедиться, что между исходным HTML и страницей, в которую он встроен, сделаны правильные переводы кодировки, чтобы не повредить не-ASCII-символы.


3

Нет библиотек, чистый Python, безопасно экранирует текст в HTML-текст:

text.replace('&', '&amp;').replace('>', '&gt;').replace('<', '&lt;'
        ).encode('ascii', 'xmlcharrefreplace')

1
Ваш заказ неправильный, вам &lt;будет спасен&amp;lt;
Джейсон С

@jason s Спасибо за исправление!
скоростной самолет

1

cgi.escape расширенный

Эта версия улучшается cgi.escape. Это также сохраняет пробелы и переводы строки. Возвращает unicodeстроку.

def escape_html(text):
    """escape strings for display in HTML"""
    return cgi.escape(text, quote=True).\
           replace(u'\n', u'<br />').\
           replace(u'\t', u'&emsp;').\
           replace(u'  ', u' &nbsp;')

например

>>> escape_html('<foo>\nfoo\t"bar"')
u'&lt;foo&gt;<br />foo&emsp;&quot;bar&quot;'

1

Не самый простой способ, но все же простой. Основное отличие от модуля cgi.escape - он все равно будет работать правильно, если у вас уже есть &amp;текст. Как видно из комментариев к нему:

версия cgi.escape

def escape(s, quote=None):
    '''Replace special characters "&", "<" and ">" to HTML-safe sequences.
    If the optional flag quote is true, the quotation mark character (")
is also translated.'''
    s = s.replace("&", "&amp;") # Must be done first!
    s = s.replace("<", "&lt;")
    s = s.replace(">", "&gt;")
    if quote:
        s = s.replace('"', "&quot;")
    return s

версия регулярного выражения

QUOTE_PATTERN = r"""([&<>"'])(?!(amp|lt|gt|quot|#39);)"""
def escape(word):
    """
    Replaces special characters <>&"' to HTML-safe sequences. 
    With attention to already escaped characters.
    """
    replace_with = {
        '<': '&gt;',
        '>': '&lt;',
        '&': '&amp;',
        '"': '&quot;', # should be escaped in attributes
        "'": '&#39'    # should be escaped in attributes
    }
    quote_pattern = re.compile(QUOTE_PATTERN)
    return re.sub(quote_pattern, lambda x: replace_with[x.group(0)], word)

0

Для устаревшего кода в Python 2.7, можете сделать это через BeautifulSoup4 :

>>> bs4.dammit import EntitySubstitution
>>> esub = EntitySubstitution()
>>> esub.substitute_html("r&d")
'r&amp;d'
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.