XSS・CSRF・SQLi — データとコードの境界が壊れる
3つの代表的な脆弱性は、どれも「データとして扱うべきものが命令として解釈される」という同じ形をしている。
先に読んでおくとよい記事
この記事の進み方
What — 3つに共通する形
一見まったく違う3つの脆弱性ですが、データとして扱うべきものが、命令として解釈されるという同じ構造を持っています。
入力された文字列が、HTML や JavaScript として解釈される。
// 入力
検索キーワード: <script>steal()</script>
// 出力される HTML
<p>"<script>steal()</script>" の検索結果</p>
~~~~~~~~~~~~~~~~~~~~~~~~
ブラウザはこれを「データ」ではなく
「実行すべきスクリプト」と解釈する- 壊れる境界
- HTML / JS パーサ
- 攻撃者ができること
- その人として何でも
- 根本対策
- 出力時のエスケープ
- 多層防御
- CSP
- –HttpOnly Cookie は「盗み出す」ことは防ぐが、「ブラウザ内から使う」ことは防げない。
- –被害範囲は、そのユーザーができることすべて。管理者が踏むと致命的になる。
XSS は HTML パーサ、SQLi は SQL パーサ、CSRF はブラウザの自動送信。壊れる場所は違うが、いずれも「文字列の連結」か「意図の欠落」が原因になっている。
XSS の被害範囲
- 1① Cookie の持ち出しdocument.cookie を外部へ送る重い
- 2② API の代理実行そのユーザーとして API を叩く重い
- 3③ 画面の書き換え偽のログインフォームを表示する重い
- 4④ 入力の記録キー入力やフォーム内容を送信する重い
- 5⑤ 持続化投稿やプロフィールに仕込む重い
対策の要は「そもそも実行させない」(出力エスケープ)こと。実行された後の被害を減らす対策(HttpOnly・CSP)は重要だが、あくまで二重の備え。
スクリプトが実行された時点で、そのページでできることはすべて攻撃者ができる。HttpOnly は①を防ぐが、②以降は防げない。
XSS・CSRF・SQLi に共通する構造はどれですか?
Why — なぜ「エスケープ漏れ」が繰り返されるのか
3つの脆弱性がなくならない理由は、安全な書き方より危険な書き方のほうが短くて自然だからです。
// 危険だが、自然に書ける
const sql = `SELECT * FROM users WHERE id = ${id}`;
element.innerHTML = `<p>${keyword}</p>`;
// 安全だが、一手間かかる
db.query('SELECT * FROM users WHERE id = ?', [id]);
element.textContent = keyword;
人間が毎回正しく選ぶことに期待する設計は、必ずどこかで破れます。だから現代のフレームワークは、既定を安全側にする方向に進みました。
- React・Vue は、変数を埋め込むと自動でエスケープする。危険な書き方には
dangerouslySetInnerHTMLのようなわざと長くて怖い名前が付いている - ORM やクエリビルダは、既定でプレースホルダを使う
- 主要なフレームワークは、CSRF トークンを既定で有効にする
安全な道が既定で、危険な道に進むには明示的な操作が要る。 この形をとることで、うっかりミスが構造的に減ります。
CSRF はなぜ成立するのか
CSRF が成立する条件は2つだけです。
- ブラウザが Cookie を自動で送る(同一オリジンポリシーは「送信」を止めない)
- サーバーがリクエストを「本人の意図」だと信じる
CSRF トークンは、2番目を壊します。攻撃者のページはあなたのページの中身を読めないので、フォームに埋め込まれたランダムなトークンを知りようがありません。
<form method="POST" action="/transfer">
<input type="hidden" name="_csrf" value="ランダム値">
...
</form>
サーバーはこのトークンがセッションのものと一致するかを確認します。攻撃者は正しい値を書けないので、リクエストは弾かれます。
「読めないこと」を利用して「本人性」を作る——同一オリジンポリシーの上に立った、簡潔な仕組みです。
HttpOnly を付けていれば XSS の被害は防げますか?
演習 — まず自分で判断する
解説を読む前に、まず自分で判断してみてください。ここで一度詰まっておくと、 次の節の判断軸が「なるほど」ではなく「そう来たか」に変わります。
この4つの指摘に、どの順で対応しますか?
- 社内の勤怠管理システム。全社員 1200 人が使い、人事部が全員のデータを見られる
- ① 申請コメント欄の内容が、承認画面で innerHTML により表示されている
- ② 検索結果のソート順を ?sort=created_at+desc のようにクエリから受け取り、SQL に連結している
- ③ 「申請を取り消す」が GET /applications/123/cancel で実装されている
- ④ セッション Cookie に HttpOnly が付いていない
- リリースまでに全部は直せず、まず2つだけ直せる状況
- · 攻撃が成立したとき、最も権限の高い人に届くのはどれか
- · 1つの脆弱性が、他の対策を無効化してしまう関係はあるか
- · 被害が自動的に広がる(persistent な)ものはどれか
「入力値のバリデーションを強化すれば脆弱性は防げる」と言う同僚に、それでは足りない理由を説明してください
- 相手は禁止文字のフィルタを追加しようとしている
- 対象は検索フォームと、その結果表示
- 何を入れるべきかまで示したい
読み終わりましたか?
読了にすると、これを前提とする記事がロードマップで開放されます。