csrf対策が機能しない
解決したいこと
画面から送信されたhiddenタグのトークンと、セッションに保存されたトークンを一致
させたい。
<状況>
mampを用いたローカル環境では、問題なくログインできていたウェブ日報登録システム
ですが、レンタルサーバーを借りて、ネットに公開すると、ログインできなくなってしま
いました。
具体的には、正しいログイン情報(id及びpassword)を入力し、システムにログインした
際に、CSRF対策として、hiddenタグのトークンとセッションに保存されたトークンが一致
しているか確認しています。
その際に、一致している場合は、業務リスト画面へ画面遷移、それ以外は、エラー表示を
するようにプログラムしていました。
その結果、正しくログインされなかった場合はerror.phpに遷移するようにプログラム設定
していたので、正しいログイン情報を入力しているにも関わらず、正しくログインされずに、
login.phpからerror.phpに遷移してしまいます。
本来、ローカル環境で実行した時は、login.php→list.phpに画面遷移しておりました。
<環境>
・レンタルサーバー(お名前ドットコムを使用)
・phpのバージョンは7.4
解決方法を教えていただければ幸いです。
該当するソースコード
【login.php】
<?php
require_once(dirname(__FILE__) . '/config/config.php');
require_once(dirname(__FILE__) . '/functions.php');
try {
session_start();
if (isset($_SESSION['USER'])) {
//ログイン済みの場合はHOME画面へ
redirect('./list.php');
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
//POST処理時
check_token();
//1.入力値を取得
$user_id = $_POST['user_id'];
$password = $_POST['password'];
//2.バリデーションチェック
$err = array();
if (!$user_id) {
$err['user_id'] = '社員IDを入力して下さい。';
} elseif (!preg_match('/^[0-9]+$/', $user_id)) {
$err['user_id'] = '社員IDを正しく入力して下さい。';
} elseif (mb_strlen($user_id, 'utf-8') > 20) {
$err['user_id'] = '社員IDが長すぎます。';
}
if (!$password) {
$err['password'] = 'パスワードを入力して下さい。';
}
if (empty($err)) {
//3.データベースに照合
$pdo = connect_db();
$sql = "SELECT * FROM user WHERE user_id = :user_id LIMIT 1";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':user_id', $user_id, PDO::PARAM_STR);
$stmt->execute();
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
//4.ログイン処理(セッションに保存)
$_SESSION['USER'] = $user;
//5.HOME画面へ遷移
redirect('./list.php');
} else {
$err['password'] = '認証に失敗しました。';
}
}
} else {
//画面初回アクセス時
$user_id = "";
$password = "";
set_token();
}
$page_title = '日報登録システム';
} catch (Exception $e) {
//エラー時の処理
redirect('./error.php');
}
?>
<!doctype html>
<html lang="ja">
<?php include(dirname(__FILE__) . '/templates/head_tag.php'); ?>
<body class="text-center bg-white">
<h1>Web日報登録</h1>
<form class="border rounded form-login" method="post">
<h2 class="h3 my-3">Login</h2>
<div class="form-group pt-3">
<input type="text" class="form-control rounded-pill<?php if (isset($err['user_id'])) echo ' is-invalid'; ?>"
name="user_id" value="<?= $user_id ?>" placeholder="社員ID" required>
<div class="invalid-feedback"><?= $err['user_id'] ?></div>
</div>
<div class="form-group pt-3">
<input type="password" class="form-control rounded-pill<?php if (isset($err['password'])) echo ' is-invalid'; ?>"
name="password" placeholder="password">
<div class="invalid-feedback"><?= $err['password'] ?></div>
</div>
<button type="submit" class="btn btn-primary rounded-pill mt-3">ログイン</button>
<input type="hidden" name="CSRF_TOKEN" value="<?= $_SESSION['CSRF_TOKEN'] ?>">
</form>
【function.php】
<?php
//データベースに接続する
function connect_db(){
$param = '省略';
$pdo = new PDO(省略);
$pdo->query('SET NAMES utf8;');
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
return $pdo;
}
//日付を日(曜日)の形式に変換する
function time_format_dw($date){
$format_date = NULL;
$week = array('日','月','火','水','木','金','土');
if($date){
$format_date = date('j('.$week[date('w',strtotime($date))].')',strtotime($date));
}
return $format_date;
}
// 時間のデータ形式を調整する
function format_time($value) {
if (!$value || $value == '00:00:00') {
return NULL;
} else {
return date('H:i', strtotime($value));
}
}
//HTMLエスケープ処理(xssクロスサイド・スクリプティング対策)
function h($original_str){
return htmlspecialchars($original_str, ENT_QUOTES, 'UTF-8');
}
//トークンを発行する処理
function set_token(){
$token = sha1(uniqid(mt_rand(), true));
$_SESSION['CSRF_TOKEN'] = $token;
}
//トークンをチェックする処理
function check_token(){
if(empty($_SESSION['CSRF_TOKEN'])||($_SESSION['CSRF_TOKEN']!= $_POST['CSRF_TOKEN'])){
unset($pdo);
header('Location:./error.php');
exit;
}
}
//時間の形式チェックを行う
function check_time_format($time){
if (preg_match('/^([01]?[0-9]|2[0-3]):([0-5][0-9])$/', $time)) {
return true;
}else{
return false;
}
}
//指定されたPHPへリダイレクトする
function redirect($path){
unset($pdo);
header("Location:$path");
exit;
}
?>
自分で試したこと
・バリデーションチェックまではできているので、ログインボタンを押した後の動きで不具合が起きていると考え、セッションとトークンが一致していないと考えました。
0