カテゴリ: PHP 更新日: 2026/01/11

PHP の CSRF 対策(トークンの活用)を完全解説!初心者向けガイド

PHP の CSRF 対策(トークンの活用)
PHP の CSRF 対策(トークンの活用)

新人と先輩の会話形式で理解しよう

新人

「CSRF って何ですか?」

先輩

「CSRF は『Cross-Site Request Forgery(クロスサイトリクエストフォージェリ)』の略で、日本語では『クロスサイトリクエスト偽造』と言うよ。」

新人

「どんな攻撃なんですか?」

先輩

「簡単に言うと、悪意のあるウェブサイトが、ユーザーの知らない間に別のウェブサイトにリクエストを送ってしまう攻撃だよ。」

新人

「えっ!?それってどういう仕組みなんですか?」

先輩

「じゃあ、具体的に CSRF の仕組みを説明するね!」

-

1. CSRF とは?

1. CSRF とは?
1. CSRF とは?

CSRF(クロスサイトリクエストフォージェリ)は、悪意のある第三者が、ユーザーの知らない間に別のウェブサイトに対して不正なリクエストを送る攻撃のことです。

たとえば、ログイン中の銀行サイトで、ユーザーが送金操作をしないにもかかわらず、攻撃者が仕掛けた偽のページにアクセスするだけで勝手に送金されてしまうことがあります。

CSRF の特徴

  • 攻撃者がユーザーの意図しないリクエストを送信する
  • ユーザーが意図せず操作を行ってしまう
  • 被害者がログインしている状態で攻撃が成立しやすい

2. CSRF 攻撃の仕組み

2. CSRF 攻撃の仕組み
2. CSRF 攻撃の仕組み

CSRF 攻撃は、以下のような流れで行われます。

攻撃の流れ

  1. ユーザーが銀行サイトにログインする
  2. 攻撃者が悪意のあるサイトを作成し、ユーザーを誘導する
  3. 悪意のあるサイトがユーザーのブラウザを利用して銀行サイトにリクエストを送信
  4. 銀行サイトは、ログイン済みのユーザーからのリクエストと判断し、処理を実行

具体的な攻撃例

例えば、攻撃者が以下のような HTML を作成し、ユーザーをこのページにアクセスさせたとします。


<form action="https://bank.example.com/transfer" method="post">
    <input type="hidden" name="to_account" value="999999">
    <input type="hidden" name="amount" value="100000">
    <input type="submit" value="クリックでプレゼント">
</form>

このフォームがユーザーのブラウザで自動送信されるように細工されていると、ログイン済みの銀行サイトに対して、意図しない送金リクエストが送られてしまいます。

CSRF の危険性

この攻撃によって、次のような被害が発生する可能性があります。

  • 勝手に送金されてしまう
  • パスワードが変更されてしまう
  • アカウントが削除される

このような攻撃を防ぐために、CSRF 対策をしっかりと行う必要があります。次回は、具体的な対策方法として「トークンを活用した CSRF 対策」について解説します。

3. CSRF 対策の基本(トークンの仕組みとは?)

3. CSRF 対策の基本(トークンの仕組みとは?)
3. CSRF 対策の基本(トークンの仕組みとは?)

CSRF 攻撃を防ぐためには、「CSRF トークン」という仕組みを利用します。

CSRF トークンとは、一意なランダム文字列を発行し、それをフォームに埋め込むことで、リクエストが正規のものであるかを確認する仕組みです。

CSRF トークンの仕組み

  1. サーバー側でランダムな CSRF トークンを生成し、セッションに保存する。
  2. フォームの hidden フィールドに CSRF トークンを埋め込む。
  3. フォームが送信された際、送信されたトークンとセッションのトークンを比較し、一致すればリクエストを受け付ける。

なぜ CSRF トークンが有効なのか?

攻撃者が勝手にリクエストを送ることはできても、サーバーに保存されたトークンを知ることはできません。そのため、トークンが一致しないリクエストはブロックされる仕組みになっています。

4. CSRF トークンを使ったフォームの保護(基本的な実装方法)

4. CSRF トークンを使ったフォームの保護(基本的な実装方法)
4. CSRF トークンを使ったフォームの保護(基本的な実装方法)

実際に CSRF トークンをフォームに埋め込み、安全にリクエストを処理する方法を見ていきましょう。

フォームの作成

フォームに CSRF トークンを埋め込むため、まずは PHP でトークンを生成し、セッションに保存します。


<?php
session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>

次に、フォームの hidden フィールドに CSRF トークンを埋め込みます。


<form action="process.php" method="post">
    <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token'], ENT_QUOTES, 'UTF-8'); ?>">
    
    <label for="name">名前:</label>
    <input type="text" id="name" name="name">
    
    <button type="submit">送信</button>
</form>

このようにすることで、ユーザーがフォームを送信するたびに、サーバーが発行した CSRF トークンを一緒に送信するようになります。

-

5. CSRF トークンの生成と検証(PHP の具体的なコード例)

5. CSRF トークンの生成と検証(PHP の具体的なコード例)
5. CSRF トークンの生成と検証(PHP の具体的なコード例)

送信された CSRF トークンをサーバーで検証し、一致しない場合はリクエストを拒否する処理を追加しましょう。

トークンの検証処理

送信された CSRF トークンと、セッションに保存されたトークンを比較します。


<?php
session_start();
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    if (!isset($_POST["csrf_token"]) || $_POST["csrf_token"] !== $_SESSION["csrf_token"]) {
        die("不正なリクエストです!");
    }
    
    echo "リクエストが正常に処理されました!";
}
?>

CSRF トークンのリセット

CSRF トークンは一度使ったら無効にすることで、セキュリティをさらに強化できます。リクエストが成功したら、新しいトークンを発行しましょう。


<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    if (!isset($_POST["csrf_token"]) || $_POST["csrf_token"] !== $_SESSION["csrf_token"]) {
        die("不正なリクエストです!");
    }

    echo "リクエストが正常に処理されました!";
    
    // 新しいトークンを発行
    $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
}
?>

CSRF トークンの有効期限を設定

トークンに有効期限を設定することで、セキュリティをさらに強化できます。


<?php
session_start();
if (!isset($_SESSION["csrf_token"]) || time() - $_SESSION["csrf_token_time"] > 300) {
    $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
    $_SESSION["csrf_token_time"] = time();
}
?>

このコードでは、CSRF トークンの有効期限を 5 分(300 秒)に設定しています。

次回は、CSRF トークンの有効期限や、他の CSRF 対策方法について詳しく解説します。

6. CSRF トークンの有効期限とセッション管理(より安全な実装)

6. CSRF トークンの有効期限とセッション管理(より安全な実装)
6. CSRF トークンの有効期限とセッション管理(より安全な実装)

CSRF トークンは一度発行したら永続的に使用できるわけではなく、一定時間で無効化するのが推奨されています。ここでは、有効期限を設けてより安全な実装を行う方法を紹介します。

トークンの有効期限を設定する理由

  • セッションが長時間続いた場合でも、古いトークンを使った攻撃を防ぐため
  • 1回のフォーム送信ごとに新しいトークンを発行し、再利用を防ぐため

トークンの有効期限を設定する方法

PHP で CSRF トークンの有効期限を 10 分(600 秒)に設定する例を示します。


<?php
session_start();

// トークンの有効期限(秒)
define("CSRF_TOKEN_EXPIRY", 600);

if (!isset($_SESSION["csrf_token"]) || time() - $_SESSION["csrf_token_time"] > CSRF_TOKEN_EXPIRY) {
    $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
    $_SESSION["csrf_token_time"] = time();
}

// トークンをフォームに埋め込む
?>

フォームに CSRF トークンを埋め込む


<form action="process.php" method="post">
    <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token'], ENT_QUOTES, 'UTF-8'); ?>">
    
    <label for="name">名前:</label>
    <input type="text" id="name" name="name">
    
    <button type="submit">送信</button>
</form>

トークンの検証時に有効期限をチェック


<?php
session_start();

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    if (!isset($_POST["csrf_token"]) || !isset($_SESSION["csrf_token"]) || $_POST["csrf_token"] !== $_SESSION["csrf_token"]) {
        die("不正なリクエストです!");
    }

    // 有効期限のチェック
    if (time() - $_SESSION["csrf_token_time"] > CSRF_TOKEN_EXPIRY) {
        die("トークンの有効期限が切れています。再度フォームを送信してください。");
    }

    echo "リクエストが正常に処理されました!";
    
    // 新しいトークンを発行
    $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
    $_SESSION["csrf_token_time"] = time();
}
?>

このコードでは、トークンの有効期限を超えている場合、フォームの再送信を促すようにしています。

7. 他の CSRF 対策方法(Referer チェックや SameSite 属性の活用)

7. 他の CSRF 対策方法(Referer チェックや SameSite 属性の活用)
7. 他の CSRF 対策方法(Referer チェックや SameSite 属性の活用)

CSRF トークン以外にも、CSRF 攻撃を防ぐための手法があります。その中でも「Referer チェック」と「SameSite 属性」を活用した対策を紹介します。

Referer チェック

Referer チェックとは、リクエストの送信元 URL を確認し、正規のサイトからのリクエストかどうかを判定する方法です。


<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    if (!isset($_SERVER["HTTP_REFERER"]) || strpos($_SERVER["HTTP_REFERER"], "https://example.com") !== 0) {
        die("不正なリクエストです!");
    }
}
?>

この方法では、リクエストの Referer ヘッダーが正しいサイトからのものであることを確認します。ただし、ブラウザやネットワーク環境によっては Referer ヘッダーが送信されない場合があるため、単独の対策としては不十分です。

SameSite 属性の活用

SameSite 属性を設定することで、異なるサイトからのリクエスト時に Cookie を送信しないようにできます。これにより、CSRF 攻撃のリスクを軽減できます。


<?php
session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'domain' => '',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);
session_start();
?>

この設定では、SameSite 属性を「Strict」にすることで、他のサイトからのリクエスト時に Cookie が送信されなくなります。

8. PHP の CSRF 対策を学ぶためのおすすめの方法

8. PHP の CSRF 対策を学ぶためのおすすめの方法
8. PHP の CSRF 対策を学ぶためのおすすめの方法

CSRF 対策をより深く理解し、実践できるようになるための方法を紹介します。

1. PHP の公式ドキュメントを読む

PHP の公式サイトには、セキュリティに関する情報が掲載されています。最新の対策方法を学ぶために、定期的に確認しましょう。

PHP セキュリティガイド

2. OWASP のセキュリティガイドを参照する

OWASP(Open Web Application Security Project)は、ウェブアプリケーションのセキュリティに関するベストプラクティスを提供しています。

OWASP CSRF ガイド

3. CSRF 脆弱性のあるサンプルアプリを試してみる

実際に CSRF 攻撃の仕組みを理解するために、意図的に脆弱なアプリケーションを使ってテストしてみるのも有効です。

例えば、以下のような手順で試せます。

  • CSRF トークンなしでフォームを作成する
  • 別の HTML ページから勝手にリクエストを送る
  • CSRF 対策を適用し、リクエストがブロックされることを確認する

4. セキュリティテストツールを活用する

CSRF を含むウェブアプリケーションの脆弱性をチェックできるツールもあります。

  • OWASP ZAP(Zed Attack Proxy)
  • Burp Suite

これらのツールを活用し、自分のアプリケーションが安全かどうか確認してみましょう。

これで PHP の CSRF 対策に関する解説は終了です。CSRF は対策を怠ると大きなリスクになるため、しっかりと理解し、実装していきましょう!

まとめ

まとめ
まとめ

本記事では、PHP における CSRF(クロスサイトリクエストフォージェリ)対策について、基本的な概念から実装方法、さらにはセキュリティ強化のための応用的な工夫まで、初心者にも理解しやすいように段階的に解説しました。CSRF はウェブアプリケーションを狙う代表的な攻撃手法の一つであり、特にフォーム送信などの POST リクエストに潜む脅威です。この記事を通じて、「なぜトークンが必要なのか」「どのようにしてフォームに埋め込むのか」「トークンの有効期限や検証方法」「その他の防御手段(Referer チェックや SameSite 属性)」などを総合的に理解することができたと思います。

CSRF トークンの導入は、PHP のセッション管理と密接に関係しており、ユーザーセッションを活用した厳密なトークン検証処理を設けることで、悪意のあるリクエストをしっかりと防ぐことができます。また、トークンの有効期限や再発行の仕組みを取り入れることで、より強固なセキュリティ体制を構築することができます。

セキュリティは一度学べば終わりというものではなく、日々のアップデートや実装の見直しが重要です。CSRF 対策も例外ではなく、定期的にコードの見直しやセキュリティガイドラインの確認を行うことが、安全なウェブ開発に繋がります。

CSRF トークンの実装イメージ(確認用)


<?php
session_start();
define("CSRF_TOKEN_EXPIRY", 600);

if (!isset($_SESSION["csrf_token"]) || time() - $_SESSION["csrf_token_time"] > CSRF_TOKEN_EXPIRY) {
    $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
    $_SESSION["csrf_token_time"] = time();
}
?>

<form action="process.php" method="post">
    <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token'], ENT_QUOTES, 'UTF-8'); ?>">
    <label for="email">メールアドレス:</label>
    <input type="email" name="email" id="email">
    <button type="submit">送信</button>
</form>

<?php
session_start();

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    if (!isset($_POST["csrf_token"]) || $_POST["csrf_token"] !== $_SESSION["csrf_token"]) {
        die("不正なリクエストです!");
    }

    if (time() - $_SESSION["csrf_token_time"] > CSRF_TOKEN_EXPIRY) {
        die("トークンの有効期限が切れています。");
    }

    echo "送信成功しました!";

    $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
    $_SESSION["csrf_token_time"] = time();
}
?>
先生と生徒の振り返り会話

生徒

「先生、CSRF の対策って実際に自分でコードを書いてみると、トークンの仕組みがよくわかりますね!」

先生

「その通り。セッションでトークンを管理して、フォーム送信時に確認するだけで、かなりの効果があるんだ。しっかりバリデーションすることが大事だよ。」

生徒

「有効期限を設定したり、使い終わったら再生成する工夫も勉強になりました!」

先生

「それがセキュアな開発の第一歩だね。さらに Referer チェックや SameSite 属性も活用すると、より安全な設計になるよ。」

生徒

「セキュリティ対策って一つだけじゃなく、複数を組み合わせることが大切なんですね。」

先生

「その通り!『多層防御(Defense in Depth)』の考え方を持って、今後も安心して使える PHP アプリケーションを作っていこう!」

この記事を読んだ人からの質問

この記事を読んだ人からの質問
この記事を読んだ人からの質問

プログラミング初心者からのよくある疑問/質問を解決します

CSRF攻撃とは何ですか?初心者でもわかるように教えてください。

CSRF攻撃とは「クロスサイトリクエストフォージェリ」と呼ばれ、ユーザーが意図しないリクエストを第三者が送信することで、ログイン中のサイトで不正な操作が行われてしまう攻撃のことです。

PHPでCSRF対策をするにはどんな方法がありますか?

最も基本的なCSRF対策は、フォームに「CSRFトークン」というランダムな文字列を埋め込み、それをサーバー側で検証する方法です。PHPのセッション機能を活用して安全性を高めます。
コメント
コメント投稿は、ログインしてください

まだ口コミはありません。

カテゴリの一覧へ
新着記事
New1
PHP
PHPのCookieセキュリティを完全ガイド!初心者でもわかるSecureとHttpOnlyの使い方
New2
PHP
PHPのCSRF対策を完全理解!トークンを使った安全なフォーム送信の仕組みを初心者向けに徹底解説
New3
PHP
PHPのSQLインジェクション対策を完全解説!初心者でも安全にデータベースを扱う方法
New4
PHP
PHPでカウントダウンタイマーを作る方法!初心者向けにやさしく解説
-
人気記事
No.1
Java&Spring記事人気No1
Python
Pythonでテキストファイルを1行ずつ読み込む方法(readline() / readlines())
No.2
Java&Spring記事人気No2
PHP
初心者向けPHPでMySQLからデータを取得する方法(SELECT文)完全ガイド
No.3
Java&Spring記事人気No3
Python
Pythonのリストの重複を削除する方法を解説!初心者向けにsetとdict.fromkeysの使い方をやさしく説明
No.4
Java&Spring記事人気No4
Python
Pythonで経過時間を測る方法をやさしく解説!初心者向けtime.perf_counterとtime.sleepの使い方
No.5
Java&Spring記事人気No5
PHP
PHPのXSS対策を完全ガイド!初心者でもわかる安全なWebアプリの作り方
No.6
Java&Spring記事人気No6
Python
PythonでExcelファイル(.xlsx)を操作する方法を徹底解説!初心者でもできるopenpyxl・pandasの使い方
No.7
Java&Spring記事人気No7
PHP
PHPのOAuth認証をやさしく解説!Google・Facebookログインを初心者向けに実装しよう
No.8
Java&Spring記事人気No8
Python
Pythonでエラースタックトレースを表示・取得する方法を徹底解説!初心者向け例外処理入門
-
-