Webアプリケーションにおいて、CSRF(Cross-Site Request Forgery:クロスサイトリクエストフォージェリ)は、ユーザーが意図しない操作を強制的に実行させる危険な攻撃手法です。OWASPでも重要な脆弱性として位置づけられており、すべてのWeb開発者が理解しておくべきセキュリティ脅威です。

この記事では、CSRFとは何か、攻撃が成立する条件、CSRFトークンによる対策、SameSite Cookie属性の活用について、実践的な観点から解説します。

CSRFとは何か

CSRF(Cross-Site Request Forgery:クロスサイトリクエストフォージェリ)は、ユーザーが認証済みのWebアプリケーションに対して、悪意のあるリクエストを強制的に送信させる攻撃手法です。「シーサーフ」や「クロスサイトリクエストフォージェリ」とも呼ばれます。

CSRF攻撃の基本的な流れ

sequenceDiagram
    participant User as ユーザー
    participant Bank as 銀行サイト
    participant Attacker as 攻撃者サイト
    
    Note over User,Bank: Step 1: ユーザーが正規サイトにログイン
    User->>Bank: ログイン
    Bank-->>User: セッションCookie発行
    
    Note over User,Attacker: Step 2: ユーザーが悪意のあるサイトを訪問(ログイン状態のまま)
    User->>Attacker: アクセス
    Attacker-->>User: 不正なフォーム/imgタグを含むHTML
    
    Note over User,Bank: Step 3: ブラウザが自動的に銀行サイトへリクエストを送信
    User->>Bank: 送金リクエスト + Cookie
    Note over Bank: 正規のリクエストとして処理
    Bank-->>User: 送金完了

CSRF攻撃で何ができるのか

CSRF攻撃が成功すると、攻撃者は被害者の権限を悪用して以下のような操作を実行できます。

  1. 不正送金: 銀行口座から攻撃者の口座への送金
  2. パスワード変更: ユーザーアカウントのパスワードを変更してアカウントを乗っ取る
  3. メールアドレス変更: 通知先を攻撃者のメールアドレスに変更
  4. 設定変更: プライバシー設定やセキュリティ設定の無効化
  5. 商品購入: ECサイトでの不正な購入処理

CSRFとXSSの違い

CSRFとXSS(クロスサイトスクリプティング)は混同されやすいですが、攻撃の仕組みが異なります。

項目 CSRF XSS
攻撃の対象 サーバー側の処理 クライアント側(ブラウザ)
攻撃の方法 ユーザーの認証情報を悪用したリクエスト送信 悪意のあるスクリプトの注入・実行
必要な条件 ユーザーがログイン状態であること 脆弱なサイトへのスクリプト注入が可能であること
攻撃者が得るもの ユーザーの権限での操作実行 Cookie、セッション情報、入力データなど

重要な点として、XSS脆弱性が存在する場合、CSRFの対策(CSRFトークンなど)を回避できる可能性があります。そのため、CSRF対策だけでなく、XSS対策も併せて実施することが重要です。

CSRF攻撃が成立する条件

CSRF攻撃が成功するためには、以下の3つの条件がすべて満たされる必要があります。

条件1: 状態を変更するHTTPリクエストが存在する

Webアプリケーションに、サーバー側の状態を変更する機能(送金、パスワード変更、設定更新など)が存在することが前提条件です。

状態変更リクエストの例:

1
2
3
4
5
POST /transfer HTTP/1.1
Host: bank.example.com
Content-Type: application/x-www-form-urlencoded

to_account=attacker&amount=100000

条件2: Cookieのみで認証が行われている

サーバーがリクエストの正当性をCookie(セッションCookie)のみで判断している場合、攻撃が可能になります。ブラウザは、クロスサイトリクエストであっても自動的にCookieを付与して送信するためです。

条件3: リクエストパラメータが予測可能である

攻撃者がリクエストに必要なパラメータをすべて推測できる場合、有効な攻撃リクエストを構築できます。逆に、予測不可能なトークンが含まれていれば、攻撃は失敗します。

flowchart TD
    subgraph Conditions["CSRF攻撃成立の3条件"]
        A["1. 状態変更リクエストが存在する<br>→ 送金、設定変更、購入など"]
        B["2. Cookieベースの認証のみ<br>→ ブラウザが自動的にCookieを送信"]
        C["3. パラメータが予測可能<br>→ 攻撃者が有効なリクエストを作成可能"]
    end
    A --> D{"3条件すべてが揃う"}
    B --> D
    C --> D
    D --> E["CSRF攻撃が可能"]

CSRF攻撃の具体例

GETリクエストを悪用した攻撃

最もシンプルなCSRF攻撃は、GETリクエストで状態変更を行う脆弱なアプリケーションを狙うものです。

脆弱な実装例(絶対に避けるべき設計):

1
2
3
GET /transfer?to=attacker&amount=100000 HTTP/1.1
Host: bank.example.com
Cookie: session=abc123

攻撃コード:

1
2
3
4
5
6
7
8
<!-- 攻撃者のサイトに埋め込まれたimgタグ -->
<img src="https://bank.example.com/transfer?to=attacker&amount=100000" 
     width="0" height="0" />

<!-- または通常のリンクとして -->
<a href="https://bank.example.com/transfer?to=attacker&amount=100000">
  お得なキャンペーンはこちら
</a>

被害者がこのページを訪問すると、ブラウザは自動的に画像を読み込もうとし、送金リクエストが実行されます。

POSTリクエストを悪用した攻撃

POSTリクエストを使用していても、CSRF攻撃は可能です。攻撃者は隠しフォームを使用します。

攻撃コード:

1
2
3
4
5
6
<body onload="document.forms[0].submit()">
  <form action="https://bank.example.com/transfer" method="POST">
    <input type="hidden" name="to_account" value="attacker" />
    <input type="hidden" name="amount" value="100000" />
  </form>
</body>

このHTMLページを被害者が開くと、JavaScriptによってフォームが自動送信されます。

JSONベースのAPIに対する攻撃

モダンなAPIでもCSRF攻撃のリスクは存在します。ただし、application/jsonのContent-Typeを使用する場合、CORSのプリフライトリクエストが発生するため、サーバー側で適切にCORS設定がされていれば防御できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<script>
fetch('https://api.example.com/transfer', {
  method: 'POST',
  credentials: 'include',  // Cookieを含める
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to_account: 'attacker',
    amount: 100000
  })
});
</script>

同一オリジンポリシーとCORSにより、このリクエストはプリフライトが必要となり、サーバーがCORSを許可していなければブロックされます。

効かないCSRF対策

CSRF対策として誤解されやすい、実際には効果のない対策を確認しておきましょう。

Cookieをシークレットにする

Cookieの値を秘密にしても、ブラウザが自動的にCookieを送信する以上、CSRF対策にはなりません。

POSTメソッドのみを使用する

POSTリクエストもフォームの自動送信で簡単に偽装できるため、CSRF対策としては不十分です。

HTTPSを使用する

HTTPSは通信の暗号化を提供しますが、CSRFを防ぐことはできません。ただし、CSRF対策が信頼できるものであるための前提条件としてHTTPSは必須です。

Refererヘッダーの検証

Refererヘッダーは偽装される可能性があり、またプライバシー設定やブラウザの設定によって送信されない場合もあるため、単独での対策としては不十分です。

マルチステップトランザクション

複数のステップを経る処理であっても、攻撃者がすべてのステップを予測できれば攻撃は可能です。

CSRFトークンによる対策

CSRFトークンは、CSRF攻撃を防ぐための最も効果的で広く採用されている対策方法です。

CSRFトークンの仕組み

CSRFトークンは、サーバーが生成する予測不可能なランダムな値で、フォームやリクエストに含めることで、リクエストが正規のものであることを検証します。

sequenceDiagram
    participant User as ユーザー
    participant Server as サーバー
    participant Attacker as 攻撃者サイト経由

    Note over User,Server: Step 1: サーバーがCSRFトークンを生成・送信
    User->>Server: フォームページをリクエスト
    Server-->>User: HTML + CSRFトークン(hidden input)

    Note over User,Server: Step 2: ユーザーがフォームを送信(トークン付き)
    User->>Server: POST + CSRFトークン
    Server->>Server: トークン検証(一致すれば許可)
    Server-->>User: 処理成功

    Note over Attacker,Server: Step 3: 攻撃者のサイトからのリクエスト(トークンなし)
    Attacker->>Server: POST(トークンなし or 不正)
    Server->>Server: トークン検証(不一致で拒否)
    Server-->>Attacker: 403 Forbidden

Synchronizer Token Pattern(同期トークンパターン)

最も一般的なCSRFトークンの実装パターンです。サーバーがセッションごとにトークンを生成し、フォームに埋め込みます。

サーバー側の実装例(Node.js/Express):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const crypto = require('crypto');
const session = require('express-session');

// セッション設定
app.use(session({
  secret: 'your-secret-key',
  resave: false,
  saveUninitialized: true,
  cookie: { 
    secure: true,      // HTTPSのみ
    httpOnly: true,    // JavaScript からアクセス不可
    sameSite: 'strict' // 追加の防御層
  }
}));

// CSRFトークン生成ミドルウェア
function generateCsrfToken(req, res, next) {
  if (!req.session.csrfToken) {
    req.session.csrfToken = crypto.randomBytes(32).toString('hex');
  }
  res.locals.csrfToken = req.session.csrfToken;
  next();
}

// CSRFトークン検証ミドルウェア
function validateCsrfToken(req, res, next) {
  const token = req.body._csrf || req.headers['x-csrf-token'];
  
  if (!token || token !== req.session.csrfToken) {
    return res.status(403).json({ error: 'Invalid CSRF token' });
  }
  next();
}

// フォームページ
app.get('/transfer', generateCsrfToken, (req, res) => {
  res.render('transfer', { csrfToken: res.locals.csrfToken });
});

// フォーム送信処理
app.post('/transfer', validateCsrfToken, (req, res) => {
  // 送金処理を実行
  res.json({ success: true });
});

HTMLフォームへの埋め込み:

1
2
3
4
5
6
<form action="/transfer" method="POST">
  <input type="hidden" name="_csrf" value="{{csrfToken}}" />
  <input type="text" name="to_account" placeholder="送金先口座" />
  <input type="number" name="amount" placeholder="金額" />
  <button type="submit">送金する</button>
</form>

Double Submit Cookie(二重送信Cookie)

ステートレスな実装が必要な場合に使用されるパターンです。CSRFトークンをCookieとリクエストボディ(またはヘッダー)の両方に含め、サーバー側で両者が一致することを確認します。

実装例(Node.js/Express):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const crypto = require('crypto');

// CSRFトークンを生成しCookieに設定
function setCsrfCookie(req, res, next) {
  if (!req.cookies.csrfToken) {
    const token = crypto.randomBytes(32).toString('hex');
    res.cookie('csrfToken', token, {
      httpOnly: false,  // JavaScriptから読み取り可能にする
      secure: true,
      sameSite: 'strict'
    });
  }
  next();
}

// CSRFトークン検証
function validateDoubleSubmit(req, res, next) {
  const cookieToken = req.cookies.csrfToken;
  const headerToken = req.headers['x-csrf-token'];
  
  if (!cookieToken || !headerToken || cookieToken !== headerToken) {
    return res.status(403).json({ error: 'Invalid CSRF token' });
  }
  next();
}

クライアント側のJavaScript:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Cookieからトークンを取得
function getCsrfToken() {
  const match = document.cookie.match(/csrfToken=([^;]+)/);
  return match ? match[1] : null;
}

// AJAXリクエストにトークンを付与
async function transferMoney(toAccount, amount) {
  const response = await fetch('/api/transfer', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-Token': getCsrfToken()
    },
    body: JSON.stringify({ to_account: toAccount, amount: amount })
  });
  return response.json();
}

署名付きDouble Submit Cookie(推奨)

基本的なDouble Submit Cookieパターンは、サブドメインからのCookie注入攻撃に対して脆弱な可能性があります。より安全な実装として、HMACを使用した署名付きトークンが推奨されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const crypto = require('crypto');

const CSRF_SECRET = process.env.CSRF_SECRET;

// HMAC署名付きCSRFトークンを生成
function generateSignedToken(sessionId) {
  const randomValue = crypto.randomBytes(32).toString('hex');
  const message = `${sessionId.length}!${sessionId}!${randomValue.length}!${randomValue}`;
  const hmac = crypto.createHmac('sha256', CSRF_SECRET)
                     .update(message)
                     .digest('hex');
  return `${hmac}.${randomValue}`;
}

// トークンを検証
function verifySignedToken(token, sessionId) {
  const [hmacFromToken, randomValue] = token.split('.');
  if (!hmacFromToken || !randomValue) return false;
  
  const message = `${sessionId.length}!${sessionId}!${randomValue.length}!${randomValue}`;
  const expectedHmac = crypto.createHmac('sha256', CSRF_SECRET)
                              .update(message)
                              .digest('hex');
  
  // タイミング攻撃を防ぐため、定数時間比較を使用
  return crypto.timingSafeEqual(
    Buffer.from(hmacFromToken),
    Buffer.from(expectedHmac)
  );
}

CSRFトークンの要件

効果的なCSRFトークンは以下の要件を満たす必要があります。

要件 説明
一意性 ユーザーセッションごとに一意であること
秘密性 攻撃者が推測できないこと
予測不可能性 暗号論的に安全な乱数で生成されること
セッション紐付け 特定のユーザーセッションに紐づいていること

SameSite Cookie属性によるCSRF対策

SameSite Cookie属性は、ブラウザがクロスサイトリクエストでCookieを送信するかどうかを制御する機能です。CSRF対策として非常に効果的であり、現在のモダンブラウザではデフォルトでLaxが適用されます。

SameSite属性の3つの値

属性値 動作
Strict クロスサイトリクエストでは一切送信しない。最も安全だがUXに影響する可能性あり
Lax(デフォルト) トップレベルナビゲーション + GETのみ送信。セキュリティとUXのバランスが良い
None すべてのリクエストで送信(Secure必須)。クロスサイト機能が必要な場合のみ使用

SameSite=Strict

最も厳格な設定で、クロスサイトリクエストではCookieが一切送信されません。

1
2
3
4
5
6
7
8
9
// Express.jsでの設定例
app.use(session({
  // ...
  cookie: {
    secure: true,
    httpOnly: true,
    sameSite: 'strict'
  }
}));

注意点: 外部サイトからのリンクでもCookieが送信されないため、ユーザーは毎回ログインが必要になる可能性があります。銀行サイトなど、外部リンクからのアクセスが想定されない場合に適しています。

SameSite=Lax

トップレベルナビゲーション(リンククリックなど)かつGETメソッドの場合のみCookieを送信します。POSTフォームやAJAXリクエストではCookieが送信されません。

1
2
3
4
5
6
7
8
9
// Express.jsでの設定例
app.use(session({
  // ...
  cookie: {
    secure: true,
    httpOnly: true,
    sameSite: 'lax'  // デフォルト値
  }
}));

Laxは多くのWebアプリケーションに適したバランスの良い設定です。2020年以降、主要ブラウザはデフォルトでLaxを適用しています。

SameSite=None

クロスサイトリクエストでもCookieを送信します。サードパーティCookieが必要な場合(埋め込みウィジェット、OAuth連携など)に使用しますが、必ずSecure属性と併用する必要があります。

1
2
3
4
5
6
// Express.jsでの設定例
res.cookie('sessionId', value, {
  secure: true,      // 必須
  httpOnly: true,
  sameSite: 'none'
});

SameSite属性の実装例

Node.js/Express:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
const express = require('express');
const session = require('express-session');

const app = express();

app.use(session({
  name: 'sessionId',
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: process.env.NODE_ENV === 'production',
    httpOnly: true,
    sameSite: 'lax',
    maxAge: 24 * 60 * 60 * 1000  // 24時間
  }
}));

Python/Django:

1
2
3
4
5
6
7
# settings.py
SESSION_COOKIE_SAMESITE = 'Lax'
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True

CSRF_COOKIE_SAMESITE = 'Lax'
CSRF_COOKIE_SECURE = True

PHP:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// PHP 7.3以降
session_set_cookie_params([
    'lifetime' => 86400,
    'path' => '/',
    'domain' => 'example.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax'
]);
session_start();

SameSite属性の注意点

SameSite Cookie属性は強力なCSRF対策ですが、単独での防御に頼るべきではありません。

  1. レガシーブラウザの非対応: 古いブラウザではSameSite属性がサポートされていない場合があります
  2. サブドメインの問題: サブドメインからの攻撃には対応できない場合があります
  3. GETリクエストの問題: Laxモードでは、GETリクエストによる状態変更(避けるべき設計)に対しては防御できません

そのため、SameSite属性は多層防御の一部として、CSRFトークンと併用することが推奨されます。

Fetch Metadataヘッダーによる対策

Fetch Metadataは、リクエストの送信元に関する追加情報をサーバーに提供するHTTPヘッダーです。サーバー側でこれらのヘッダーを検証することで、クロスサイトリクエストをブロックできます。

主要なFetch Metadataヘッダー

ヘッダー 説明
Sec-Fetch-Site リクエストの送信元とターゲットの関係(same-origin, same-site, cross-site, none
Sec-Fetch-Mode リクエストのモード(navigate, cors, no-corsなど)
Sec-Fetch-Dest リクエストの目的(document, script, imageなど)
Sec-Fetch-User ユーザーの操作によるリクエストかどうか

サーバー側での検証実装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
function validateFetchMetadata(req, res, next) {
  const secFetchSite = req.headers['sec-fetch-site'];
  const method = req.method.toUpperCase();
  
  // Fetch Metadataが存在する場合
  if (secFetchSite) {
    // クロスサイトからの状態変更リクエストをブロック
    if (secFetchSite === 'cross-site' && 
        !['GET', 'HEAD', 'OPTIONS'].includes(method)) {
      return res.status(403).json({ error: 'Cross-site request blocked' });
    }
  }
  
  // Fetch Metadataが存在しない場合(レガシーブラウザ)
  // フォールバックとしてCSRFトークン検証などを実施
  
  next();
}

app.use(validateFetchMetadata);

ブラウザサポート状況

Fetch Metadataヘッダーは、2023年3月以降のすべての主要ブラウザでサポートされており、グローバルカバレッジは98%を超えています。レガシーブラウザのためのフォールバック(Origin/Refererヘッダー検証やCSRFトークン)を併用することが推奨されます。

フレームワーク別のCSRF対策

多くのWebフレームワークには、CSRF対策機能が組み込まれています。これらを活用することで、安全かつ効率的に対策を実装できます。

Express.js(csurf)

注意: csurfパッケージは現在メンテナンスされていないため、代替としてcsrf-csrfなどの使用が推奨されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
const { doubleCsrf } = require('csrf-csrf');

const {
  generateToken,
  doubleCsrfProtection
} = doubleCsrf({
  getSecret: () => process.env.CSRF_SECRET,
  cookieName: '__Host-csrf',
  cookieOptions: {
    secure: true,
    sameSite: 'strict'
  }
});

app.use(doubleCsrfProtection);

app.get('/form', (req, res) => {
  res.render('form', { csrfToken: generateToken(req, res) });
});

Django

Djangoには堅牢なCSRF保護機能が組み込まれています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# settings.py
MIDDLEWARE = [
    # ...
    'django.middleware.csrf.CsrfViewMiddleware',
    # ...
]

# テンプレート
# <form method="post">
#     {% csrf_token %}
#     ...
# </form>
1
2
3
4
5
6
7
<!-- Djangoテンプレート -->
<form method="post">
  {% csrf_token %}
  <input type="text" name="to_account" />
  <input type="number" name="amount" />
  <button type="submit">送金</button>
</form>

Ruby on Rails

Rails 5.2以降では、デフォルトでCSRF保護が有効になっています。

1
2
3
4
# application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
end
<!-- ERBテンプレート -->
<%= form_with url: transfer_path do |form| %>
  <%= form.text_field :to_account %>
  <%= form.number_field :amount %>
  <%= form.submit '送金' %>
<% end %>

Spring Security(Java)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) 
            throws Exception {
        http
            .csrf(csrf -> csrf
                .csrfTokenRepository(
                    CookieCsrfTokenRepository.withHttpOnlyFalse()
                )
            );
        return http.build();
    }
}

Angular

Angularには、Cookie-to-Headerパターンが組み込まれています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// app.config.ts
import { provideHttpClient, withXsrfConfiguration } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(
      withXsrfConfiguration({
        cookieName: 'XSRF-TOKEN',
        headerName: 'X-XSRF-TOKEN'
      })
    )
  ]
};

CSRF対策のベストプラクティス

効果的なCSRF対策を実装するためのベストプラクティスをまとめます。

多層防御の実装

単一の対策に頼らず、複数の対策を組み合わせることで、より強固な防御を実現できます。

flowchart TB
    A["第1層: SameSite Cookie<br>ブラウザレベルでクロスサイトリクエストを制限"]
    B["第2層: CSRFトークン検証<br>予測不可能なトークンでリクエストの正当性を確認"]
    C["第3層: Fetch Metadata検証<br>リクエストの送信元を検証"]
    D["第4層: Origin/Referer検証<br>補助的な送信元チェック(フォールバック)"]
    
    A --> B --> C --> D

チェックリスト

項目 推奨事項
CSRFトークン すべての状態変更リクエストに必須
SameSite Cookie セッションCookieにLax以上を設定
Secure属性 本番環境では必ず有効化
HttpOnly属性 セッションCookieに設定
GETリクエスト 状態変更には絶対に使用しない
フレームワーク 組み込みのCSRF対策機能を活用
XSS対策 CSRF対策と併せて実施(XSSはCSRF対策を無効化できる)

ログインフォームのCSRF対策

ログインフォームもCSRF攻撃の対象となりえます(ログインCSRF)。攻撃者のアカウントにユーザーをログインさせ、その後の活動を監視する攻撃が可能です。

ログインCSRF対策として、プレセッション(認証前のセッション)でCSRFトークンを発行し、ログインフォームに含めることが推奨されます。

まとめ

CSRF(クロスサイトリクエストフォージェリ)は、ユーザーの認証情報を悪用してサーバーに不正なリクエストを送信する攻撃です。この記事で解説した内容を振り返ります。

  1. CSRFの仕組み: ユーザーがログイン状態で悪意のあるサイトを訪問すると、ブラウザがCookieを自動送信することを悪用した攻撃
  2. 成立条件: 状態変更リクエストの存在、Cookieベースの認証、予測可能なパラメータの3条件が揃った場合に攻撃が成立
  3. CSRFトークン: 予測不可能なトークンをリクエストに含め、サーバーで検証することで対策
  4. SameSite Cookie: ブラウザレベルでクロスサイトリクエストへのCookie送信を制限
  5. 多層防御: 単一の対策ではなく、複数の対策を組み合わせることが重要

CSRFトークンとSameSite Cookie属性を組み合わせた多層防御を実装し、安全なWebアプリケーションを構築しましょう。

参考リンク