このたび、GMO Flatt Security 開発者応援プログラム for バイブコーダー に選定いただき、 セキュリティ学習サービス 「KENRO」 を1か月無料で利用できることになりました!
「KENRO」は、実践的な脆弱性診断やセキュリティ演習を通じて、 開発者が 攻撃者の視点 から安全なシステム設計を学べるオンライン学習プラットフォームです。
本記事では、KENROの学習内容をもとに Cross-Site Request Forgery(CSRF)脆弱性 についてまとめます。
Cross-Site Request Forgery(CSRF)脆弱性とは
この章では Cross-Site Request Forgery(CSRF)という脆弱性について、実際の攻撃手法を踏まえながらその仕組み、問題点、対策方法を学びます。
Cookieとは
Cookie は、Webアプリケーションがレスポンス時に Set-Cookie ヘッダを使ってブラウザに送信する「キーと値のペア」です。
ブラウザは受け取った Cookie を保存し、次回同じサイトへリクエストを送信する際に、自動的にその Cookie を付与 します。これにより、ログイン状態の維持(セッション管理)などが可能になります。
CSRF脆弱性の概要
Cookie は(古いブラウザでは特に)リクエスト元に関係なく自動で送信される という挙動があり、これがセキュリティ上の問題を引き起こすことがあります。
具体例:SNS の設定変更
ユーザーがログインしているSNSサイト sns.example.com で、以下のリクエストでメールアドレスの変更ができるとします。
POST /settings/email_change
Host: sns.example.com
Cookie: session_id=(セッションID)
Content-Type: application/x-www-form-urlencoded
new_email=user@new.example.com
攻撃者が、このユーザーを騙すための罠ページを自身のサイト attacker.com に用意します。
<form method="POST" action="[http://sns.example.com/settings/email_change](http://sns.example.com/settings/email_change)" id="csrf-form">
<input type="hidden" name="new_email" value="attacker@malicious.com" />
</form>
<script>
document.getElementById('csrf-form').submit(); // ページ表示時に自動でフォームを送信
</script>
ユーザーがログインした状態でこの罠ページを開くと、ブラウザは自動的に sns.example.comのCookie(session_id)を付与し、以下のリクエストをユーザーが意図しないうちに送信してしまいます。
POST /settings/email_change
Host: sns.example.com
Cookie: session_id=(セッションID)
Content-Type: application/x-www-form-urlencoded
new_email=attacker@malicious.com
結果、ユーザーのSNSアカウントのメールアドレスが攻撃者のものに変更されてしまうことになります。これがCSRF攻撃です。
CSRF 対策
CSRF脆弱性への対策は、ウェブアプリケーションのセキュリティにおいて極めて重要です。
1. 設計段階での洗い出し
まず、「データの変更・追加・削除を伴う画面遷移」を洗い出します。これらの処理を行うリクエストには、必ずCSRF対策を実装する必要があります。
対策が必要な例
パスワード変更、投稿作成、設定変更、商品の購入、アカウント削除など
対策不要な例
ニュースの閲覧、口座残高表示などの閲覧系
2. CSRFトークンを利用した検証(最も一般的で確実な対策)
フォームに「正しい画面遷移を経た証拠」となる CSRFトークンを付与し、サーバー側で検証します。
悪い例
<form method="POST" action="/post">
<input type="text" name="content" />
<input type="submit" value="submit" />
</form>
改善例(CSRFトークン追加)
<form method="POST" action="/post">
<input type="text" name="content" />
<input
type="hidden"
name="csrf_token"
value="(アクセス毎にランダム生成される値)"
/>
<input type="submit" value="submit" />
</form>
サーバー側の処理
- トークンを生成し、ユーザーのセッション情報に保存します。
- フォームにトークンを埋め込みます。
- ユーザーからリクエストがあった際、リクエストに含まれる
csrf_tokenの値と、セッションに保存した値を照合します。 - 一致しなければ処理を拒否 します。
これにより、外部サイトから偽装されたリクエストには正しいトークンが含まれないため、攻撃を防止できます。
3. その他の対策
| 対策 | 説明 |
|---|---|
| 重要操作時の再認証 | パスワードやワンタイムコードの再入力を求め、操作を実行するユーザーが本人であることを確認します。 |
| カスタムヘッダの利用 | Ajaxリクエストなどにおいて、特定のカスタムヘッダ(例: X-Requested-With: XMLHttpRequest)を付与し、サーバー側でこのヘッダが存在しないリクエストを拒否します。他サイトからのリクエストでは、ブラウザの制約によりこのヘッダを自由に付与できないことを利用した対策です。 |
| プリフライトリクエストで他オリジンを拒否 | CORS設定を利用し、POSTなどのリクエストを行う際にプリフライトリクエスト(OPTIONSメソッド)を行い、許可されていないオリジンからのリクエストを拒否します。 |
| SameSite属性による防御 | Cookieに SameSite 属性を設定することで、異なるサイトからのリクエスト時に Cookie が送信されないように制御します。 |
SameSite 属性の詳細
| SameSite属性 | 説明 | CSRF リスク |
|---|---|---|
| Strict | 完全に同一サイト(同一オリジン) からのリクエスト以外では Cookie を送信しない。セキュリティは高いが、ユーザー体験に影響が出る場合がある。 | 非常に低い |
| Lax |
同一 eTLD+1(例: example.com 内)でのみ送信。クロスサイトリクエストでも、ユーザーがリンクをクリックした際などのトップレベルナビゲーションを伴うGETリクエストのみ Cookie を送信する。POSTなどでは送信されない。 |
低い |
| None | 制限なし。従来の挙動(常に送信)。この場合、Secure 属性が必須となります。 |
あり |
まとめ
Cross-Site Request Forgery(CSRF)脆弱性について、攻撃者の視点から具体的な手法と対策をかなり深いところまで学べました。 KENROでは、実際にCTF形式で脆弱性を試したり、コードの改修を通して学べるため、とても完成度の高い学習サイトでした!