Pythonで一部のHTMLタグをエスケープする
セキュリティーなどの都合で、フォームから送信されてきた文字列の中にHTMLやjavascript,cssの文字列があったらエスケープしたい。 というのをPythonでやってみます。
環境
Python 3.7.1
bleach==3.1.0
HTMLのエスケープにはbleach
というライブラリが便利です。
インストール
pip install bleach
ちなみに、bleach
はMozillaが開発をしているそうです。
エスケープする
使い方はごく簡単、エスケープしたい文字列をclean
関数に渡してあげます。
import bleach
text = """
a<div title="sample" onclick="alert(0)" style="color:red;">b<script>alert(1)</script>c</div>d
"""
bleach.clean(text)
# '\na<div title="sample" onclick="alert(0)" style="color:red;">b<script>alert(1)</script>c</div>d\n'
削除する
先ほどの方法では、HTMLのタグが実体参照に変換されましたが、clean
関数の引数にstrip=True
を指定するとHTMLのタグ部分が削除されます。
text = """
a<div title="sample" onclick="alert(0)" style="color:red;">b<script>alert(1)</script>c</div>d
"""
bleach.clean(text, strip=True)
# '\nabalert(1)cd\n'
ホワイトリストを指定する
先ほどまでは、ほとんどのHTMLタグがエスケープや削除の対象になりましたが、中にはmarkdown等との組み合わせの都合で、エスケープや削除の対象にしたくないタグもあるかもしれません。
そんな時は、ホワイトリストを指定してあげることで特定のタグのみエスケープや削除の対象外にすることもできます。
text = """
a<div title="sample" onclick="alert(0)" style="color:red;">b<script>alert(1)</script>c</div>d
"""
bleach.clean(text, tags=["div", "br", "p"])
# '\na<div>b<script>alert(1)</script>c</div>d\n'
タグだけでなく属性、スタイルもホワイトリスト指定できます。
text = """
a<div title="sample" onclick="alert(0)" style="color:red;">b<script>alert(1)</script>c</div>d
"""
bleach.clean(text, tags=["div", "br", "p"], attributes={"div":["title", "style"]}, styles=["color"]) # stylesを指定するときは、タグのstyle属性をホワイトリストに入れる必要がある
# '\na<div style="color: red;" title="sample">b<script>alert(1)</script>c</div>d\n'
実は最初に紹介したシンプルにclean
関数にエスケープ対象の文字列だけを渡す方法では、いくつかのHTMLタグはエスケープの対象から外れています。
デフォルトでは、以下のHTMLタグがエスケープの対象外
bleach.ALLOWED_TAGS
# ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'strong', 'ul']
bleach.ALLOWED_ATTRIBUTES
# {'a': ['href', 'title'], 'abbr': ['title'], 'acronym': ['title']}
bleach.ALLOWED_STYLES
# [] スタイルはデフォルトだと何も許容されていない模様
まとめてホワイトリストに指定する
ワイルドカード
属性を許可する際に、全てのタグに対して同じ属性を許可したい場合、ワイルドカード*
が利用できます。
text2 = '<div title="a" style="color: blue;"></div> <span title="b"></span> <p title="c"></p>'
bleach.clean(text2, tags=['div', 'span', 'p'], attributes={'*': ['title']})
# '<div title="a"></div> <span title="b"></span> <p title="c"></p>'
関数で条件を指定する
ワイルドカード指定以外にも関数を渡して条件を指定することもできます。
タグ名と属性名、属性の値の三つを引数にとる関数を用意します。
下の例ではon
で始まるハンドラ系の属性を削っています。
def not_on_attribute(tag, name, value):
return not name.startswith("on")
text3 = '<div title="a" onclick="alert(1)" onmouseover="alert(2)">a</div>'
bleach.clean(text3, tags=['div'], attributes=not_on_attribute)
# '<div title="a">a</div>'