PHP Tips | 文字列処理:ランダム・生成 – パスワード用ハッシュ生成

PHP PHP
スポンサーリンク

まず、「パスワード用ハッシュ生成」で何をしたいのか

パスワード用ハッシュ生成の目的は一言でいうと、

「パスワードそのものは絶対に保存せず、“検証にだけ使える形”で安全に保存する」

ことです。

ユーザーが入力したパスワードをそのまま DB に保存してしまうと、
もし DB が漏洩したときに、全ユーザーのパスワードが丸裸になります。

そこでやるのが「ハッシュ化」です。
ただし、パスワードだけは「普通のハッシュ(SHA-256 など)」では不十分で、
専用の仕組みを使う必要があります。

PHP では、そのための関数がすでに用意されています。

password_hash()
password_verify()
PHP

この 2 つを正しく使えるようになることが、「パスワード用ハッシュ生成」のゴールです。


絶対に覚えてほしい結論:自前実装はしない、password_hash 一択

なぜ SHA-256 などで自前実装してはいけないのか

初心者がやりがちな「危ない実装」は、こんな感じです。

// ダメな例
$hash = hash('sha256', $password);
PHP

一見それっぽいですが、パスワードに対しては弱すぎます。

理由はざっくり言うと、

同じパスワードなら同じハッシュ値になる(salt がない)。
ハッシュ計算が速すぎるので、総当たり攻撃(ブルートフォース)に弱い。

からです。

攻撃者は、よく使われるパスワードのリストを用意して、
片っ端から SHA-256 を計算して照合する、ということが簡単にできます。

これを防ぐために必要なのが、

ユーザーごとのランダムな salt
何万回もハッシュを繰り返す「ストレッチング」
アルゴリズムのバージョン管理

などですが、これを自前で正しく設計するのはかなり難しいです。

だからこそ、PHP は「パスワード専用の安全な仕組み」を標準で用意しています。
それが password_hash()password_verify() です。


password_hash の基本的な使い方

保存時:生パスワード → ハッシュ文字列

ユーザーが新規登録やパスワード変更をしたとき、
生のパスワードから「保存用ハッシュ」を作ります。

$password = $_POST['password'];

// パスワード用ハッシュを生成
$hash = password_hash($password, PASSWORD_DEFAULT);

// これを DB に保存する
$stmt = $pdo->prepare('INSERT INTO users (email, password_hash) VALUES (:email, :hash)');
$stmt->execute([
    ':email' => $email,
    ':hash'  => $hash,
]);
PHP

ここでの重要ポイントは、

自分で salt を作らなくていい
自分でストレッチング回数を決めなくていい
アルゴリズム(bcrypt, Argon2 など)は PASSWORD_DEFAULT に任せればよい

ということです。

password_hash() の戻り値は、こんな感じの文字列です。

$2y$10$9uQW...(中略)...pX9pQv0pZbTq7f7q
PHP

この中に、

どのアルゴリズムを使ったか
salt
ストレッチングのコスト

などの情報が全部埋め込まれています。
なので、「この文字列さえ DB に保存しておけば、あとで検証できる」状態になります。


password_verify の基本的な使い方

ログイン時:入力パスワードと保存済みハッシュを照合する

ユーザーがログインフォームからメールアドレスとパスワードを送ってきたとします。

やることは次の 2 ステップです。

  1. メールアドレスからユーザーを検索し、保存済みの password_hash を取得する
  2. 入力されたパスワードと、そのハッシュを password_verify() で照合する

コードで書くと、こうなります。

$email    = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';

// 1. ユーザーを検索
$stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email = :email');
$stmt->execute([':email' => $email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);

if (!$user) {
    // ユーザーがいない
    echo 'メールアドレスまたはパスワードが違います。';
    exit;
}

// 2. パスワードを検証
$hash = $user['password_hash'];

if (!password_verify($password, $hash)) {
    // パスワードが違う
    echo 'メールアドレスまたはパスワードが違います。';
    exit;
}

// ここまで来たら認証成功
echo 'ログイン成功';
PHP

ここでの重要ポイントは、

「ハッシュを再計算して文字列比較する」のではなく、
必ず password_verify() を使う

ということです。

password_verify() は、password_hash() が埋め込んだ情報(アルゴリズム・salt・コスト)を読み取って、
正しい方法で検証してくれます。


アルゴリズムの「将来の変更」にも勝手に追従できる仕組み

PASSWORD_DEFAULT を使う意味

password_hash() の第二引数には、アルゴリズムを指定します。

password_hash($password, PASSWORD_DEFAULT);
PHP

PASSWORD_DEFAULT は、「PHP がその時点で推奨するアルゴリズム」を意味します。

PHP のバージョンが上がると、
内部的に使われるアルゴリズムが変わる可能性があります(例えば bcrypt → Argon2 など)。

でも、password_verify() は「ハッシュ文字列の中に書かれたアルゴリズム情報」を見て検証するので、

古いアルゴリズムで作られたハッシュも
新しいアルゴリズムで作られたハッシュも

同じコードで検証できます。

これが、「自前実装ではなく標準関数を使う」最大のメリットの一つです。


実務でよくやる「ハッシュの再ハッシュ(アップグレード)」の考え方

password_needs_rehash で「古いハッシュ」を見つける

例えば、ある時点で「コスト(計算の重さ)を上げたい」「アルゴリズムを変えたい」となったとします。

そのときに使えるのが password_needs_rehash() です。

$options = [
    'cost' => 12, // bcrypt のコストを 12 にしたい、など
];

if (password_needs_rehash($hash, PASSWORD_DEFAULT, $options)) {
    // 新しい条件でハッシュを作り直す
    $newHash = password_hash($password, PASSWORD_DEFAULT, $options);

    // DB のハッシュを更新
}
PHP

これをログイン処理の中に組み込んでおくと、

ユーザーがログインするタイミングで、
古いハッシュを少しずつ新しい条件にアップグレードしていく

ということができます。

初心者のうちはここまでやらなくても大丈夫ですが、
「将来の変更にも対応しやすい設計になっている」という感覚だけ持っておくと、理解が深まります。


まとめ:今日からの「パスワード用ハッシュ生成」ユーティリティ

一番大事なところだけ、ぎゅっとまとめます。

パスワードは絶対に平文で保存しない。
SHA-256 などで自前のハッシュ実装をしない(salt 付き SHA-256 も含めて NG)。
PHP では、パスワードは必ず password_hash()password_verify() で扱う。

実務でまず用意しておくといい「ユーティリティ関数」は、この 2 つです。

// 保存用ハッシュを作る
function make_password_hash(string $password): string
{
    return password_hash($password, PASSWORD_DEFAULT);
}

// 入力パスワードと保存済みハッシュを検証する
function verify_password(string $password, string $hash): bool
{
    return password_verify($password, $hash);
}
PHP

使い方のイメージはこうです。

// 保存時
$hash = make_password_hash($password);

// 検証時
if (verify_password($password, $hash)) {
    // OK
}
PHP

もし、あなたのプロジェクトのどこかで「hash('sha256', $password) をそのまま DB に保存している」ようなコードを見つけたら、
そこがこのユーティリティに差し替えるべき“最優先ポイント”です。
その一箇所を置き換えるだけで、アプリ全体のパスワード周りの安全性が一段階上がります。

PHPPHP
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました