0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【セキュリティ】出力エスケープ(Output Escaping)はXSS を防ぐ最も基本で最強の防御線

Posted at

はじめに

— XSS を防ぐ最も基本で最強の防御線—

XSS(Cross-Site Scripting)は、多くの場合 “信頼してはいけないデータをそのまま HTML に埋め込むこと” が原因で起きる。
この根本的な問題を解決する王道対策が 出力エスケープ(Output Escaping) だ。

出力エスケープを正しく理解すれば、XSS の 70〜80% は防ぐことができる。
Modern Web であっても、出力エスケープは依然として最重要の技術だ。


1. 出力エスケープとは?

出力エスケープとは:

信頼できないデータを HTML・JavaScript・URL などに埋め込むとき、
文脈に応じて危険な文字を安全な表現に変換すること。

これにより、攻撃者が不正な HTML / JavaScript を混入させても、ブラウザが「ただの文字列」として扱うようになる。

例:
悪意ある入力

"><script>alert(1)</script>

エスケープ後の安全な出力

&quot;&gt;&lt;script&gt;alert(1)&lt;/script&gt;

ブラウザはコードではなく“テキスト”として表示する。


2. 文脈(コンテキスト)ごとの出力エスケープ

XSS 対策で最も重要なのは “どの文脈でデータを埋め込むか”
文脈により必要なエスケープ方法が違う。


① HTML 本文(テキストコンテキスト)

最も基本形。

文字 エスケープ後
< <
> >
" "
' '
& &

例:

<p>{{ userInput }}</p>

→ フレームワークによるデフォルトの HTML-escape が働くべき。


② HTML 属性値

たとえば:

<img src="{{ userInput }}">

属性値では 引用符の閉じを防ぐ必要 がある。

" → &quot;
' → &#x27;

属性値コンテキストのミスは Classic XSS の温床。


③ JavaScript 内文字列

次のケースは非常に危険な文脈:

<script>
  var name = "{{ userInput }}";
</script>

ここで必要なのは:

文字 エスケープ後
" \"
' \'
\ \\
改行 \n

JS 文脈を間違うと JS-Injection → XSS になる。


④ URL(リンク・GET パラメータ)

<a href="/search?q={{ userInput }}">

URL 文脈では:

encodeURIComponent(userInput)

が必須。

例えば:

?next=javascript:alert(1)

などの URL ベース XSS を防げる。


⑤ CSS 文脈(rare but dangerous)

CSS にユーザー入力を入れると XSS になるケースがある:

<style>
  .box { background: url("{{ userInput }}"); }
</style>

特に expression() や古いブラウザは危険。

→ そもそも CSS にユーザー入力を入れない がベスト。


3. 出力エスケープが防げる攻撃

出力エスケープを適切に行うことで、次の XSS がほぼ完全に防げる。

  • Stored XSS
  • Reflected XSS
  • HTML 属性で起きる XSS
  • JS 文字列ベースの XSS
  • URL ベース XSS
  • イベントハンドラ注入(onclick など)

エスケープだけで XSS の大部分は無効化できる。


4. ❌ しかし出力エスケープにも限界がある

次のケースはエスケープでは防げない:

① DOM-Based XSS(innerHTML など)

element.innerHTML = userInput;

→ Trusted Types や DOMPurify が必要


② リッチテキスト(Markdown / WYSIWYG)

Markdown → HTML の変換時に XSS が混入しやすい。

→ 必ず HTML sanitizer(DOMPurify など) を併用


③ フレームワークの危険操作

React の dangerouslySetInnerHTML
Vue の v-html

→ 名前の通り危険。サニタイズ必須。


④ 人間のミス(最大の問題)

出力エスケープは 開発者のコード量に比例して漏れやすい

そのため:

  • 100% エスケープできている保証がない
  • 大規模プロジェクトでは必ず抜けが発生する

→ この弱点を補うのが CSPTrusted Types


5. フレームワーク別のエスケープ実装

フレームワーク デフォルトの挙動
React JSX で自動エスケープ(dangerouslySetInnerHTML 以外)
Vue Mustache({{}})は自動エスケープ
Angular 標準で強力な HTML sanitization
Django 変数は自動エスケープ
Rails <%= %> は自動エスケープ

自動エスケープを無効化する機能を使った瞬間、XSS の穴が開く。


6. 出力エスケープ + CSP + Trusted Types = 現代の最強構成

出力エスケープだけでは XSS を完全に防ぎきれない。
現代の Web では三つを重ねることが必須:


① 出力エスケープ

→ XSS の 70〜80% を防ぐ基礎

② CSP(script-src 'self')

→ 攻撃者の JS 実行をブロックする第二防御

③ Trusted Types

→ DOM-Based XSS を完全封殺する三段階目の防御


この構成を採用すると、
Web アプリ全体の XSS 攻撃面が ほぼ消滅する


まとめ:出力エスケープは XSS 対策の本丸

出力エスケープは単なる“変換処理”ではなく、
Web セキュリティの根幹にある考え方

  • HTML、JS、URL などの文脈ごとに
  • 正しいエスケープを行い
  • 自動エスケープを採用し
  • 危険な出力方法を禁じる

これが XSS 防御の基礎となる。

そしてその上に、CSP、Trusted Types、Sanitizer を積み重ねることで、
現代的な“破られないセキュリティレイヤー”が完成する。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?