31
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

php+MySQLでセッションを使用したログイン認証

Last updated at Posted at 2016-04-13

参考サイト

いつも参考にさせていただいてます。

機能

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');
31
41
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?