Cách dễ nhất để thoát HTML trong Python là gì?


137

cgi.escape dường như là một lựa chọn có thể. Nó có hoạt động tốt không? Có một cái gì đó được coi là tốt hơn?

Câu trả lời:


176

cgi.escapeỔn. Nó trốn thoát:

  • < đến &lt;
  • > đến &gt;
  • & đến &amp;

Thế là đủ cho tất cả HTML.

EDIT: Nếu bạn có ký tự không phải mã ascii, bạn cũng muốn thoát, để đưa vào một tài liệu được mã hóa khác sử dụng mã hóa khác, như Craig nói, chỉ cần sử dụng:

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

Đừng quên để decode datađể unicodeđầu tiên, sử dụng bất cứ mã hóa nó đã được mã hóa.

Tuy nhiên theo kinh nghiệm của tôi, loại mã hóa là vô dụng nếu bạn chỉ làm việc với unicodetất cả thời gian từ đầu. Chỉ cần mã hóa ở cuối mã hóa được chỉ định trong tiêu đề tài liệu ( utf-8để tương thích tối đa).

Thí dụ:

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

Cũng đáng lưu ý (cảm ơn Greg) là quotetham số thêm cgi.escapemất. Với nó được đặt thành True, cgi.escapecũng thoát khỏi ký tự trích dẫn kép ( ") để bạn có thể sử dụng giá trị kết quả trong thuộc tính XML / HTML.

EDIT: Lưu ý rằng cgi.escape đã không được dùng trong Python 3.2 html.escape, điều này cũng tương tự ngoại trừ quotemặc định là True.


7
Tham số boolean bổ sung cho cgi.escape cũng nên được xem xét để thoát dấu ngoặc kép khi văn bản được sử dụng trong các giá trị thuộc tính HTML.
Greg Hewgill

Chỉ cần chắc chắn: Nếu tôi chạy tất cả dữ liệu không đáng tin cậy thông qua cgi.escapechức năng, liệu có đủ để bảo vệ chống lại tất cả các attacs XSS (đã biết) không?
Tomas Sedovic

@Tomas Sedovic: Phụ thuộc vào nơi bạn sẽ đặt văn bản sau khi chạy cgi.escape trong đó. Nếu được đặt trong ngữ cảnh HTML gốc thì có, bạn hoàn toàn an toàn.
nosklo

Điều gì về đầu vào như {{Biện pháp 12 Ω "H x 17 5/8" W x 8 7/8 "D. Nhập khẩu.}} Đó không phải là ascii, vì vậy, mã hóa () sẽ ném ngoại lệ vào bạn.
Andrew Kolesnikov

@Andrew Kolesnikov: Bạn đã thử chưa? cgi.escape(yourunicodeobj).encode('ascii', 'xmlcharrefreplace') == '{{Measures 12 &#937;"H x 17 5/8"W x 8 7/8"D. Imported.}}'- như bạn có thể thấy, biểu thức trả về ascii bytestring, với tất cả các ký tự unicode không mã hóa được mã hóa bằng bảng tham chiếu ký tự xml.
nosklo

112

Trong Python 3.2, một htmlmô-đun mới đã được giới thiệu, được sử dụng để thoát các ký tự dành riêng khỏi đánh dấu HTML.

Nó có một chức năng 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;'

Thế còn quote=True?
2rs2ts

1
@SalmanAbas Bạn có sợ rằng trích dẫn không thoát được không? Lưu ý rằng html.escape()không có dấu ngoặc kép, theo mặc định (ngược lại, cgi.quote()không - và chỉ thoát dấu ngoặc kép, nếu được nói như vậy). Vì vậy, tôi phải đặt một cách rõ ràng một tham số tùy chọn để đưa một cái gì đó vào một thuộc tính html.escape(), nghĩa là làm cho nó không an toàn cho các thuộc tính:t = '" onclick="alert()'; t = html.escape(t, quote=False); s = f'<a href="about.html" class="{t}">foo</a>'
maxschlepzig

@maxschlepzig Tôi nghĩ rằng Salman đang nói escape()là không đủ để làm cho các thuộc tính an toàn. Nói cách khác, điều này không an toàn:<a href=" {{ html.escape(untrusted_text) }} ">
pianoJames

@pianoJames, tôi hiểu rồi. Tôi xem xét việc kiểm tra các giá trị liên kết một xác nhận ngữ nghĩa cụ thể của miền. Không phải là một từ vựng như thoát. Bên cạnh Java Script nội tuyến, bạn thực sự không muốn tạo liên kết từ đầu vào của người dùng không đáng tin cậy mà không cần xác thực cụ thể thêm URL (ví dụ: vì Spammers). Một phương pháp đơn giản để bảo vệ chống lại Java Script nội tuyến trong các thuộc tính như href là đặt Chính sách bảo mật nội dung không tuân theo nó.
maxschlepzig

@pianoJames Nó an toàn, vì html.escapekhông thoát dấu ngoặc đơn và dấu ngoặc kép.
Flimm

11

Nếu bạn muốn thoát HTML trong một URL:

Đây có lẽ KHÔNG phải là điều OP muốn (câu hỏi không chỉ rõ trong bối cảnh thoát được sử dụng trong bối cảnh nào), nhưng urllib của thư viện riêng của Python có một phương pháp để thoát các thực thể HTML cần được đưa vào URL một cách an toàn.

Sau đây là một ví dụ:

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

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

Tìm tài liệu ở đây


10
Đây là loại thoát sai; chúng tôi đang tìm kiếm các lối thoát HTML , trái ngược với mã hóa URL .
Chaosphere2112

7
Không cần thiết - đó là những gì tôi đang thực sự tìm kiếm ;-)
Brad

9

Ngoài ra còn có gói markupsafe tuyệt vời .

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

Các markupsafegói phần mềm được thiết kế tốt, và có lẽ là hầu hết các linh hoạt và Pythonic cách để đi về thoát, IMHO, bởi vì:

  1. return ( Markup) là một lớp có nguồn gốc từ unicode (nghĩa làisinstance(escape('str'), unicode) == True
  2. nó xử lý đúng cách đầu vào unicode
  3. nó hoạt động trong Python (2.6, 2.7, 3.3 và pypy)
  4. nó tôn trọng các phương thức tùy chỉnh của các đối tượng (tức là các đối tượng có thuộc __html__tính) và quá tải mẫu ( __html_format__).

7

cgi.escape nên thoát HTML theo nghĩa hạn chế thoát các thẻ HTML và các thực thể ký tự.

Nhưng bạn cũng có thể phải xem xét các vấn đề mã hóa: nếu HTML bạn muốn trích dẫn có các ký tự không phải ASCII trong một mã hóa cụ thể, thì bạn cũng phải quan tâm rằng bạn thể hiện những điều đó một cách hợp lý khi trích dẫn. Có lẽ bạn có thể chuyển đổi chúng thành các thực thể. Mặt khác, bạn nên đảm bảo rằng các bản dịch mã hóa chính xác được thực hiện giữa HTML "nguồn" và trang được nhúng vào, để tránh làm hỏng các ký tự không phải ASCII.


3

Không có thư viện, python thuần, thoát văn bản thành văn bản html một cách an toàn:

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

1
Đặt hàng của bạn là sai, &lt;sẽ được thoát đến&amp;lt;
Jason S

@jason s Cảm ơn bạn đã sửa chữa!
tàu cao tốc

1

cgi.escape đa thế hệ

Phiên bản này được cải thiện cgi.escape. Nó cũng bảo tồn khoảng trắng và dòng mới. Trả về một unicodechuỗi.

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

ví dụ

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

1

Không phải là cách dễ nhất, nhưng vẫn đơn giản. Sự khác biệt chính từ mô-đun cgi.escape - nó vẫn sẽ hoạt động bình thường nếu bạn đã có &amp;trong văn bản của mình. Như bạn thấy từ các bình luận cho nó:

phiên bản 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

phiên bản 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

Đối với mã kế thừa trong Python 2.7, có thể thực hiện thông qua BeautifulSoup4 :

>>> bs4.dammit import EntitySubstitution
>>> esub = EntitySubstitution()
>>> esub.substitute_html("r&d")
'r&amp;d'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.