固定トークンによるCSRF対策

WEBのフォームで固定トークンを生成して、CSRF(クロスサイトリクエストフォージェリ)対策をします。入力画面での実装は以下の通りです。

セッションを開始

トークンのセッション変数がNULLならトークンをセッション変数に代入

トークンを変数に代入

フォームのhiddenフィールドの値に変数に代入したトークンを指定して確認画面へPOST

<?php
//セッションを開始
session_start();
//初回以外ですでにセッション変数に値が代入されていれば、その値を。そうでなければNULLで初期化
$username = isset($_SESSION['username']) ? $_SESSION['username'] : NULL;
//CSRF対策の固定トークンを生成
if(!isset($_SESSION['ticket'])){
  //セッション変数にトークンを代入
  $_SESSION['ticket'] = sha1(uniqid(mt_rand(), true));
}
//トークンを変数に代入
$ticket = $_SESSION['ticket'];
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<body>
<p>お問い合わせフォーム:入力</p>
<form method="post" action="confirm.php">
<p>お名前:<input type="text" name="username" size="15" value="<?php echo $username; ?>"></p>
<!--確認ページへトークンをPOSTする、隠しフィールド「ticket」-->
<input type="hidden" name="ticket" value="<?php echo $ticket; ?>">
<input type="submit" value="確認画面へ">
</form>
</body>
</html>

確認画面での実装は以下の通りです。die関数は引数に指定した文字列を出力してスクリプトを終了します。exit関数と同じです。

セッションを開始

確認画面へPOSTされたトークンとセッション変数に代入したトークンがあれば次の処理へ。なければdie関数でエラーメッセージを出力して処理を中断。(確認画面への直アクセスを禁止)

確認画面へPOSTされたトークンとセッション変数に代入したトークンが同じであれば次の処理へ。同じでなければdie関数でエラーメッセージを出力して処理を中断。(CSRF対策)

<?php
//セッションを開始
session_start();
//固定トークンを確認(CSRF対策)
if(isset($_POST['ticket'], $_SESSION['ticket'])){
  if($_POST['ticket'] !== $_SESSION['ticket']){
    //トークンが一致しない場合は処理を中止
    die('Access Denied!');
  }
} else {
  //トークンが存在しない場合は処理を中止(直接このページにアクセスするとエラーになる)
  die('Access Denied(直接このページにはアクセスできません)');
}
//POSTされたデータをセッション変数に保存
$_SESSION['username'] = $_POST['username'];
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<body>
<p>お問い合わせフォーム:確認</p>
<p>お名前:<?php echo $_POST['username']; ?></p>
<p><a href="index.php">入力画面へ戻る</a></p>
</body>
</html>
タイトルとURLをコピーしました