PR

クロスサイトリクエストフォージェリ(CSRF)の仕組みと対策

この記事は学習用です。ChatGPT と GitHub Copilot を使っています。

1. 概要:CSRF とは何か

クロスサイトリクエストフォージェリ(CSRF: Cross-Site Request Forgery)は、ユーザーが意図しないリクエストを第三者に送信させられる攻撃である。
認証済みのユーザーが攻撃者の用意したページを閲覧すると、被害サイトに対して不正な操作(アカウント設定変更、送金、投稿など)が行われる。

  • 銀行サイトにログインしたまま、攻撃者が用意したページを開く。
  • 攻撃者ページ内の <img src="https://bank.example.com/transfer?to=attacker&amount=1000"> が実行され、GET による送金が行われる(被害サイトが GET で状態変更を受け付けていた場合)。

2. 攻撃が成立する条件

CSRF は以下の条件が揃うと発生する。

条件内容
1ユーザーが正規サイトにログイン済みで、認証情報(Cookie など)が有効である
2攻撃者が任意のリクエストを被害サイトに送信させるページを用意できる
3被害サイトがリクエスト元を検証せずに処理してしまう

特に、Cookie ベースのセッション認証を採用している場合、リクエスト時にブラウザが自動で Cookie を送信するため攻撃が成立しやすい。

3. 攻撃フロー(Mermaid 図)

以下は CSRF 攻撃の典型的な流れである。

sequenceDiagram participant U as ユーザー participant A as 攻撃者サイト participant S as 正規サイト U->>S: ログイン(Cookie 発行) U->>A: 攻撃者サイトにアクセス A->>S: 不正リクエストのトリガー note over A,S: ユーザーのブラウザが Cookie を自動送信 S->>A: 操作成功(意図しないデータ変更)

このように、ブラウザが自動的に Cookie を送信してしまう点が根本的な要因である。

4. 対策:CSRF トークンの導入

最も一般的な防御方法は CSRF トークン(ワンタイムトークン、ノンス)を使用することである。

仕組み

  1. サーバーはフォームを生成するとき、推測困難なランダム値のトークンを付与する。
  2. クライアントはフォーム送信時にそのトークンを送信する(隠しフィールドやカスタムヘッダ)。
  3. サーバーは受け取ったトークンをセッション内の値などと比較する。
  4. 一致しない場合はリクエストを拒否する。

コード例(Python/Flask)

@app.route('/transfer', methods=['POST'])
def transfer():
    token = request.form.get('_csrf_token')
    if token != session.get('_csrf_token'):
        abort(403)
    process_transfer()
    return 'OK'

このように、フォーム送信ごとに検証可能なトークンを使うことで、外部サイトからの偽装を防ぐ。

5. その他の防御手法

方法内容
SameSite Cookie 属性Cookie の送信を同一サイト発行のリクエストに限定する(SameSite=Lax または Strict)。多くのブラウザで未指定時は Lax 相当として扱われる。なお、SameSite=None を用いる場合は Secure 属性が必須。
Referer/Origin チェックリクエストヘッダの送信元ドメインを検証する(Origin 優先、なければ Referer)。
多要素認証(MFA)/二要素認証(2FA)重要操作にワンタイムパスコードなどを要求する。
API 設計の見直し状態変更は POST/PUT/DELETE に限定し、CORS を厳格に設定する。ただし CORS だけでは CSRF は防げない点に留意。

これらを組み合わせ、多層防御(Defense in Depth)でトークン管理の不備を補完する。

6. フロントエンドにおける実装ポイント

JavaScript フレームワーク(例:React, Vue.js)でも CSRF 対策は必須である。

  • SPA(Single Page Application)では、トークンを meta タグ等から読み取り、リクエストヘッダに含める。
axios.defaults.headers.common['X-CSRF-Token'] =
  document.querySelector('meta[name="csrf-token"]')?.content ?? '';
  • サーバー側では、ヘッダ内のトークンを照合する。XSS 対策(エスケープ、CSP など)も前提とする。

7. よくある誤りと注意点

誤り問題点
GET メソッドで状態変更を行うキャッシュやリンククリックで容易に実行される。副作用のある操作は禁止する。
トークンを Cookie のみで検証するCookie は自動送信されるため CSRF 防御にならない。ダブルサブミットクッキー等、Cookie とフォーム/ヘッダ双方での一致検証が必要。
同一トークンを長期間使い回す漏えい時の影響が大きく、リプレイ攻撃のリスクが高まる。適切な失効とローテーションを行う。

CSRF 対策は単一の手段では不十分であり、複数の防御を重ねることが重要である。

8. まとめ

  • CSRF は「認証済みユーザーを悪用して不正操作を行わせる攻撃」。
  • トークン検証・SameSite 属性・送信元検証などを併用して防御する。
  • 実装段階で「リクエストがどの経路から来るか」を常に意識することが、安全な Web アプリ設計の第一歩となる。

A. 参考サイト

OWASP: Cross-Site Request Forgery (CSRF)
MDN Web Docs: SameSite cookies
Django Docs: CSRF protection

B. 関連書籍

コメント