Was ist der einfachste Weg, um HTML in Python zu entkommen?

137

cgi.escape scheint eine mögliche Wahl zu sein. Funktioniert es gut Gibt es etwas, das als besser angesehen wird?

Josh Gibson
quelle

Antworten:

175

cgi.escapeist gut. Es entkommt:

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

Das reicht für alles HTML.

BEARBEITEN: Wenn Sie Nicht-ASCII-Zeichen haben, möchten Sie auch entkommen, um sie in ein anderes codiertes Dokument aufzunehmen, das eine andere Codierung verwendet, wie Craig sagt. Verwenden Sie einfach:

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

Vergessen Sie nicht , zu dekodieren , dataum unicodeerste, mit was auch immer dafür kodierenden codiert wurde.

Nach meiner Erfahrung ist diese Art der Codierung jedoch nutzlos, wenn Sie unicodevon Anfang an immer mit ihnen arbeiten. Codieren Sie einfach am Ende mit der im Dokumentkopf angegebenen Codierung ( utf-8für maximale Kompatibilität).

Beispiel:

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

Bemerkenswert (danke Greg) ist auch der zusätzliche quoteParameter cgi.escape. Wenn es auf gesetzt ist, Truewird cgi.escapeauch chars ( ") in doppelten Anführungszeichen maskiert, sodass Sie den resultierenden Wert in einem XML / HTML-Attribut verwenden können.

BEARBEITEN: Beachten Sie, dass cgi.escape in Python 3.2 zugunsten von veraltet ist html.escape, was dasselbe tut, außer dass quotestandardmäßig True verwendet wird.

nosklo
quelle
7
Der zusätzliche boolesche Parameter zu cgi.escape sollte auch berücksichtigt werden, um Anführungszeichen zu maskieren, wenn Text in HTML-Attributwerten verwendet wird.
Greg Hewgill
Nur um sicher zu gehen: Wenn ich alle nicht vertrauenswürdigen Daten über die cgi.escapeFunktion ausführe , reicht dies aus, um mich vor allen (bekannten) XSS-Attacs zu schützen?
Tomas Sedovic
@Tomas Sedovic: Hängt davon ab, wo Sie den Text platzieren, nachdem Sie cgi.escape ausgeführt haben. Wenn Sie in den HTML-Stammkontext gestellt werden, sind Sie absolut sicher.
Nosklo
Was ist mit Eingaben wie {{misst 12 Ω "H x 17 5/8" B x 8 7/8 "D. Importiert.}} Das ist nicht ascii, also wird encode () eine Ausnahme auf Sie werfen.
Andrew Kolesnikov
@ Andrew Kolesnikov: Hast du es versucht? cgi.escape(yourunicodeobj).encode('ascii', 'xmlcharrefreplace') == '{{Measures 12 &#937;"H x 17 5/8"W x 8 7/8"D. Imported.}}'- Wie Sie sehen können, gibt der Ausdruck ASCII-Bytestring zurück, wobei alle Nicht-ASCII-Unicode-Zeichen mithilfe der XML-Zeichenreferenztabelle codiert werden.
Nosklo
111

In Python 3.2 wurde ein neues htmlModul eingeführt, mit dem reservierte Zeichen aus dem HTML-Markup entfernt werden.

Es hat eine Funktion 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;'
Maciej Ziarko
quelle
Was ist mit quote=True?
2rs2ts
1
@SalmanAbbas Haben Sie Angst, dass Zitate nicht entkommen? Beachten Sie, dass html.escape()Anführungszeichen standardmäßig nicht verwendet werden (im Gegensatz dazu cgi.quote()nicht - und nur doppelte Anführungszeichen, wenn dies mitgeteilt wird). Daher muss ich explizit einen optionalen Parameter festlegen, um etwas in ein Attribut html.escape()t = '" onclick="alert()'; t = html.escape(t, quote=False); s = f'<a href="about.html" class="{t}">foo</a>'
einzufügen
@maxschlepzig Ich denke, Salman sagt, dass escape()es nicht ausreicht, um Attribute sicher zu machen. Mit anderen Worten, dies ist nicht sicher:<a href=" {{ html.escape(untrusted_text) }} ">
PianoJames
@pianoJames, ich verstehe. Ich betrachte das Überprüfen von Linkwerten als domänenspezifische semantische Validierung. Keine lexikalische wie Flucht. Neben Inline-Java-Skript möchten Sie wirklich keine Links aus nicht vertrauenswürdigen Benutzereingaben ohne weitere URL-spezifische Validierung erstellen (z. B. aufgrund von Spammern). Eine einfache Methode zum Schutz vor Inline-Java-Skripten in Attributen wie href ist das Festlegen einer Inhaltssicherheitsrichtlinie, die dies nicht zulässt.
Maxschlepzig
@pianoJames Es ist sicher, weil html.escapeeinfache Anführungszeichen und doppelte Anführungszeichen entgehen.
Flimm
11

Wenn Sie HTML in einer URL maskieren möchten:

Dies ist wahrscheinlich NICHT das, was das OP wollte (die Frage gibt nicht eindeutig an, in welchem ​​Kontext das Escapezeichen verwendet werden soll), aber Pythons native Bibliothek urllib verfügt über eine Methode, um HTML-Entitäten zu maskieren, die sicher in eine URL aufgenommen werden müssen.

Das Folgende ist ein Beispiel:

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

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

Hier finden Sie Dokumente

SuperFamousGuy
quelle
10
Dies ist die falsche Art der Flucht; Wir suchen nach HTML-Escapezeichen im Gegensatz zur URL-Codierung .
Chaosphere2112
7
Trotzdem - es war das, wonach ich eigentlich gesucht habe ;-)
Brad
9

Es gibt auch das ausgezeichnete markupsafe Paket .

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

Das markupsafePaket ist ausgereift und wahrscheinlich der vielseitigste und pythonischste Weg, um zu entkommen, IMHO, weil:

  1. Die return ( Markup) ist eine von Unicode abgeleitete Klasse (dhisinstance(escape('str'), unicode) == True
  2. Unicode-Eingaben werden ordnungsgemäß verarbeitet
  3. es funktioniert in Python (2.6, 2.7, 3.3 und pypy)
  4. Es berücksichtigt benutzerdefinierte Methoden von Objekten (dh Objekten mit einer __html__Eigenschaft) und Vorlagenüberladungen ( __html_format__).
Brian M. Hunt
quelle
7

cgi.escape sollte gut sein, um HTML im begrenzten Sinne der HTML-Tags und Zeichenentitäten zu entkommen.

Möglicherweise müssen Sie jedoch auch Codierungsprobleme berücksichtigen: Wenn der HTML-Code, den Sie zitieren möchten, Nicht-ASCII-Zeichen in einer bestimmten Codierung enthält, müssen Sie auch darauf achten, dass Sie diese beim Zitieren sinnvoll darstellen. Vielleicht könnten Sie sie in Entitäten konvertieren. Andernfalls sollten Sie sicherstellen, dass die korrekten Codierungsübersetzungen zwischen dem HTML-Quellcode und der eingebetteten Seite durchgeführt werden, um eine Beschädigung der Nicht-ASCII-Zeichen zu vermeiden.

Craig McQueen
quelle
3

Keine Bibliotheken, reines Python, entkommen sicher Text in HTML-Text:

text.replace('&', '&amp;').replace('>', '&gt;').replace('<', '&lt;'
        ).encode('ascii', 'xmlcharrefreplace')
Geschwindigkeitsflugzeug
quelle
1
Ihre Bestellung ist falsch, die &lt;wird entkommen&amp;lt;
Jason S
@ jason s Danke für das Update!
Speedplane
1

cgi.escape verlängert

Diese Version verbessert sich cgi.escape. Außerdem bleiben Leerzeichen und Zeilenumbrüche erhalten. Gibt eine unicodeZeichenfolge zurück.

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;')

beispielsweise

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

Nicht der einfachste Weg, aber dennoch unkompliziert. Der Hauptunterschied zum cgi.escape- Modul - es funktioniert immer noch einwandfrei, wenn Sie es bereits &amp;in Ihrem Text haben. Wie Sie aus den Kommentaren sehen:

cgi.escape Version

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

Regex-Version

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)
Palestamp
quelle
0

Für Legacy-Code in Python 2.7 ist dies über BeautifulSoup4 möglich :

>>> bs4.dammit import EntitySubstitution
>>> esub = EntitySubstitution()
>>> esub.substitute_html("r&d")
'r&amp;d'
scharfmn
quelle