ID/passwordによる認証 > passwordのhash化
目次ページ https://zenn.dev/gallu/articles/05335420b8e585
前提
前回の https://zenn.dev/articles/d32e47a44ed8a5 はあまりにも洒落にならないので、最低限「パスワードを平文ではない状態で保存する」仕組みです。
現状のPHPだと、パスワードは「password_hash() / password_verify() 関数を使う」のがよいと思われるので、そのように実装をしていきます。
解説
比較的簡単な「IDとパスワードによる認証」で、最低限の処理だけに絞っています。
パスワードの保存の仕方(のうち、推奨されているもの)はいくつかありますが、PHPにおいては「password_hash()を使う」のが手っ取り早いので、PASSWORD_DEFAULT でhash化しておきましょう(本記事を書いているタイミングでは、bcryptが使われていると思われます)。
他にも色々と「追加したい処理」はあるのですが、最低限「パスワードは、適切なアルゴリズムのhashでhash化する(md5でhashとかしない)」という所を踏まえておきましょう。
「ログインIDがそもそも存在しない」場合は、認証NGなので、先頭でとっとと判定をしています。
パスワードの比較は「hash化されたパスワードと生パスワード(入力値)との比較」になるため、password_verify() 関数を使って比較を行うようにします。
想定するテーブルレイアウト
CREATE TABLE `ログインアカウント` (
`login_id` varbinary(256) NOT NULL COMMENT 'ログインID',
`password` varbinary(256) NOT NULL COMMENT 'パスワード(password_hash()使用)',
PRIMARY KEY (`login_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='1レコードが「(1ユーザの)ログイン情報」なテーブル'
実装
入力は一端、 $_POST['login_id']
と $_POST['password']
で取得可能である、とします。
また、DBハンドルは $dbh
にすでに(PDOインスタンスが)入っているもの、とします。
また、「IDが違うのかpasswordが違うのか」がわかるのはあまり好ましくないので、「処理を簡単に関数化」して、戻り値をbooleanにしてエラー処理をします。
関数内の $dbh
は「なんらかの方法で取得しているもの」としてください。
実装例
class Authentication
{
/**
* 認証処理本体
*
* @param string $login_id ログインID
* @param string $password ログインパスワード
* @return array|null 認証の可否(arrayなら認証成功、nullなら認証失敗)
*/
public static function login(string $login_id, string $password) : ?array
{
// ごく最低限のvalidate
if ( ('' === $id)||('' === $password) ) {
// ログイン失敗
return null;
}
// プリペアドステートメントの作成
$pre = $dbh->prepare('SELECT * FROM ログインアカウント WHERE login_id=:login_id;');
// 値のバインド
$pre->bindValue(':login_id', $login_id);
// SQLの実行
$r = $pre->execute();
// レコードの取得
$account = $pre->fetch( \PDO::FETCH_ASSOC );
// レコードが空なら
if (false === $account) {
// ログイン失敗
return null;
}
// パスワードを比較
if (false === password_verify($password, $account['password'])) {
// ログイン失敗
return null;
}
// ログイン成功
return $account;
}
}
// ログイン
$login_account = Authentication::login(strval($_POST['login_id'] ?? ''), strval($_POST['password'] ?? ''));
if (null === $login_account) {
// ログイン失敗
echo 'NG';
exit;
}
// XXX 以下、認可処理に続く
Discussion