CSRF
ユーザーが認証済みの Web サイトに対して、攻撃者が意図しないリクエストを送信させる攻撃手法
CSRF とは
CSRF (Cross-Site Request Forgery、クロスサイトリクエストフォージェリ) は、ユーザーが認証済みの Web サイトに対して、攻撃者が用意した罠ページから意図しないリクエストを送信させる攻撃手法である。ブラウザが Cookie を自動送信する仕組みを悪用する。
OWASP Top 10 に長年ランクインしていた攻撃で、2013 年版では 8 位だった。SameSite Cookie の普及により脅威は低下したが、レガシーシステムや Cookie ベースの認証を使うアプリケーションでは依然として注意が必要だ。
攻撃の仕組み
1. ユーザーが銀行サイト (bank.example.com) にログイン
→ セッション Cookie がブラウザに保存される
2. ユーザーが攻撃者の罠ページ (evil.example.com) を閲覧
3. 罠ページに埋め込まれた HTML が銀行サイトへ POST リクエストを自動送信
<form action="https://bank.example.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker-account" />
<input type="hidden" name="amount" value="1000000" />
</form>
<script>document.forms[0].submit();</script>
4. ブラウザがセッション Cookie を自動付与
→ 銀行サイトは正規ユーザーからのリクエストと判断
→ 送金が実行される
攻撃者はユーザーの Cookie を盗む必要がない。ブラウザが自動的に Cookie を付与してくれるため、ユーザーが罠ページを開くだけで攻撃が成立する。
対策
CSRF トークン (Synchronizer Token Pattern)
フォームに一意なトークンを埋め込み、サーバーで検証する。最も伝統的で確実な対策だ。
<form action="/transfer" method="POST">
<input type="hidden" name="_csrf" value="a1b2c3d4..." />
<!-- フォームの内容 -->
</form>
攻撃者は CSRF トークンの値を知ることができないため、正しいトークンを含むリクエストを偽造できない。
SameSite Cookie
SameSite 属性を設定すると、クロスサイトリクエストに Cookie を送信しなくなる。
| 値 | 動作 | CSRF 防御 |
|---|---|---|
Strict |
クロスサイトリクエストに一切 Cookie を送信しない | 完全に防御 |
Lax |
GET リクエストのみ Cookie を送信 (POST は送信しない) | ほぼ防御 |
None |
常に Cookie を送信 (Secure 必須) |
防御なし |
Chrome 80 以降、SameSite のデフォルト値が Lax に変更された。これにより、明示的に設定しなくても POST リクエストの CSRF は防御される。ただし、GET リクエストで副作用を起こす API (GET /delete-account) は Lax でも防御できない。
Origin / Referer ヘッダー検証
リクエストの Origin ヘッダーが自サイトのドメインと一致するか検証する。CSRF トークンの管理が不要で実装が簡単だが、一部のブラウザやプロキシが Origin ヘッダーを送信しないケースがある。
SPA + API 構成での CSRF
SPA が JWT を Authorization ヘッダーで送信する構成では、ブラウザが自動送信しないため CSRF のリスクは低い。
| 認証方式 | CSRF リスク | 理由 |
|---|---|---|
| セッション Cookie | 高い | ブラウザが自動送信 |
| JWT in Cookie | 高い | ブラウザが自動送信 |
| JWT in Authorization ヘッダー | 低い | JavaScript で明示的に付与 |
| JWT in localStorage | 低い (XSS リスクあり) | ブラウザが自動送信しない |
JWT を Cookie に保存する場合は、SameSite=Strict と CSRF トークンの併用が推奨される。
API Gateway + Lambda での対策
API Gateway の CORS 設定で Access-Control-Allow-Origin を自サイトのドメインに限定する。* (ワイルドカード) は使わない。
# SAM テンプレート
Cors:
AllowOrigin: "'https://example.com'"
AllowMethods: "'GET,POST,PUT,DELETE'"
AllowHeaders: "'Content-Type,Authorization'"
AllowCredentials: true
CORS はブラウザが強制する仕組みであり、curl やサーバーサイドからのリクエストは制限できない。CORS だけに頼らず、認証・認可を適切に実装する。
CSRF を扱う関連書籍も多い。