CSRFは「終わった脆弱性」か?――その甘い認識が会社を沈める
「CSRFなんて今の時代、フレームワークが勝手に防いでくれるでしょ?」
ペネトレーションテストの現場で、開発チームから何度この言葉を聞いたことか。確かに、現代のWebフレームワークは強力だ。だが、脆弱性は「ツール」にあるのではなく、その「設定」と「運用」の隙間にこそ潜んでいる。
今日は、CSRFの攻撃シーケンスという「泥臭い現実」と、それを確実に封じ込めるための実務的な防壁について話そう。教科書をなぞるだけの平和なセキュリティ教育は終わりだ。
—
1. 攻撃者が狙うのは「セッションの自動送出」という仕様
CSRF(Cross-Site Request Forgery)の核心はシンプルだ。ブラウザが「クッキー(Cookie)」をドメイン単位で自動的に付与してリクエストを送る、というWebの根源的な仕様を悪用する。
攻撃者の視点から見れば、標的のユーザーがブラウザで認証済み(セッション保持中)であれば、攻撃者が用意した巧妙な罠(悪意のあるHTMLやJavaScript)を踏ませるだけで、標的の権限で勝手に「パスワード変更」「メールアドレス更新」「決済実行」ができてしまう。
攻撃のシーケンス例
1. 罠の設置: 攻撃者は攻撃用サーバーに、標的サイトの POST /api/change-email を実行するフォームを仕込む。
2. 標的の誘引: 標的ユーザーが攻撃者のサイトにアクセスする。
3. 自動実行: ページ読み込みと同時に、JavaScriptでフォームが自動送信される。
4. リクエスト着弾: ブラウザは標的サイトのクッキーを自動的に付与して送信するため、サーバーは「ユーザー本人の意思による操作」と誤認して処理を実行する。
これの恐ろしいところは、サーバー側でログを見ても「正規のセッションID」が使われているため、不正アクセスの判別が極めて困難な点にある。
—
2. 対症療法から根本解決へ:防御のベストプラクティス
CSRF対策の鉄則は「トークン」と「ブラウザのCookie制御」の二段構えだ。
① Anti-CSRFトークンの実装(サーバーサイドの正義)
リクエストごとに予測不可能なトークンを埋め込み、サーバー側で照合する。これが最も堅牢だ。PHPのセッションを使った実装例を見てみよう。
<?php
// セッション開始
session_start();
// トークンがなければ生成
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// フォームで送信するためのトークンを取得
$token = $_SESSION['csrf_token'];
?>
<!-- HTMLフォーム内には必ずトークンを潜ませる -->
<form action="/update-email.php" method="POST">
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($token, ENT_QUOTES); ?>">
<input type="email" name="email" required>
<button type="submit">メールアドレス変更</button>
</form>
サーバー側での検証ロジック:
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 送信されたトークンとセッションのトークンを比較
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die("CSRFトークンが無効です。"); // ここで弾くのが鉄則
}
// 処理を実行...
}
② SameSite属性の魔法(ブラウザ側の防壁)
近年のブラウザは、Cookieの SameSite 属性によってCSRFを高い精度で防ぐことができる。NginxなどのWebサーバー側でCookieをセットする際、必ず SameSite=Lax または Strict を指定すること。
Nginx設定例:
# 全てのSet-CookieヘッダーにSameSite=Laxを強制適用する
proxy_cookie_path / "/; HTTPOnly; Secure; SameSite=Lax";
Strict: サイト間移動時にはCookieを送らない。最も安全だが、SNSのリンク踏みなどでログイン状態が引き継がれずUXが低下する場合がある。Lax: デフォルトの推奨値。安全な遷移(GETリクエストなど)には許可を与えるが、POSTリクエストなどの危険な操作にはCookieを渡さない。
—
3. なぜ「トークン」をサボると死ぬのか
「SPA(Single Page Application)だから、Authorization: Bearer <token> でヘッダー管理してるし大丈夫でしょ?」という慢心も危険だ。
もしそのAPIが、Cookie認証を併用していたり、将来的に仕様変更でCookie認証に対応した場合、その瞬間にCSRFの標的になる。「セキュリティ設計は、システムが拡張される未来の脆弱性までカバーしなければならない」。これがプロの視点だ。
実務で守るべきチェックリスト
1. POST/PUT/DELETEはCSRF対策必須: GETリクエストで状態変更(データの更新など)を行うのは論外。GETはあくまで情報の取得のみに使う。
2. トークンの使い回し禁止: セッションごとにリジェネレート(再生成)することが望ましい。
3. WAFの活用: もしアプリケーションコードの修正が困難なレガシーシステムであっても、AWS WAFなどで「POSTリクエストに対するトークン検証ルール」を適用することで、外部から防御レイヤーを積むことは可能だ。
最後に:防御は「疑うこと」から始まる
インシデントは、常に「まさかこんな単純なリクエストが攻撃に使われるわけがない」という油断の隙間を突いてくる。CSRFは攻撃手法としてはクラシックだが、その破壊力は今も現役だ。
今日紹介したコードは、今すぐあなたのプロジェクトのコードベースと見比べてほしい。「トークンは入っているか?」「SameSite属性は付与されているか?」――この確認一つで、防げる被害がある。
セキュリティは、ツールを導入して終わりではない。あなたの書く一行一行のコードの中にこそ、最大の防壁があることを忘れないでくれ。健闘を祈る。
コメント