Який найпростіший спосіб уникнути HTML у Python?


137

cgi.escape здається одним із можливих варіантів. Це добре працює? Чи є щось, що вважається кращим?

Відповіді:


176

cgi.escapeдобре. Це втече:

  • < до &lt;
  • > до &gt;
  • & до &amp;

Цього достатньо для всього HTML.

EDIT: Якщо у вас є символи, що не мають права 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.

EDIT: Зауважте, що cgi.escape був застарілим у Python 3.2 на користь html.escape, що робить те саме, за винятком quoteзначень True.


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

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

@Tomas Sedovic: Залежить від того, куди ви покладете текст після запуску cgi.escape у ньому. Якщо розміщено в кореневому HTML-контексті, так, ви повністю безпечні.
nosklo

Як щодо введення на зразок {{Заходи 12 Ω "H x 17 5/8" W x 8 7/8 "D. Imported.}} Це не ascii, тому encode () кине на вас виняток.
Андрій Колесніков,

@Andrew Kolesnikov: Ви пробували? cgi.escape(yourunicodeobj).encode('ascii', 'xmlcharrefreplace') == '{{Measures 12 &#937;"H x 17 5/8"W x 8 7/8"D. Imported.}}'- як ви бачите, вираз повертає ascii bytestring, з усіма символами unicode unicode, кодованими за допомогою контрольної таблиці символів xml.
nosklo

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, ви дійсно не хочете створювати посилання з ненадійного введення користувача без додаткової перевірки URL-адреси (наприклад, через спамерів). Простий метод захисту від вбудованого сценарію Java в таких атрибутах, як href встановлення політики безпеки вмісту, яка забороняє його.
maxschlepzig

@pianoJames Це безпечно, тому html.escapeщо уникає одиничних і подвійних лапок.
Flimm

11

Якщо ви хочете залишити HTML у URL-адресі:

Це, мабуть, НЕ, чого хотіла ОП (питання не чітко вказує, в якому контексті передбачається використовувати епізод), але у власній URL-адресі бібліотеки Python є метод уникнути 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) - клас, похідний від unicode (тобто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.



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.