1. 本記事のポイント
- PHPのみを用いた基本的なCSRF対策の実装方法を解説
- フレームワーク非依存で、フォーム処理に適用可能な構成
- セッションを活用したトークン生成・検証の流れを紹介
2. CSRFとは?
CSRF(Cross-Site Request Forgery)は、認証済みユーザーの権限を悪用して、意図しないリクエストを外部サイトから送信させる攻撃です。
例えば、ログイン済みの銀行サイトに対し、攻撃者が仕込んだ外部ページを通じて勝手に送金リクエストを実行させるといったケースが代表例です。ユーザーのCookieが自動送信されることで、不正リクエストであっても正当な操作として処理されるリスクがあります。
CSRF対策として一般的なのは、フォーム送信時にランダムなトークンを埋め込み、それが正当なものであるかをサーバー側で検証する方式です。この仕組みにより、攻撃者が用意した偽のフォームからはトークンが一致せず、リクエストを拒否できます。
実務ではフレームワークに組み込まれていることが多いですが、本記事ではPHPのみで最小限の仕組みを自作する方法に焦点を当てます。
3. 詳細解説
トークンの生成と保存
セッションを使ってトークンを生成・保持します。ページ初回表示時にトークンを作成し、フォームに埋め込みます。
<?php
session_start();
// トークンが未生成であれば作成
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['csrf_token'];
?>
フォームにトークンを埋め込む
フォーム内にhiddenフィールドでトークンを埋め込みます。
<form method="post" action="submit.php">
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($token, ENT_QUOTES); ?>">
<input type="text" name="message">
<input type="submit" value="送信">
</form>
サーバー側での検証
フォーム送信後、トークンが正しいかを検証します。
<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!isset($_POST['csrf_token'], $_SESSION['csrf_token']) ||
!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
die('不正なリクエストです');
}
// 正常なリクエストとして処理を続行
$message = $_POST['message'];
echo "受信メッセージ: " . htmlspecialchars($message, ENT_QUOTES);
}
?>
補足:hash_equalsを使う理由
===
による単純比較ではタイミング攻撃に対して脆弱なため、PHP 5.6以降で導入されたhash_equals()
を使います。
この関数は比較にかかる時間を一定に保つため、安全な比較が可能です。
4. よくあるミス・誤解・落とし穴
- トークンをセッションに保存していない:送信されたトークンと突き合わせるため、セッション側の保持が必須です。
- トークンを使い回す:同じトークンを複数回使い回すと、別ページで取得した値を流用されるリスクがあります。
必要に応じてワンタイム化を検討しましょう。 - トークンの出力に
htmlspecialchars
を忘れる:フォームにトークンを埋め込む際、エスケープしないとXSSの温床になります。 - GETメソッドに適用していない:CSRF対策は基本的に状態変更系(POSTなど)に限定して適用するのが一般的です。
5. まとめ
CSRFは外部サイトからの意図しないリクエストを防ぐための基本的なセキュリティ対策です。
PHPのみでもセッションとトークンを用いた仕組みを構築可能であり、hash_equals
やhtmlspecialchars
を適切に使うことがポイントです。
フレームワークを使わない環境や、小規模なスクリプトでも活用できる手法として覚えておくと便利です。