Apa cara termudah untuk keluar dari HTML dengan Python?

138

cgi.escape sepertinya salah satu pilihan yang memungkinkan. Apakah ini bekerja dengan baik? Adakah yang dianggap lebih baik?

Josh Gibson
sumber

Jawaban:

177

cgi.escapebaik-baik saja. Ini lolos:

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

Itu cukup untuk semua HTML.

EDIT: Jika Anda memiliki karakter non-ascii yang juga ingin Anda hilangkan, untuk dimasukkan dalam dokumen yang dikodekan lain yang menggunakan pengkodean berbeda, seperti yang dikatakan Craig , gunakan saja:

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

Jangan lupa untuk decode datauntuk unicodepertama, menggunakan apa pun pengkodean itu dikodekan.

Namun menurut pengalaman saya, pengkodean semacam itu tidak berguna jika Anda hanya bekerja unicodesepanjang waktu dari awal. Cukup encode di akhir encoding yang ditentukan di header dokumen ( utf-8untuk kompatibilitas maksimum).

Contoh:

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

Yang juga perlu diperhatikan (terima kasih Greg) adalah quoteparameter ekstra yang cgi.escapedibutuhkan. Dengan menyetelnya ke True, cgi.escapejuga lolos dari chars petik ganda ( ") sehingga Anda bisa menggunakan nilai yang dihasilkan dalam atribut XML / HTML.

EDIT: Perhatikan bahwa cgi.escape sudah tidak digunakan lagi dengan Python 3.2 html.escape, yang melakukan hal yang sama kecuali quotedefaultnya adalah True.

nosklo.dll
sumber
7
Parameter boolean tambahan ke cgi.escape juga harus dipertimbangkan untuk meng-escape tanda kutip saat teks digunakan dalam nilai atribut HTML.
Greg Hewgill
Hanya untuk memastikan: Jika saya menjalankan semua data yang tidak dipercaya melalui cgi.escapefungsi, apakah cukup untuk melindungi dari semua (diketahui) XSS attacs?
Tomas Sedovic
@Tomas Sedovic: Tergantung di mana Anda akan meletakkan teks setelah menjalankan cgi.escape di dalamnya. Jika ditempatkan dalam konteks root HTML maka ya, Anda sepenuhnya aman.
nosklo
Bagaimana dengan masukan seperti {{Measures 12 Ω "H x 17 5/8" W x 8 7/8 "D. Imported.}} Itu bukan ascii, jadi encode () akan memberikan pengecualian pada Anda.
Andrew Kolesnikov
@ Andrew Kolesnikov: Sudahkah Anda mencobanya? cgi.escape(yourunicodeobj).encode('ascii', 'xmlcharrefreplace') == '{{Measures 12 &#937;"H x 17 5/8"W x 8 7/8"D. Imported.}}'- seperti yang Anda lihat, ekspresi mengembalikan bytestring ascii, dengan semua karakter unicode non-ascii dikodekan menggunakan tabel referensi karakter xml.
nosklo
119

Di Python 3.2, htmlmodul baru diperkenalkan, yang digunakan untuk meng-escape karakter yang dilindungi dari markup HTML.

Ini memiliki satu fungsi 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
sumber
Tentang apa quote=True?
2rs2t
1
@SalmanAbbas Apakah Anda takut bahwa kutipan tidak lolos? Perhatikan bahwa html.escape()tidak ada tanda kutip, secara default (sebaliknya, cgi.quote()tidak - dan hanya menghilangkan tanda kutip ganda, jika diberitahu). Jadi, saya harus secara eksplisit menetapkan parameter opsional untuk memasukkan sesuatu ke dalam atribut html.escape(), yaitu membuatnya tidak aman untuk atribut:t = '" onclick="alert()'; t = html.escape(t, quote=False); s = f'<a href="about.html" class="{t}">foo</a>'
maxschlepzig
@maxschlepzig Saya pikir yang dikatakan Salman escape()tidak cukup untuk membuat atribut aman. Dengan kata lain, ini tidak aman:<a href=" {{ html.escape(untrusted_text) }} ">
pianoJames
@pianoJames, begitu. Saya mempertimbangkan untuk memeriksa nilai link sebagai validasi semantik khusus domain. Bukan yang leksikal seperti melarikan diri. Selain Java Script sebaris, Anda benar-benar tidak ingin membuat tautan dari input pengguna yang tidak tepercaya tanpa validasi khusus URL lebih lanjut (misalnya karena Spammer). Metode sederhana untuk melindungi dari Java Script sebaris dalam atribut seperti href adalah dengan menyetel Kebijakan Keamanan Konten yang melarangnya.
maxschlepzig
@pianoJames Aman, karena html.escapetidak luput dari tanda kutip tunggal dan ganda.
Flimm
11

Jika Anda ingin keluar dari HTML di URL:

Ini mungkin BUKAN yang diinginkan OP (pertanyaannya tidak secara jelas menunjukkan dalam konteks mana escaping dimaksudkan untuk digunakan), tetapi urllib pustaka asli Python memiliki metode untuk keluar dari entitas HTML yang perlu disertakan dalam URL dengan aman.

Berikut ini adalah contohnya:

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

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

Temukan dokumen di sini

SuperFamousGuy
sumber
10
Ini adalah jenis pelarian yang salah; kami mencari pelolosan HTML , bukan penyandiaksaraan URL .
Chaosphere2112
7
Nontheless - itu yang sebenarnya saya cari ;-)
Brad
9

Ada juga paket markupsafe yang sangat baik .

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

The markupsafepaket baik rekayasa, dan mungkin yang paling serbaguna dan Pythonic cara untuk pergi tentang melarikan diri, IMHO, karena:

  1. return ( Markup) adalah kelas yang diturunkan dari unicode (misisinstance(escape('str'), unicode) == True
  2. itu dengan benar menangani masukan unicode
  3. ini bekerja dengan Python (2.6, 2.7, 3.3, dan pypy)
  4. itu menghormati metode kustom objek (yaitu objek dengan __html__properti) dan template overloads ( __html_format__).
Brian M. Hunt
sumber
7

cgi.escape harus baik untuk melepaskan diri dari HTML dalam arti terbatas dari keluar dari tag HTML dan entitas karakter.

Tetapi Anda mungkin juga harus mempertimbangkan masalah pengkodean: jika HTML yang ingin Anda kutip memiliki karakter non-ASCII dalam pengkodean tertentu, Anda juga harus berhati-hati untuk merepresentasikannya dengan tepat saat mengutip. Mungkin Anda bisa mengubahnya menjadi entitas. Jika tidak, Anda harus memastikan bahwa terjemahan encoding yang benar dilakukan antara HTML "sumber" dan halaman yang menyematkannya, untuk menghindari kerusakan karakter non-ASCII.

Craig McQueen
sumber
3

Tidak ada pustaka, python murni, dengan aman mengonversi teks menjadi teks html:

text.replace('&', '&amp;').replace('>', '&gt;').replace('<', '&lt;'
        ).encode('ascii', 'xmlcharrefreplace')
speedplane
sumber
1
Pemesanan Anda salah, surat &lt;wasiat lolos ke&amp;lt;
Jason S
@jason s Terima kasih atas perbaikannya!
speedplane
1

cgi.escape diperpanjang

Versi ini membaik cgi.escape. Ini juga mempertahankan spasi dan baris baru. Mengembalikan unicodestring.

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

sebagai contoh

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

Bukan cara termudah, tapi tetap lugas. Perbedaan utama dari modul cgi.escape - masih akan berfungsi dengan baik jika Anda sudah memiliki &amp;teks Anda. Seperti yang Anda lihat dari komentar untuk itu:

versi 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

versi 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)
palestamp
sumber
0

Untuk kode legacy di Python 2.7, bisa dilakukan melalui BeautifulSoup4 :

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