実現したいこと
「b.php」を実行するときに、「a.php」から遷移してきた場合のみ実行する、という実装を安全につくりたいのです。
セッションをつかう作戦はどうでしょうか
リファラをつかうやりかたは安定しないようでしたので、セッションをつかうやりかたはどうだろうと思いました。
a.php
1<?php 2 session_start(); 3 $_SESSION['a'] = 'a'; 4 header("Location: ./b.php");
↑こちらのa.phpから↓のb.phpへリダイレクトします!
b.php
1<?php 2 session_start(); 3 if($_SESSION['a'] !== 'a'){ 4 exit; 5 } 6 echo "a.phpから遷移してきたb.phpです。";
b.phpが実行されたときに、「$_SESSION['a']に"a"という文字列が入っていない…これは、a.php以外から遷移してきたに違いない!」と判断し、スクリプトを終了します。
セッションのキーと値を便宜上"a"としておりますが、このキーや値を複雑なものにするといいのかな、と考えました。
しかし…もっと安全なやりかたが存在するのならばそれを知りたい!と考え質問を投稿させていただきました。
「想定しているページからの遷移しか許可しない」という実装をする上で何が必要なのか。
アドバイス等いただけますと幸いです。よろしくお願いいたします!
2022-10-09 お昼 追記
ログイン画面(login.php)でユーザ名とパスワードを入力
↓
a.phpへ遷移し、DBからデータを取得し、セッション変数に入れてb.phpで扱えるようにする
↓
b.phpへ遷移し、取得したデータを表示
という一連の流れを実装しようとしました。3つのファイルのソースは下記のものです!
login.php
1<?php 2 $randomBytesDigit = 32; 3 $csrfToken = bin2hex(random_bytes($randomBytesDigit)); 4 5 session_start(); 6 $_SESSION['csrf_token'] = $csrfToken; 7?> 8<!DOCTYPE html> 9<html lang="ja"> 10<head> 11 <!-- 略 --> 12</head> 13<body> 14 <form action="./a.php" method="POST"> 15 ユーザ名:<input type="text" name="username"><br> 16 パスワード:<input type="password" name="password"><br> 17 <input type="hidden" name="csrf_token" value="<?=$csrfToken?>"> 18 送信:<input type="submit"> 19 </form> 20</body> 21</html>
a.php
1<?php 2 session_start(); 3 if(array_key_exists("csrf_token", $_SESSION)){ 4 if($_SESSION['csrf_token'] !== $_POST['csrf_token']){ 5 //トークンが一致しないためlogin.php以外から遷移してきたものと断定し、ログイン画面へ強制遷移 6 header("Location: ./login.php"); 7 exit; 8 } 9 }else{ 10 //セッション変数にCSRFトークンが入っていなかった時点で不正リクエストと断定し、ログイン画面へ強制遷移 11 header("Location: ./login.php"); 12 exit; 13 } 14 15 define("USER", "user"); 16 define("PASSWORD", "pass"); 17 18 if(isset($_POST['username']) && isset($_POST['password'])){ 19 if($_POST['username'] !== USER || $_POST['password'] !== PASSWORD){ 20 echo 'ユーザ名かパスワードが、ちがいます!<br>'; 21 echo '<a href="./login.php">ログイン画面</a>'; 22 exit; 23 } 24 } 25 26 $dsn = 'mysql:host=test;dbname=test_db;charset=utf8'; 27 $db_user = 'root'; 28 $db_pass = 'pass'; 29 $driver_options = [ 30 //オプション使用の際は追記 31 ]; 32 33 try { 34 $pdo = new PDO($dsn, $db_user, $db_pass, $driver_options); 35 } catch (PDOException $e) { 36 exit('DB接続失敗:' . $e->getMessage()); 37 } 38 39 $sql = 'SELECT id FROM test'; //「test」というテーブルからid一覧を取得 40 $stmt = $pdo->query($sql); 41 42 if(!$stmt){ 43 echo 'DBからのデータ取得にしっぱいしました・・・'; 44 exit; 45 } 46 47 $_SESSION['data'] = $stmt->fetchAll(PDO::FETCH_ASSOC); //「test」テーブルから取得したデータをb.phpへ渡すためセッション変数へ格納 48 header("Location: ./b.php");
b.php
1<?php 2 session_start(); 3 4 //DBから取得されているはずのデータを受け取っていない場合、ログイン画面へ戻る 5 if(!isset($_SESSION['data'])){ 6 header('Location: ./login.php'); 7 exit; 8 }else{ 9 echo '<pre>'; 10 var_dump($_SESSION['data']); 11 echo '</pre>'; 12 } 13 echo "a.phpで取得したDBのデータが正常に表示されました!";
気になったのはb.phpについてです。「$_SESSION['data']」には、a.phpにてDBから取得したデータが入っているはずなので、その内容を画面に表示する。そのような役割をb.phpは担っています。
そのため、もし「$_SESSION['data']」にデータがはいっていないということがもしあれば、そのときは異常事態なので、とりあえずログイン画面に強制遷移するようにしています。
しかし、a.php以外のページでもしも「$_SESSION['data']」にデータを入れた状態でb.phpに遷移された場合、意図せぬ形で画面表示が行われると考え、当質問のタイトルに「a.phpからb.phpに遷移したとき、a.phpから遷移したことを保証する手段」という内容を設定しました。
ただ、もし外部ページから「$_SESSION['data']」が渡されてそれがb.phpで表示されたとしても、特に実害が発生することはなさそう、という思いもあります!
したがいまして、そのような特例まではめんどうをみなくてもよいかもしれない、とも考えております。
上記内容を踏まえご意見頂戴できますと幸いです、よろしくお願いいたします!
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/10/05 13:06