参考サイト
いつも参考にさせていただいてます。
機能
functions.php
<?php
/*
* ログイン状態によってリダイレクト
* 初回時または失敗時にはヘッダを送信してexitする
*/
function require_unlogined_session () {
// セッション開始
@session_start();
// ログインしていれば
if (isset($_SESSION["username"])) {
header('Location: ./index.php');
exit;
}
}
function require_logined_session() {
// セッション開始
@session_start();
// ログインしていなければlogin.phpに遷移
if (!isset($_SESSION["username"])) {
header('Location: ./login.php');
exit;
}
}
// CSRFトークンの生成
function generate_token() {
// セッションIDからハッシュを生成
return hash ( 'sha256', session_id() );
}
// CSRFトークン
function validate_token ($token) {
return $token === generate_token();
}
// htmlspecialchars
function h ($var) {
if (is_array($var)){
return array_map(h, $var);
} else {
return htmlspecialchars($var, ENT_QUOTES, 'UTF-8');
}
}
ログイン画面
login.php
<?php
require_once __DIR__ . '/functions.php';
require_unlogined_session();
foreach (['username','password','token','submit'] as $key) {
$$key = (string)filter_input(INPUT_POST, $key);
}
// エラーを格納する配列を初期化
$errors = [];
// POSTのときのみ実行
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ( $username === "" || $password ==="" ) {
$errors[] = 'ユーザ名またはパスワードが入力されていません。';
} else {
$username = h($username);
$password = h($password);
$dbtype = 'mysql';
$host = 'localhost';
$db = 'dbname';
$charset = 'utf8';
$dsn = "$dbtype:host=$host; dbname=$db; charset=$charset";
$db = new PDO ( $dsn, 'user', 'pass' );
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = 'SELECT * FROM user_kaiin WHERE email = ?';
$prepare = $db->prepare($sql);
$prepare->bindValue(1, $username, PDO::PARAM_INT);
$prepare->execute();
$result = $prepare->fetch(PDO::FETCH_ASSOC);
if (validate_token(filter_input(INPUT_POST, 'token')) && password_verify( $password, $result["password"] )) {
// 認証が成功
// セッションIDの追跡を防ぐ
session_regenerate_id(true);
//ユーザ名をセット
$_SESSION['username'] = $username;
// ログイン後に/に遷移
header ('Location: ./index.php');
exit;
}
// 認証が失敗
$errors[] = "ユーザ名またはパスワードが違います";
}
}
header ('Content-Type: text/html; charset=UTF-8');
?>
<!DOCTYPE html>
<html>
<head>
<title>ログインページ</title>
</head>
<body>
<h1>ログインしてください</h1>
<?php if ($errors): ?>
<ul>
<?php foreach ($errors as $err): ?>
<li><?=h($err)?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<form method="post" action="">
<p>ユーザ名: <input type="text" name="username" value="<?php echo $username = isset($_POST['username']) ? $_POST['username']: ''; ?>"></p>
<p>パスワード: <input type="password" name="password" value=""></p>
<!-- トークン -->
<input type="hidden" name="token" value="<?=h(generate_token())?>"> <!--<input type="hidden" name="token" value="<?php echo password_hash('1111', PASSWORD_DEFAULT, array('cost', 10)) ?>">-->
<p><input type="submit" name="submit" value="ログイン"></p>
</form>
</body>
</html>
ログイン中に閲覧可能な画面
index.php
<?php
require_once __DIR__ . '/functions.php';
require_logined_session();
header ('Content-Type: text/html; charset=UTF-8');
?>
<!DOCTYPE html>
<html>
<head>
<title>会員ページ</title>
</head>
<body>
<h1><?=h($_SESSION["username"])?>としてログインしています</h1>
<p><a href="./logout.php?token=<?=h(generate_token())?>">ログアウト</a></p>
</body>
</html>
ログアウト画面
logout.php
<?php
require_once __DIR__ . '/functions.php';
require_logined_session();
// CSRFトークンを検証
if ( !validate_token(filter_input(INPUT_GET, 'token')) ) {
// 400 Bad Request
header ( 'Content-type: text/plain; charset=UTF-8', true, 400 );
exit('トークンが無効です');
}
// セッション用Cookieの破棄
setcookie(session_name(), '', 1);
// セッションファイルの破棄
session_destroy();
// ログアウト完了後に/login.phpに遷移
header ('Location: ./login.php');