CSRF対策としてのPHPでのトークン発行方法
トークンの発行方法を勉強する機会があったのでメモ。
CSRFとは?
CSRF:Cross-site Request Forgery(クロスサイト リクエスト フォージェリ)の略。 日本語にすると「リクエスト強要」ということらしい。
悪意のある第三者がWebサービスの利用者に意図しないHTTPリクエストを送信させ、サービス提供側が意図しない処理を実行させること。
forjeryは「偽造」という意味。
対策方法としてのトークン発行
CSRF対策方法の一つとして、トークンを利用するやり方がある。
Webサービスに何らかのフォームを設置する場合、
① あらかじめセッション(通信の一連の処理)に推測されにくい文字列(トークン)を格納しておき、
② ユーザーがフォーム送信する際に、フォームからもセッションに格納されたトークンを引っぱってきて送信
③ サーバー側でセッションの中のトークンとフォームから送られてきたトークンが一致するかをチェックする
これによってフォームから変なデータを受け取らないようにする。
PHPでのトークン発行方法
index.php フォーム側のコード例
<?php session_start(); //セッションを開始 require_once(__DIR__ . '/Hoge.php'); ?> <!DOCTYPE html> <html lang="ja"> <body> <div id="container"> <h1>Sample Site</h1> <form action=""> <input type="text"> <input type="hidden" id="token" value="<?= h($_SESSION['token']); ?>"> //hiddenでセッションからとったトークンを一緒に送信 <input type="submit" value="送信"> </form> </div> </body> </html>
<?php class Hoge { public function __construct() { $this->createToken(); //トークン発行のメソッドを呼び出し } //トークン発行(メソッドとしてまとめておく) private function createToken() { if (!isset($_SESSION['token'])) { //トークンがセットされていなかったら $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(16)); } //32桁のトークンを発行 } //フォームが送信されたら if ($_SERVER['REQUEST_METHOD']==='POST') { $this->validateToken(); } //トークンの照合 private function validateToken() { if ( !isset($_SESSION['token']) || //トークンがセットされていない場合 !isset($_POST['token']) || //トークンが送信されていない場合 $_SESSION['token'] !== $_POST['token'] // 両者が一致しない場合 ) { throw new \Exception("invalid token!"); //バリデーションエラーとする } } }