PythonでHTMLをエスケープする最も簡単な方法は何ですか?


137

cgi.escapeは1つの選択肢のようです。うまくいきますか?より良いと思われるものはありますか?

回答:


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;

また注目すべき点は(Gregに感謝しquoteます)追加のパラメーターをcgi.escape取ることです。これをに設定するとTruecgi.escape二重引用符(")もエスケープされ、結果の値をXML / HTML属性で使用できるようになります。

編集:cgi.escapeはPython 3.2で非推奨になっていることに注意してくださいhtml.escape。これは、quoteデフォルトがTrueであることを除いて同じことを行います。


7
HTML属性値でテキストが使用されている場合、cgi.escapeへの追加のブールパラメータも引用符をエスケープするために検討する必要があります。
グレッグヒューギル

念のため:cgi.escape関数を介してすべての信頼できないデータを実行する場合、すべての(既知の)XSS攻撃から保護するのに十分ですか?
Tomas Sedovic

@Tomas Sedovic:cgi.escapeを実行した後にテキストを配置する場所によって異なります。ルートHTMLコンテキストに配置した場合は、完全に安全です。
nosklo

{{Measures 12Ω "H x 17 5/8" W x 8 7/8 "D。Imported。}}のような入力についてはどうですか。これは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 Unicode文字はxml文字参照テーブルを使用してエンコードされています。
nosklo

112

Python 3.2 htmlでは、HTMLマークアップから予約文字をエスケープするために使用される新しいモジュールが導入されました。

これには1つの機能があります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 2013年

1
@SalmanAbbas引用符がエスケープされないことを恐れていますか?html.escape()はデフォルトで引用符をエスケープすることに注意してください(対照的に、cgi.quote()そうしない場合は、二重引用符をエスケープします)したがって、私は明示的に持つ属性に注入何かにオプションのパラメータを設定する必要がありhtml.escape()、それは属性に対して安全でない作るために、すなわち:t = '" onclick="alert()'; t = html.escape(t, quote=False); s = f'<a href="about.html" class="{t}">foo</a>'
maxschlepzig

@maxschlepzig Salmanはescape()属性を安全にするのに十分ではないと言っていると思います。言い換えれば、これは安全ではありません<a href=" {{ html.escape(untrusted_text) }} ">
。– pianoJames

@pianoJames、なるほど。リンク値をドメイン固有のセマンティック検証で確認することを検討します。エスケープのような語彙的なものではありません。インラインJava Scriptのほかに、URL固有の検証(スパマーなど)がなければ、信頼できないユーザー入力からリンクを作成したくありません。のような属性のインラインJavaScriptから保護する簡単な方法href は、それを許可しないコンテンツセキュリティポリシーを設定することです。
maxschlepzig

@pianoJames html.escape一重引用符と二重引用符をエスケープするため、安全です。
Flimm

11

URLでHTMLをエスケープする場合:

これはおそらくOPが望んだものではありませんが(エスケープがどのコンテキストで使用されることを意図しているかは明確に示されていません)、Pythonのネイティブライブラリurllibには、URLに安全に含める必要があるHTMLエンティティをエスケープするメソッドがあります。

次に例を示します。

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

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

ここでドキュメントを検索


10
これは間違った種類のエスケープです。URLエンコーディングではなく、HTMLエスケープを探しています
Chaosphere2112 2013

7
それにもかかわらず-それは私が実際に探していたものでした;-)
ブラッド

9

優れたマークアップセーフパッケージもあります

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

markupsafeパッケージはよく設計され、そして、私見をエスケープついて行くには、おそらく最も汎用性とPython的な方法、理由はされています。

  1. 戻り値(Markup)は、Unicode(つまり、isinstance(escape('str'), unicode) == True
  2. Unicode入力を適切に処理します
  3. Python(2.6、2.7、3.3、pypy)で動作します
  4. オブジェクトのカスタムメソッド(つまり、__html__プロパティを持つオブジェクト)とテンプレートオーバーロード(__html_format__)を尊重します。

7

cgi.escape HTMLタグと文字エンティティをエスケープするという限られた意味でHTMLをエスケープするのに適しています。

ただし、エンコーディングの問題も考慮する必要がある場合があります。引用するHTMLに特定のエンコーディングの非ASCII文字が含まれている場合は、引用するときにそれらを慎重に表す必要があります。おそらく、それらをエンティティに変換できます。それ以外の場合は、ASCII以外の文字の破損を回避するために、「ソース」HTMLとそれが埋め込まれているページとの間で正しいエンコーディング変換が行われるようにする必要があります。


3

ライブラリ、純粋なpythonは、テキストをhtmlテキストに安全にエスケープしません。

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

1
注文が間違ってい&lt;ます。エスケープされます&amp;lt;
ジェイソンS

@jason s修正ありがとうございます!
スピードプレーン2018

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

正規表現バージョン

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.