##はじめに
自分でPHPを使ってログイン機能を作ったので、一連の流れをまとめました。
間違えていることなどがあれば、教えてくださるとありがたいです。
PHPのバージョンは7.3を使用しています。
password_hash関数とpassword_verify関数を使ってパスワードのハッシュ化とパスワードの認証をやっていきます。
##そもそもハッシュ化とは
ユーザーのパスワードなど、大切な情報をデータベースに格納する時に、元データをハッシュ関数と呼ばれるものを利用して変換することです。
##ハッシュ関数の特徴
以下の2つが大きな特徴です。
● 元データが同じならば変換後は必ず同じ値になる
● 変換後の値から元データの値を復元することが不可能である
##password_hash関数の書き方
$password = password_hash([ユーザーが入力したpassword], PASSWORD_DEFAULT);
自分はこのように書きました。$password
この変数をDBに格納することで、ハッシュ化されたパスワードを保存することが可能です。ちなみに変数名は何でも大丈夫です。
##注意点
デフォルトのアルゴリズムを使ってパスワードをハッシュするので、現時点でのデフォルトは
BCRYPT で、その結果は 60 文字になります。
デフォルトは、今後変わる可能性があることに注意しましょう。結果が
60文字以上になっても対応できるようにしておきましょう (255 あたりが適切です)
と公式のページに書いていました。 詳しくは、公式ページを見てください。password_hash
##DB設定
DBにuserの情報を追加するテーブルを作成します。カラムはシンプルに名前とパスワードのみにしました。
user_name | password |
---|---|
- | - |
##コード例
<?php
if(isset($_POST['user_name'])){
$user_name = $_POST['user_name'];
}
if(isset($_POST['user_password'])){
$user_password = password_hash($_POST['user_password'], PASSWORD_DEFAULT);
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>登録画面</title>
</head>
<body>
<!-- ユーザーネーム -->
<form method = "post">
<p class="name">ユーザーネーム:
<input type="text" name="user_name"></p>
<!-- パスワード -->
<p class="password">パスワード:
<input type="password" name="user_password"></p>
<input type="submit" value="登録する">
</form>
</body>
</html>
あとはデータベース接続して、それぞれの変数をテーブルに保存することでハッシュ化は完了です。
##テーブルの中身
user_name | password |
---|---|
sample | 0$8U6sqRKNF5iYMJ.8zSQ8GOsk2efw2ZFWWKRtDSMGIspTaBOOYcn |
user_nameはそのままで、passwordのみランダムな文字列に変換されています。
これで、登録は完了です
##ログイン認証の手順
登録された、データを使ってログインの仕組みを作ります。
<form method = "post">
<p class="name">ユーザーネーム:
<input type="text" name="user_name"></p>
<p class="password">パスワード:
<input type="password" name="user_password"></p>
<input type="submit" value="ログインする">
</form>
登録と同じように、ユーザーネームと、パスワードを入力できるフォームを用意します。
認証では、password_verify関数を使います。
##password_verify関数の使い方
password_verify('入力されたパスワード', 'DBに保存したhash化したパスワード')
このように引数が二つ必要になります。
##コード例
<?php
if(isset($_POST['user_name'])){
$user_name = $_POST['user_name'];
}
if(isset($_POST['user_password'])){
$user_password = password_hash($_POST['user_password'], PASSWORD_DEFAULT);
}
try {
// データベースに接続
$dbh = new PDO($dsn, $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4'));
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$sql = 'SELECT * FROM users WHERE user_name = :user_name';
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':user_name' => $user_name));
$user = $stmt->fetch(PDO::FETCH_ASSOC);
}catch(PDOException $e){
echo '接続できませんでした。理由:'.$e->getMessage();
}
if($user === false || password_verify($user_password, $user['password']) === false){
echo 'ログインできませんでした';
}
else{
echo 'ログイン成功';
}
?>
SELECT文が失敗したり、passwordが間違っていると、ログインできないような条件をif文で書きました。
##最後に
これで、完全に安全というわけではなく、辞書攻撃など弱点もあるので、上記のコードでは、ほとんどバリデーションをしていませんが、なるべく辞書にないような、文字の組み合わせをユーザーに登録させるよう、バリデーションをしていくことが必要だと思います。