Qual è il modo più semplice per sfuggire all'HTML in Python?


137

cgi.escape sembra una possibile scelta. Funziona bene? C'è qualcosa che è considerato migliore?

Risposte:


176

cgi.escapeè ok. Sfugge:

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

Questo è abbastanza per tutto l'HTML.

EDIT: Se hai caratteri non ascii che vuoi scappare, per l'inclusione in un altro documento codificato che utilizza una codifica diversa, come dice Craig , basta usare:

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

Non dimenticare di decodificare dataper unicodeprimo, usando qualunque codifica sia stata codificata.

Tuttavia, nella mia esperienza, quel tipo di codifica è inutile se lavori unicodesempre dall'inizio. Basta codificare alla fine della codifica specificata nell'intestazione del documento ( utf-8per la massima compatibilità).

Esempio:

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

Degno di nota (grazie Greg) è il quoteparametro aggiuntivo cgi.escapeprende. Se impostato su True, cgi.escapeevita anche i caratteri di virgolette doppie ( ") in modo da poter utilizzare il valore risultante in un attributo XML / HTML.

EDIT: Nota che cgi.escape è stato deprecato in Python 3.2 a favore di html.escape, che fa lo stesso tranne che per quoteimpostazione predefinita è True.


7
Il parametro booleano aggiuntivo a cgi.escape dovrebbe essere preso in considerazione anche per evitare le virgolette quando il testo viene utilizzato nei valori degli attributi HTML.
Greg Hewgill,

Solo per essere sicuri: se eseguo tutti i dati non attendibili attraverso la cgi.escapefunzione, è sufficiente per proteggermi da tutti gli attacchi XSS (noti)?
Tomas Sedovic,

@Tomas Sedovic: dipende da dove inserirai il testo dopo aver eseguito cgi.escape al suo interno. Se inserito in un contesto HTML root, sì, sei completamente sicuro.
nosklo,

Che dire input come {{Misura 12 Ω "H x 17 5/8" W x 8 7/8 "D. Importato.}} Questo non è ASCII, quindi encode () genererà un'eccezione per te
Andrew Kolesnikov il

@Andrew Kolesnikov: l'hai provato? cgi.escape(yourunicodeobj).encode('ascii', 'xmlcharrefreplace') == '{{Measures 12 &#937;"H x 17 5/8"W x 8 7/8"D. Imported.}}'- come puoi vedere, l'espressione restituisce ascii bytestring, con tutti i caratteri unicode non ascii codificati usando la tabella di riferimento dei caratteri xml.
nosklo,

112

In Python 3.2 htmlè stato introdotto un nuovo modulo, che viene utilizzato per sfuggire ai caratteri riservati dal markup HTML.

Ha una funzione 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;'

Che dire quote=True?
ore

1
@SalmanAbbas Hai paura che le virgolette non siano sfuggite? Si noti che html.escape()sfugge alle virgolette, per impostazione predefinita (al contrario, cgi.quote()non - e sfugge solo alle doppie virgolette, se detto). Quindi, devo impostare esplicitamente un parametro opzionale con cui iniettare qualcosa in un attributo html.escape(), cioè renderlo insicuro per gli attributi:t = '" onclick="alert()'; t = html.escape(t, quote=False); s = f'<a href="about.html" class="{t}">foo</a>'
maxschlepzig

@maxschlepzig Penso che Salman stia dicendo che escape()non è abbastanza per rendere sicuri gli attributi. In altre parole, questo non è sicuro:<a href=" {{ html.escape(untrusted_text) }} ">
pianoJames

@pianoJames, capisco. Considero il controllo dei valori dei collegamenti una convalida semantica specifica del dominio. Non lessicale come scappare. Oltre a Java Script incorporato, non si desidera creare collegamenti da input dell'utente non attendibile senza ulteriore convalida specifica dell'URL (ad esempio a causa degli spammer). Un metodo semplice per proteggere da Java Script in linea in attributi come href è quello di impostare una politica di sicurezza del contenuto che non lo consente.
maxschlepzig

@pianoJames È sicuro, perché html.escapesfugge alle virgolette singole e alle virgolette doppie.
Flimm

11

Se desideri sfuggire all'HTML in un URL:

Questo probabilmente NON è quello che voleva l'OP (la domanda non indica chiaramente in quale contesto si debba usare l'escaping), ma l' URLlib della libreria nativa di Python ha un metodo per sfuggire alle entità HTML che devono essere incluse in un URL in modo sicuro.

Quanto segue è un esempio:

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

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

Trova documenti qui


10
Questo è il tipo sbagliato di fuga; stiamo cercando escape HTML , anziché codifica URL .
Chaosphere2112,

7
Tuttavia - era quello che stavo effettivamente cercando ;-)
Brad

9

C'è anche l'eccellente pacchetto markupsafe .

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

Il markupsafepacchetto è ben progettato, e probabilmente il modo più versatile e Pythonic per evadere, IMHO, perché:

  1. il ritorno ( Markup) è una classe derivata da unicode (cioèisinstance(escape('str'), unicode) == True
  2. gestisce correttamente l'input Unicode
  3. funziona in Python (2.6, 2.7, 3.3 e pypy)
  4. rispetta i metodi personalizzati di oggetti (ovvero oggetti con una __html__proprietà) e template overload ( __html_format__).

7

cgi.escape dovrebbe essere buono per sfuggire all'HTML nel senso limitato di sfuggire ai tag HTML e alle entità carattere.

Ma potresti dover considerare anche i problemi di codifica: se l'HTML che vuoi citare ha caratteri non ASCII in una particolare codifica, allora dovresti anche preoccuparti di rappresentarli sensibilmente durante il preventivo. Forse potresti convertirli in entità. In caso contrario, è necessario assicurarsi che vengano eseguite le traduzioni di codifica corrette tra l'HTML "sorgente" e la pagina in cui è incorporato, per evitare di corrompere i caratteri non ASCII.


3

Nessuna libreria, puro Python, sfugge al testo in modo sicuro nel testo HTML:

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

1
Il tuo ordine è sbagliato, &lt;verrà evaso&amp;lt;
Jason S

@jason s Grazie per la correzione!
speedplane,

1

cgi.escape esteso

Questa versione migliora cgi.escape. Conserva anche gli spazi bianchi e le nuove righe. Restituisce una unicodestringa.

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

per esempio

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

1

Non è il modo più semplice, ma comunque semplice. La differenza principale rispetto al modulo cgi.escape - funzionerà ancora correttamente se hai già &amp;nel tuo testo. Come vedi dai commenti ad esso:

versione 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

versione 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

Per il codice legacy in Python 2.7, puoi farlo tramite BeautifulSoup4 :

>>> bs4.dammit import EntitySubstitution
>>> esub = EntitySubstitution()
>>> esub.substitute_html("r&d")
'r&amp;d'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.