
XSSを防ぐエスケープをPythonで理解する|出力時に無害化する
2026-09-15 ・ 実践
XSS(クロスサイトスクリプティング) は、ユーザー入力に紛れ込んだスクリプトが他人のブラウザで実行されてしまう脆弱性です。防御の鍵は 「出力するときに無害化(エスケープ)する」 こと。Pythonで危険と安全を比較して理解します。Webの脆弱性の実装編、SQLインジェクション防御の兄弟です。
なぜ起きるのか:入力がHTMLとして解釈される
コメント欄などのユーザー入力を、そのままHTMLに埋め込むと、入力中の <script> などがタグとして解釈され実行されてしまいます。SQLインジェクションと構造は同じ——「データ」が「命令」になる問題です。
準備
標準ライブラリのhtmlだけ。
① 危ない書き方(そのまま埋め込む)
def render_unsafe(comment):
# ❌ ユーザー入力を無加工でHTMLに入れる
return f"<div class='comment'>{comment}</div>"
evil = "<script>alert('盗んだよ')</script>"
print(render_unsafe(evil))
# → <div class='comment'><script>alert('盗んだよ')</script></div>
# ブラウザがこの<script>を実行してしまう
入力の <script> が生きたタグとしてHTMLに入り、閲覧者のブラウザで実行されます。
② 安全な書き方(出力時にエスケープ)
html.escape で < > & " を無害な文字実体に変換します。
import html
def render_safe(comment):
# ✅ 表示直前にエスケープして「ただの文字」にする
return f"<div class='comment'>{html.escape(comment)}</div>"
print(render_safe(evil))
# → <div class='comment'><script>alert('盗んだよ')</script></div>
# タグではなく「文字」として表示される(実行されない)
< が < に変換され、スクリプトはただの文字列として画面に出るだけ。実行されません。
鉄則は『出力時』エスケープ
エスケープは「入力を保存するとき」ではなく「HTMLに出力するとき」に行うのが基本。保存時に加工すると、別の文脈(JSON・メール等)で使い回すとき二重変換などの事故が起きます。表示する場所(HTML属性・URL・JS内)ごとに適切なエスケープを使い分けます。
テンプレートエンジンを使うのが実務
実際のWebフレームワーク(Django、Flask+Jinja2など)は、標準で自動エスケープしてくれます。まずそれを有効にし、意図的にHTMLを出したい箇所だけ明示的に許可するのが安全。加えて Content-Security-Policy を設定すると多層防御になります。
まとめ
- XSSは「ユーザー入力をそのままHTMLに埋め込む」ことが原因
- 入力中のタグが実行され、Cookie窃取やなりすましに悪用される
- 対策は出力時のエスケープ(
html.escape)でタグを「ただの文字」に - テンプレートの自動エスケープ+CSPで多層防御
もう少し詳しく(背景と理論)
XSS(クロスサイトスクリプティング、CWE-79)は、攻撃者が仕込んだスクリプトが被害者のブラウザで実行される脆弱性です。反射型・格納型・DOM型に分類されます1。基本対策は出力時のコンテキストに応じたエスケープ——HTML本文・属性値・JavaScript内・URL内では必要なエスケープが異なり、テンプレートエンジンの自動エスケープを活かすのが定石です2。多層防御として、実行を許可するスクリプト元を制限する Content Security Policy(CSP) ヘッダや、Cookie に HttpOnly を付けて JavaScript からの読み取りを防ぐ手法を併用します3。入力バリデーションだけでは不十分で、「出力時のエスケープ」が主戦場である点が重要です。
次の一歩 🌸
入力側の対策はSQLインジェクション防御、Web脆弱性の全体像はWebの脆弱性、なりすまし対策はCSRFトークンへどうぞ。