วิธีที่ง่ายที่สุดในการหนี HTML ใน Python คืออะไร


137

cgi.escape ดูเหมือนเป็นทางเลือกเดียวที่เป็นไปได้ มันทำงานได้ดีหรือไม่ มีบางสิ่งที่ถือว่าดีขึ้นหรือไม่?

คำตอบ:


176

cgi.escapeไม่เป็นไร มันหนี:

  • < ถึง &lt;
  • > ถึง &gt;
  • & ถึง &amp;

นั่นก็เพียงพอสำหรับ HTML ทั้งหมด

แก้ไข: หากคุณมีตัวอักษรที่ไม่ใช่ ASCII คุณยังต้องการหลบหนีสำหรับการรวมไว้ในเอกสารที่เข้ารหัสอื่นที่ใช้การเข้ารหัสที่แตกต่างกันเช่นCraigกล่าวเพียงใช้:

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

อย่าลืมที่จะถอดรหัสdataจะunicodeเป็นครั้งแรกโดยใช้การเข้ารหัสสิ่งที่มันจะถูกเข้ารหัส

อย่างไรก็ตามจากประสบการณ์ของฉันการเข้ารหัสแบบนั้นไม่มีประโยชน์อะไรถ้าคุณแค่ทำงานunicodeตลอดเวลาตั้งแต่เริ่มต้น เพียงเข้ารหัสที่ส่วนท้ายของการเข้ารหัสที่ระบุในส่วนหัวของเอกสาร ( utf-8เพื่อความเข้ากันได้สูงสุด)

ตัวอย่าง:

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

นอกจากนี้มูลค่าของโน้ต (ขอบคุณเกร็ก) เป็นพิเศษquoteพารามิเตอร์cgi.escapeเตะ กับมันตั้งค่าให้True, cgi.escapeนอกจากนี้ยังมีตัวอักษรหนีราคาคู่ ( ") เพื่อให้คุณสามารถใช้ค่าที่เกิดขึ้นใน XML แอตทริบิวต์ / HTML

แก้ไข: โปรดทราบว่า cgi.escape ได้รับการคัดค้านใน Python 3.2 เพื่อสนับสนุนhtml.escapeซึ่งทำเช่นเดียวกันยกเว้นquoteค่าเริ่มต้นที่เป็นจริง


7
พารามิเตอร์บูลีนเพิ่มเติมไปยัง cgi.escape ควรได้รับการพิจารณาสำหรับการหลีกเลี่ยงคำพูดเมื่อมีการใช้ข้อความในค่าแอตทริบิวต์ HTML
Greg Hewgill

เพื่อให้แน่ใจว่า: หากฉันเรียกใช้ข้อมูลที่ไม่น่าเชื่อถือทั้งหมดผ่านcgi.escapeฟังก์ชั่นเพียงพอที่จะป้องกัน attacs XSS ที่รู้จักทั้งหมดได้หรือไม่
Tomas Sedovic

@Tomas Sedovic: ขึ้นอยู่กับว่าคุณจะใส่ข้อความอะไรหลังจากเรียกใช้ cgi.escape หากอยู่ในบริบท HTML ของรูทแล้วใช่คุณปลอดภัยอย่างสมบูรณ์
nosklo

สิ่งที่เกี่ยวกับการป้อนข้อมูลเช่น {{มาตรการ 12 H "H x 17 5/8" W x 8 7/8 "D นำเข้า.}} นั่นไม่ใช่ ascii ดังนั้น encode () จะส่งข้อยกเว้นให้คุณ
Andrew Kolesnikov

@Andrew Kolesnikov: คุณเคยลองหรือยัง cgi.escape(yourunicodeobj).encode('ascii', 'xmlcharrefreplace') == '{{Measures 12 &#937;"H x 17 5/8"W x 8 7/8"D. Imported.}}'- อย่างที่คุณเห็นนิพจน์คืนค่าการทดสอบด้วย ASCII ด้วยตัวอักษรที่ไม่ใช่ ASCII ทั้งหมดที่เข้ารหัสโดยใช้ตารางการอ้างอิงอักขระ xml
nosklo

112

ใน Python 3.2 มีการนำhtmlโมดูลใหม่มาใช้ซึ่งจะใช้เพื่อหลีกเลี่ยงอักขระที่สงวนไว้จากมาร์กอัพ HTML

มันมีฟังก์ชั่นเดียว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;'

เกี่ยวกับquote=Trueอะไร
2rs2ts

1
@SalmanAbbas คุณกลัวว่าคำพูดจะไม่หนีออกมา? โปรดทราบว่าhtml.escape()ตามค่าเริ่มต้น escape (ในทางตรงข้ามcgi.quote()ไม่ได้ - และจะยกเว้นเครื่องหมายคำพูดคู่เท่านั้นหากมีการแจ้งให้ทราบ) ดังนั้นฉันต้องตั้งค่าพารามิเตอร์ที่เป็นทางเลือกเพื่อแทรกบางสิ่งบางอย่างลงในแอตทริบิวต์ด้วยhtml.escape()เช่นทำให้มันไม่ปลอดภัยสำหรับคุณลักษณะ:t = '" onclick="alert()'; t = html.escape(t, quote=False); s = f'<a href="about.html" class="{t}">foo</a>'
23419

@maxschlepzig ฉันคิดว่า Salman กำลังพูดescape()ไม่เพียงพอที่จะทำให้คุณลักษณะปลอดภัย ในคำอื่น ๆ นี้จะไม่ปลอดภัย:<a href=" {{ html.escape(untrusted_text) }} ">
pianoJames

@pianoJames ฉันเห็น ฉันพิจารณาการตรวจสอบค่าลิงค์ของการตรวจสอบความหมายเฉพาะโดเมน ไม่ใช่ศัพท์ที่ชอบหนี นอกเหนือจากจาวาสคริปต์แบบอินไลน์คุณไม่ต้องการสร้างลิงก์จากการป้อนข้อมูลของผู้ใช้ที่ไม่น่าเชื่อถือโดยไม่ต้องมีการตรวจสอบความถูกต้องเฉพาะ URL เพิ่มเติม (เช่นเนื่องจากผู้ส่งอีเมลขยะ) วิธีง่ายๆในการป้องกันจาวาสคริปต์แบบอินไลน์ในลักษณะเช่นhref นี้คือการตั้งค่านโยบายความปลอดภัยเนื้อหาที่ไม่อนุญาต
maxschlepzig

@pianoJames ปลอดภัยเพราะhtml.escapeจะหลีกเลี่ยงการใส่เครื่องหมายคำพูดเดี่ยวและเครื่องหมายคำพูดคู่
Flimm

11

หากคุณต้องการหลบหนี HTML ใน URL:

นี่อาจไม่ใช่สิ่งที่ OP ต้องการ (คำถามไม่ได้ระบุอย่างชัดเจนในบริบทที่ควรใช้การหลบหนี) แต่urllibไลบรารีดั้งเดิมของ Python มีวิธีการหลีกเลี่ยงเอนทิตี HTML ที่ต้องรวมอยู่ใน URL อย่างปลอดภัย

ต่อไปนี้เป็นตัวอย่าง:

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

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

ค้นหาเอกสารที่นี่


10
นี่เป็นการหลบหนีที่ผิด เรากำลังมองหาHTML หนีเมื่อเทียบกับการเข้ารหัส URL
Chaosphere2112

7
นนท์ - มันเป็นสิ่งที่ฉันกำลังมองหา ;-)
แบรด

9

นอกจากนี้ยังมีดีแพคเกจ markupsafe

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

markupsafeแพคเกจถูกออกแบบมาอย่างดีและอาจจะมากที่สุดที่หลากหลายและวิธี Pythonic ไปเกี่ยวกับการหลบหนี IMHO เพราะ:

  1. return ( Markup) เป็นคลาสที่ได้มาจาก Unicode (เช่นisinstance(escape('str'), unicode) == True
  2. มันจัดการอินพุตยูนิโคดได้อย่างเหมาะสม
  3. มันทำงานได้ใน Python (2.6, 2.7, 3.3 และ pypy)
  4. มันเคารพวิธีการที่กำหนดเองของวัตถุ (เช่นวัตถุที่มี__html__คุณสมบัติ) และแม่แบบเกินพิกัด ( __html_format__)

7

cgi.escape ควรหลีกเลี่ยง HTML ในแง่ที่ จำกัด ในการหลีกเลี่ยงแท็ก HTML และเอนทิตีอักขระ

แต่คุณอาจต้องพิจารณาถึงปัญหาการเข้ารหัสเช่นกัน: หาก HTML ที่คุณต้องการอ้างถึงมีอักขระที่ไม่ใช่ ASCII ในการเข้ารหัสเฉพาะดังนั้นคุณจะต้องระมัดระวังว่าคุณเป็นตัวแทนของเหตุผลเหล่านั้นอย่างสมเหตุสมผลเมื่อทำการอ้างอิง บางทีคุณสามารถแปลงเป็นเอนทิตีได้ มิฉะนั้นคุณควรตรวจสอบให้แน่ใจว่ามีการแปลการเข้ารหัสที่ถูกต้องระหว่าง HTML "source" และหน้าที่ฝังอยู่เพื่อหลีกเลี่ยงการอักขระที่ไม่ใช่ ASCII เสียหาย


3

ไม่มีไลบรารี่แท้ๆ, ไพ ธ อนปลอดภัยในการดึงข้อความออกเป็นข้อความ HTML:

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

1
การสั่งซื้อของคุณผิด&lt;จะได้รับการหลบหนีไป&amp;lt;
Jason S

@ Jason s ขอบคุณสำหรับการแก้ไข!
speedplane

1

cgi.escape ขยาย

cgi.escapeรุ่นนี้จะช่วยปรับปรุง นอกจากนี้ยังรักษาช่องว่างและบรรทัดใหม่ ส่งคืนunicodeสตริง

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

ตัวอย่างเช่น

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

1

ไม่ใช่วิธีที่ง่ายที่สุด แต่ก็ยังตรงไปตรงมา ความแตกต่างที่สำคัญจากโมดูลcgi.escape - มันยังคงทำงานได้อย่างถูกต้องหากคุณมี&amp;ข้อความของคุณอยู่แล้ว ตามที่คุณเห็นจากความคิดเห็นไปที่:

รุ่น 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

รุ่น 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

สำหรับรหัสดั้งเดิมใน Python 2.7 สามารถทำได้ผ่านทางBeautifulSoup4 :

>>> bs4.dammit import EntitySubstitution
>>> esub = EntitySubstitution()
>>> esub.substitute_html("r&d")
'r&amp;d'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.