Jaki jest najłatwiejszy sposób na ucieczkę z HTML w Pythonie?


137

cgi.escape wydaje się być jednym z możliwych wyborów. Czy to działa dobrze? Czy jest coś, co uważa się za lepsze?

Odpowiedzi:


176

cgi.escapejest w porządku. Ucieka:

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

To wystarczy dla całego HTML.

EDYCJA: Jeśli masz znaki inne niż ASCII, z których również chcesz uciec, aby włączyć je do innego zakodowanego dokumentu, który używa innego kodowania, jak mówi Craig , po prostu użyj:

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

Nie zapomnij dekodowania datado unicodepierwszego, z wykorzystaniem co kodowania został zakodowany.

Jednak z mojego doświadczenia wynika, że ​​ten rodzaj kodowania jest bezużyteczny, jeśli pracujesz z nim unicodecały czas od początku. Po prostu zakoduj na końcu zgodnie z kodowaniem określonym w nagłówku dokumentu (utf-8 dla maksymalnej kompatybilności).

Przykład:

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

Warto również zwrócić uwagę (dzięki Greg) na dodatkowy quoteparametr cgi.escape. Ustawiając ją na True, cgi.escaperównież wyłącza "znak podwójnego cudzysłowu ( ), dzięki czemu można użyć wynikowej wartości w atrybucie XML / HTML.

EDYCJA: Zauważ, że cgi.escape został przestarzały w Pythonie 3.2 na korzyść html.escape, który robi to samo, z wyjątkiem tego, że quotedomyślnie ma wartość True.


7
Dodatkowy parametr boolowski cgi.escape powinien być również brany pod uwagę w celu zmiany znaczenia znaków cudzysłowu, gdy tekst jest używany w wartościach atrybutów HTML.
Greg Hewgill

Dla pewności: jeśli uruchomię wszystkie niezaufane dane za pośrednictwem cgi.escapefunkcji, czy wystarczy, aby zabezpieczyć się przed wszystkimi (znanymi) atakami XSS?
Tomas Sedovic

@Tomas Sedovic: Zależy, gdzie umieścisz tekst po uruchomieniu w nim cgi.escape. Jeśli umieścisz go w głównym kontekście HTML, to tak, jesteś całkowicie bezpieczny.
nosklo

A co z danymi wejściowymi takimi jak {{Środki 12 Ω "H x 17 5/8" W x 8 7/8 "D. Imported.}} To nie jest ascii, więc encode () rzuci wyjątek.
Andrew Kolesnikov

@Andrew Kolesnikov: Czy próbowałeś tego? cgi.escape(yourunicodeobj).encode('ascii', 'xmlcharrefreplace') == '{{Measures 12 &#937;"H x 17 5/8"W x 8 7/8"D. Imported.}}'- jak widać, wyrażenie zwraca ascii bytestring, ze wszystkimi znakami Unicode innymi niż ASCII zakodowanymi przy użyciu tabeli odwołań znaków xml.
nosklo

112

W Pythonie 3.2 htmlzostał wprowadzony nowy moduł, który służy do ucieczki znaków zastrzeżonych ze znaczników HTML.

Ma jedną funkcję 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;'

O co chodzi quote=True?
2rs2ts

1
@SalmanAbbas Boisz się, że nie uciekniesz przed cytatami? Zwróć uwagę, że html.escape()domyślnie nie stosuje cudzysłowów (w przeciwieństwie do cgi.quote()tego nie robi - i wyłącza tylko cudzysłowy, jeśli tak powiedziano). W związku z tym muszę jawnie ustawić opcjonalny parametr, aby wstrzyknąć coś do atrybutu html.escape(), tj. Aby uczynić go niebezpiecznym dla atrybutów:t = '" onclick="alert()'; t = html.escape(t, quote=False); s = f'<a href="about.html" class="{t}">foo</a>'
maxschlepzig

@maxschlepzig Myślę, że Salman twierdzi, że escape()nie wystarczy, aby atrybuty były bezpieczne. Innymi słowy, to nie jest bezpieczne:<a href=" {{ html.escape(untrusted_text) }} ">
pianoJames

@pianoJames, rozumiem. Uważam sprawdzenie wartości linków za semantyczną walidację specyficzną dla domeny. Nie leksykalny, jak ucieczka. Oprócz wbudowanego skryptu Java, naprawdę nie chcesz tworzyć linków z niezaufanych danych wejściowych użytkownika bez dalszej weryfikacji konkretnego adresu URL (np. Z powodu spamerów). Prostą metodą ochrony przed wbudowanymi skryptami Java w atrybutach, takich jak, href jest ustawienie polityki bezpieczeństwa treści, która na to nie zezwala.
maxschlepzig

@pianoJames Jest to bezpieczne, ponieważ pomija html.escapepojedyncze i podwójne cudzysłowy.
Flimm

11

Jeśli chcesz wyjść z kodu HTML w adresie URL:

Prawdopodobnie NIE jest to to, czego chciał OP (pytanie nie wskazuje jasno, w jakim kontekście ucieczka ma być używana), ale natywna biblioteka Pythona urllib ma metodę ucieczki z jednostek HTML, które muszą być bezpiecznie zawarte w adresie URL.

Oto przykład:

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

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

Znajdź dokumenty tutaj


10
To jest niewłaściwy sposób ucieczki; szukamy ucieczki HTML , a nie kodowania adresów URL .
Chaosphere2112

7
Niemniej jednak - tego właśnie szukałem ;-)
Brad

9

Istnieje również doskonały pakiet zabezpieczający przed znacznikami .

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

markupsafePakiet jest dobrze zaprojektowane, i prawdopodobnie najbardziej wszechstronny i pythonowy droga o ucieczce, IMHO, ponieważ:

  1. return ( Markup) jest klasą pochodzącą z Unicode (tjisinstance(escape('str'), unicode) == True
  2. poprawnie obsługuje dane wejściowe Unicode
  3. działa w Pythonie (2.6, 2.7, 3.3 i pypy)
  4. szanuje niestandardowe metody obiektów (tj. obiekty z __html__właściwością) i przeciążenia szablonów ( __html_format__).

7

cgi.escape powinna być dobra ucieczka przed HTML w ograniczonym sensie ucieczki przed znacznikami HTML i jednostkami znakowymi.

Ale być może będziesz musiał również wziąć pod uwagę problemy z kodowaniem: jeśli HTML, który chcesz zacytować, zawiera znaki spoza ASCII w określonym kodowaniu, musisz również uważać, aby rozsądnie je przedstawiać podczas cytowania. Być może mógłbyś przekształcić je w byty. W przeciwnym razie należy upewnić się, że między „źródłowym” kodem HTML a stroną, na której jest osadzony, wykonywane są prawidłowe tłumaczenia kodowania, aby uniknąć uszkodzenia znaków spoza zestawu ASCII.


3

Brak bibliotek, czysty Python, bezpiecznie zapisuje tekst w tekście html:

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

1
Twoje zamówienie jest złe, &lt;testament uciekł do&amp;lt;
Jason S

@jason s Dzięki za poprawkę!
speedplane

1

cgi.escape rozszerzony

Ta wersja jest ulepszona cgi.escape. Zachowuje również spacje i nowe linie. Zwraca unicodeciąg.

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

na przykład

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

1

Nie jest to najłatwiejszy sposób, ale nadal prosty. Główna różnica w stosunku do modułu cgi.escape - nadal będzie działać poprawnie, jeśli już masz&amp; w tekście. Jak widać z komentarzy do tego:

Wersja 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

wersja regex

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

W przypadku starszego kodu w Pythonie 2.7 można to zrobić za pośrednictwem BeautifulSoup4 :

>>> bs4.dammit import EntitySubstitution
>>> esub = EntitySubstitution()
>>> esub.substitute_html("r&d")
'r&amp;d'
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.