前提・実現したいこと
[PHP]
CSRF対策
セッションを使って、CSRF対策をしたい。
POSTを押すと、セットされた$_SESSION['token']が新しくなり、$_POST['token']と一致しなくなってしまいます。
$_SESSION['token'] === $_POST['token']を一致させたい。
PHPで簡易的なログインシステムを作っています。
セッション機能を使って、サインアップが押された時に、事前に仕込んであったtokenとPOSTされたtokenを比較する実装をしていて、以下のエラーメッセージが発生しました。
(やりたいことは、読み込み時に、セットしたtokenがPOSTされたtokenと等しかったらOK,等しくなかったらエラーを返すというような処理をしたい。)
発生している問題・エラーメッセージ
Invalid Token!
sign up 前 (サインアップボタンを押す前)
php
1SESSION:token: 66b33407b289f53ad8934224ed071922 2POST:token:
sign up 後 (サインアップボタンを押した後)
php
1SESSION:token: c56356defa3a206e97c3278243e490b1 2POST:token: 66b33407b289f53ad8934224ed071922 3Invalid Token!
該当のソースコード
↓ Controller.php
php
1<?php 2require_once(__DIR__ . '/Controller/Signup.php'); 3 4class Controller 5{ 6 // エラー情報を格納する変数 7 // private $_errors; 8 9 public function __construct() 10 { 11 // セッションを使ってCSRF対策 12 // session_start(); 13 $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(16)); 14 echo 'SESSION:token: ' . $_SESSION['token']; 15 echo '<br>'; 16 echo 'POST:token: ' . $_POST['token']; 17 echo '<br>'; 18 } 19 protected function isLoggedIn() 20 { 21 // ログイン判定 22 // ログイン時にセッションにmeというキーで情報を保持->判定 23 return isset($_SESSION['me']) && !empty($_SESSION['me']); 24 } 25}
↓ Signup.php
php
1<?php 2require_once(__DIR__ . '/../Controller.php'); 3require_once(__DIR__ . '/../Exception/InvalidEmail.php'); 4require_once(__DIR__ . '/../Exception/InvalidPassword.php'); 5 6class Signup extends Controller 7{ 8 public function run() 9 { 10 // ログインしていたら、ホームに飛ばす 11 // フォームがもしポストされたら、postProcess() 12 13 14 // ログインしていたら、ホームに飛ばす 15 if ($this->isLoggedIn()) { 16 // login URLは定数管理 17 header('Location: index.php'); 18 exit; 19 } 20 21 // フォームがもしポストされたら 22 if ($_SERVER['REQUEST_METHOD'] == 'POST') { 23 $this->postProcess(); 24 } 25 } 26 27 protected function postProcess() 28 { 29 // データの検証 30 // ユーザー作成 31 // 登録処理 32 33 // validate 34 // Controllerにエラーオブジェクトを持たせて、値をセット/取得できるようにする 35 try { 36 $this->_validate(); 37 } catch (\InvalidEmail $e) { 38 echo $e->getMessage(); 39 exit; 40 41 // emailのキーでエラーメッセージを渡す 42 // $this->setErrors('email', $e->getMessage()); 43 44 } catch (\InvalidPassword $e) { 45 echo $e->getMessage(); 46 exit; 47 48 // passwordのキーでエラーメッセージを渡す 49 // $this->setErrors('password', $e->getMessage()); 50 } 51 52 // echo 'Success'; 53 // exit; 54 55 // if ($this->hasError()) { 56 // return; 57 // } else { 58 // ユーザー作成 59 // 登録処理 60 // } 61 62 63 } 64 65 private function _validate() 66 { 67 //セッションの検証 68 if ($_POST['token'] !== $_SESSION['token']) { 69 echo "Invalid Token!"; 70 exit; 71 } 72 73 // filter_var関数を使って、OKじゃなかったら例外を返す 74 if(!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) { 75 throw new InvalidEmail(); 76 } 77 78 // preg_matchの正規表現で調べる 79 if(!preg_match('/\A[a-zA-Z0-9]+\z/', $_POST['password'])) { 80 throw new InvalidPassword(); 81 } 82 } 83 84}
↓ signup.php
php
1<?php 2 3// 新規登録 4 5require_once(__DIR__ . '/../config/config.php'); 6require_once(__DIR__ . '/../lib/Controller/Signup.php'); 7require_once(__DIR__ . '/../lib/Controller.php'); 8require_once(__DIR__ . '/../lib/functions.php'); 9 10 11// このビューに表示するデータをControllerから引っ張ってくる 12// Controllerのインスタンスを作る 13$app = new Signup(); 14 15 16// html部分にデータを表示する 17$app->run(); 18 19?> 20 21<!DOCTYPE html> 22<html lang="ja"> 23<head> 24 <meta charset="UTF-8"> 25 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 26 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 27 <title>Sign Up</title> 28 <link rel="stylesheet" href="styles.css"> 29</head> 30<body> 31 <div id="container"> 32 <form action="" method="POST" id="signup"> 33 <p> 34 <input type="text" name="email" placeholder="email"> 35 </p> 36 <p> 37 <input type="password" name="password" placeholder="password"> 38 </p> 39 <div class="btn" onclick="document.getElementById('signup').submit();">Sign Up</div> 40 <p class="fs12"><a href="login.php">Log In</a></p> 41 <input type="hidden" name="token" value="<?php echo h($_SESSION['token']); ?>"> 42 </form> 43 </div> 44 45 46</body> 47</html>
試したこと
echoなどを使って、$_SESSION['token']と$_POST['token']を確認してみたら、
$_SESSION['token']がサインアップボタンを押すたびに新しくトークンが作成されているため、 $_SESSION['token']=$_POST['token'] が永遠と一致せずにエラーになってしまうことがわかりました。
しかし、なぜ$_SESSION['token']がポストするたびに変更されてしまうのかわからないです。
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/09/08 00:24