カテゴリ: PHP 更新日: 2026/06/08

PHPのCSRF対策を完全理解!トークンを使った安全なフォーム送信の仕組みを初心者向けに徹底解説

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

先生と生徒の会話形式で理解しよう

生徒

「PHPでフォームを送信するときに“CSRF”という攻撃が危ないと聞いたんですが、どういうものなんですか?」

先生

「CSRFは、ユーザーが気づかないうちに勝手にフォームを送信させられてしまう攻撃のことだよ。とても危険だから、PHPでWebアプリを作るなら必ず対策が必要なんだ。」

生徒

「どうやって対策するんですか?」

先生

「“CSRFトークン”という秘密の合言葉を使うのが一般的だよ。今日は、その仕組みとPHPでの実装方法をくわしく説明していくね。」

1. CSRF攻撃とは?初心者にもわかる仕組み

1. CSRF攻撃とは?初心者にもわかる仕組み
1. CSRF攻撃とは?初心者にもわかる仕組み

CSRF(Cross-Site Request Forgery・クロスサイトリクエストフォージェリ)とは、ユーザーが意図していないのに勝手に操作を実行されてしまう攻撃のことです。

例えば、あなたがショッピングサイトにログイン中だとします。その状態で悪意あるサイトを開くと、あなたが知らないうちに「注文確定のリクエスト」が送られてしまうことがあります。これは “リクエストを偽造される” という意味で、PHPのWebアプリでは特に注意が必要です。

Webアプリの操作は「ログインしている本人の意図」が前提ですが、CSRF攻撃はその前提を破壊します。そのため、フォーム送信やボタン操作には必ず安全な仕組みが必要です。

2. CSRFを防ぐには「秘密のトークン」が必須

2. CSRFを防ぐには「秘密のトークン」が必須
2. CSRFを防ぐには「秘密のトークン」が必須

CSRF対策で最も一般的なのが「CSRFトークン(令和風に言えば “秘密の合言葉”)」です。このトークンをフォームに埋め込み、送信されたときに一致するかどうかをサーバー側でチェックします。

ポイントは次の3つです:

  • トークンはユーザーごとに異なるランダム文字列
  • トークンをセッションに保存しておく
  • フォームにもトークンを入れて送信し、一致を確認

これにより、外部サイトから勝手にリクエストを送られてもトークンが一致しないため不正なリクエストは拒否されます。

3. PHPでCSRFトークンを作る方法(超基本)

3. PHPでCSRFトークンを作る方法(超基本)
3. PHPでCSRFトークンを作る方法(超基本)

まず、トークンを生成し、セッションに保存します。ランダムな文字列を生成するには bin2hex(random_bytes()) を使うのが安全です。


session_start();

if (!isset($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

random_bytes は暗号学的に安全な乱数を生成します。セキュリティ用途で必ず使うべき関数です。

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

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

次に、生成したトークンをフォームに hidden(画面に表示しない入力欄)として埋め込みます。


<form action="submit.php" method="POST">
    <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
    <input type="text" name="comment">
    <button type="submit">送信</button>
</form>

hiddenタグは、ユーザーには見えませんがフォーム送信時にサーバーへ送られる便利な仕組みです。

5. PHPでCSRFチェックを行う

5. PHPでCSRFチェックを行う
5. PHPでCSRFチェックを行う

送信されたトークンがセッションのトークンと一致しているかを確認します。もし違っていた場合は不正なリクエストとして処理を中止します。


session_start();

if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
    die('不正なアクセスです。');
}

echo "コメントを受け付けました!";

このチェックがPHPのCSRF対策の中心的な部分です。これを入れておくだけで、外部サイトからの偽造リクエストをほぼ100%防ぐことができます。

6. トークンは使い捨てにするのが安全

6. トークンは使い捨てにするのが安全
6. トークンは使い捨てにするのが安全

さらに強固にするなら、「一度使ったトークンは破棄する」という方法があります。これはワンタイムトークンという方式で、フォーム送信が完了したらトークンを削除し、新しいトークンを作ります。

こうすることで、もしトークンが盗まれたとしても再利用される可能性を減らせます。

7. なぜCSRF対策は必須なのか?初心者でも理解できる具体例

7. なぜCSRF対策は必須なのか?初心者でも理解できる具体例
7. なぜCSRF対策は必須なのか?初心者でも理解できる具体例

例えば、銀行のWebアプリで「送金する」ボタンがあったとします。

CSRF対策がないと、悪意あるサイトを開いただけで、あなたのログイン状態を利用して勝手に送金される可能性があります。

これは PHP に限らず、すべてのWebアプリで起こりうる危険です。だからこそ、トークンによるCSRF対策は “必須のセキュリティ設定” として世界中の開発者が実装しています。

8. CSRF対策でよくある間違いと注意点

8. CSRF対策でよくある間違いと注意点
8. CSRF対策でよくある間違いと注意点

初心者がやってしまいがちな注意点もまとめます。

  • トークンを固定してしまう(毎回新しいものを作るべき)
  • GETリクエストにトークンをつける(URLは漏れやすいので危険)
  • トークンのチェックを忘れる(フォーム作って満足してしまう)
  • random_bytes の代わりに mt_rand を使う(セキュアではない)

とくに「トークンをチェックしていない」は多いミスなので注意してください。

まとめ

まとめ
まとめ

CSRF対策の重要ポイントを総整理

本記事では、PHPにおけるCSRF対策の基本から実装方法、そして注意点までを段階的に解説してきました。CSRF(クロスサイトリクエストフォージェリ)は、ユーザーがログインしている状態を悪用し、意図しないリクエストを実行させる非常に危険な攻撃手法です。特にフォーム送信を伴うWebアプリケーションでは、この攻撃に対する備えが欠かせません。

CSRF対策の中心となるのが「CSRFトークン」です。これはユーザーごとに生成されるランダムな文字列であり、セッションに保存された値と、フォームから送信された値を比較することで、正規のリクエストかどうかを判断します。この仕組みによって、外部サイトからの不正なリクエストを確実に防ぐことが可能になります。

また、トークンの生成には random_bytes() を利用することが推奨されます。これは暗号学的に安全な乱数を生成するため、セキュリティ対策として非常に信頼性が高い方法です。逆に、mt_rand() などの疑似乱数を使うと、予測される可能性があるため避けるべきです。

実務で使えるCSRF対策の流れ

実際の開発現場では、以下の流れを確実に実装することが求められます。まず、ページ表示時にCSRFトークンを生成し、セッションに保存します。次に、そのトークンをフォームのhiddenフィールドに埋め込みます。そして、フォーム送信時にPOSTデータとして送られてきたトークンとセッション内のトークンを比較し、一致しなければ処理を中断します。

さらに安全性を高めるためには「ワンタイムトークン」の考え方も重要です。一度使用したトークンは破棄し、新しいトークンを再生成することで、トークンの再利用による攻撃リスクを大幅に減らすことができます。これは銀行系システムや重要な操作を伴うサービスでは特に重要な対策です。

サンプルプログラム(実践的なCSRF対策)


session_start();

// トークン生成
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// フォーム送信時の処理
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
        die('不正なリクエストです');
    }

    // ワンタイムトークンとして破棄
    unset($_SESSION['csrf_token']);

    echo "安全にデータを受信しました";
}

<form method="POST">
    <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
    <input type="text" name="message" class="form-control mb-2">
    <button type="submit" class="btn btn-primary">送信</button>
</form>

よくある失敗と改善ポイント

実際の開発では、「トークンを生成しただけで安心してしまう」「チェック処理を忘れる」「トークンを固定値にしてしまう」といったミスがよく見られます。これらはすべてセキュリティ上の重大な欠陥につながるため注意が必要です。

また、GETリクエストにトークンを含める設計も避けるべきです。URLはブラウザ履歴やログに残るため、第三者に漏洩する可能性があります。CSRF対策は必ずPOSTメソッドと組み合わせて実装しましょう。

まとめの結論

PHPで安全なWebアプリケーションを構築するためには、CSRF対策は必須の実装です。特にログイン機能や決済機能、データ更新処理を含むページでは、トークンによる検証を徹底することで、ユーザーを不正操作から守ることができます。CSRFトークンの生成・埋め込み・検証という基本を確実に押さえ、さらにワンタイム化や適切なセッション管理を組み合わせることで、より強固なセキュリティを実現できます。

先生と生徒の振り返り会話

生徒

「CSRFって最初は難しそうに感じましたが、仕組みは『秘密の合言葉を確認する』って考えると分かりやすいですね。」

先生

「その通りだね。ポイントは“本人が送ったリクエストかどうかを確認する”ことなんだ。トークンはそのための重要な鍵になるよ。」

生徒

「トークンは毎回変えたほうがいいっていうのも理解できました。使い回すと危険なんですね。」

先生

「そうだね。ワンタイムトークンにすることで、万が一漏れても再利用されにくくなる。実務ではここまで考えると安心だよ。」

生徒

「PHPでフォームを作るときは、必ずCSRF対策を入れるようにします!」

先生

「いい意識だね。セキュリティは後からではなく、最初から組み込むことが大切だよ。」

コメント
コメント投稿は、ログインしてください

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

PHP のセキュリティの記事一覧へ
新着記事
New1
Claude
ClaudeにPHPデバッグを相談するコツ!原因候補を仮説に分けて確認する質問例
New2
Python
PythonのCLIで対話型メニューを作る方法(prompt_toolkit)
New3
ChatGPT
ChatGPTでPHPコードを理解する方法!読めない処理をわかりやすく質問しよう
New4
PHP
PHP の定数(define, const)を使った設定方法を徹底解説!
人気記事
No.1
Java&Spring記事人気No1
Python
Pythonで文字列が数値か判定する方法!isdigit()・isnumeric()の違い
No.2
Java&Spring記事人気No2
Python
Pythonのコンストラクタ(__init__)の使い方!初心者でもわかるオブジェクトの初期化
No.3
Java&Spring記事人気No3
Python
Pythonでテキストファイルを1行ずつ読み込む方法(readline() / readlines())
No.4
Java&Spring記事人気No4
Python
PythonでSQLiteを使う方法を完全ガイド!初心者でもわかるデータベース基本操作
No.5
Java&Spring記事人気No5
PHP
PHP のフォームデータの受け取り($_POST, $_GET)を完全解説!初心者向けガイド
No.6
Java&Spring記事人気No6
Python
Pythonのシェルスクリプトを自動実行する方法(cron & Task Scheduler)
No.7
Java&Spring記事人気No7
Claude
ClaudeにPHPリファクタリングを相談するコツ!目的・前提・制約を仕様メモにまとめるコツ
No.8
Java&Spring記事人気No8
PHP
PHPのMVCパターンを完全解説!初心者でもわかるModel・View・Controller