PHPでログイン機能を実装する際、パスワードの照合処理は避けて通れない重要な要素です。この記事では、PHPのpassword_verify関数について、初心者の方にも分かりやすく詳しく解説していきます。
password_verify関数とは?
password_verifyは、PHPでハッシュ化されたパスワードと平文のパスワードを安全に比較するための関数です。PHP 5.5.0以降で利用可能で、セキュアなパスワード認証システムを構築する上で欠かせない関数となっています。
基本的な構文
bool password_verify(string $password, string $hash)
パラメータ:
$password: ユーザーが入力した平文のパスワード$hash: データベースなどに保存されているハッシュ化されたパスワード
戻り値:
- パスワードが一致する場合は
true - 一致しない場合は
false
なぜpassword_verify関数が必要なのか?
パスワードを直接比較するのではなく、password_verifyを使う理由は主に3つあります。
1. セキュリティの確保
パスワードはデータベースにハッシュ化して保存するのが基本です。ハッシュ化されたパスワードは元に戻せないため、データベースが漏洩しても実際のパスワードは守られます。
2. ソルトの自動処理
password_hash関数で生成されたハッシュには、ソルト(ランダムな文字列)が含まれています。password_verifyはこのソルトを自動的に抽出して検証を行うため、開発者が手動でソルトを管理する必要がありません。
3. タイミング攻撃への対策
単純な文字列比較では、比較処理にかかる時間からパスワードの一部を推測される可能性があります。password_verifyはこのようなタイミング攻撃に対して安全な比較を行います。
実際の使用例
基本的な使い方
<?php
// ユーザー登録時:パスワードをハッシュ化してデータベースに保存
$password = "MySecurePassword123";
$hash = password_hash($password, PASSWORD_DEFAULT);
// $hashをデータベースに保存
// ログイン時:入力されたパスワードを検証
$inputPassword = $_POST['password']; // ユーザーが入力したパスワード
$storedHash = "データベースから取得したハッシュ"; // 実際にはDBから取得
if (password_verify($inputPassword, $storedHash)) {
echo "ログイン成功!";
// セッションの開始などの処理
} else {
echo "パスワードが間違っています";
}
?>
実践的なログイン処理の例
<?php
session_start();
// データベース接続(PDOを使用)
try {
$pdo = new PDO('mysql:host=localhost;dbname=myapp', 'username', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("接続エラー: " . $e->getMessage());
}
// ログイン処理
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';
// ユーザー情報を取得
$stmt = $pdo->prepare("SELECT id, email, password_hash FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password_hash'])) {
// パスワードが正しい場合
$_SESSION['user_id'] = $user['id'];
$_SESSION['email'] = $user['email'];
// パスワードハッシュの再ハッシュ化が必要かチェック
if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT)) {
$newHash = password_hash($password, PASSWORD_DEFAULT);
$updateStmt = $pdo->prepare("UPDATE users SET password_hash = ? WHERE id = ?");
$updateStmt->execute([$newHash, $user['id']]);
}
header('Location: dashboard.php');
exit;
} else {
$error = "メールアドレスまたはパスワードが正しくありません";
}
}
?>
よくある間違いと注意点
間違い1: ハッシュを直接比較してしまう
// ❌ 悪い例
if ($inputPassword === $storedHash) {
// これは絶対に動作しません
}
// ✅ 正しい例
if (password_verify($inputPassword, $storedHash)) {
// 正しい検証方法
}
間違い2: ハッシュ化せずにパスワードを保存
// ❌ 絶対にやってはいけない
$stmt = $pdo->prepare("INSERT INTO users (email, password) VALUES (?, ?)");
$stmt->execute([$email, $password]); // 平文で保存
// ✅ 必ずハッシュ化してから保存
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO users (email, password_hash) VALUES (?, ?)");
$stmt->execute([$email, $hash]);
間違い3: エラーメッセージで情報を漏らす
// ❌ セキュリティリスクあり
if (!$user) {
echo "このメールアドレスは登録されていません";
} elseif (!password_verify($password, $user['password_hash'])) {
echo "パスワードが間違っています";
}
// ✅ 情報を明かさない
if (!$user || !password_verify($password, $user['password_hash'])) {
echo "メールアドレスまたはパスワードが正しくありません";
}
password_needs_rehashとの組み合わせ
セキュリティ標準は時間とともに変化します。password_needs_rehash関数を使うことで、古いハッシュアルゴリズムで作成されたパスワードを自動的に更新できます。
if (password_verify($password, $hash)) {
// ログイン成功
// ハッシュの更新が必要かチェック
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
// 新しいハッシュを生成
$newHash = password_hash($password, PASSWORD_DEFAULT);
// データベースを更新
$stmt = $pdo->prepare("UPDATE users SET password_hash = ? WHERE id = ?");
$stmt->execute([$newHash, $userId]);
}
}
パフォーマンスの考慮事項
password_verifyは意図的に処理に時間がかかるように設計されています。これはブルートフォース攻撃(総当たり攻撃)を困難にするためです。通常、1回の検証には0.1〜0.3秒程度かかります。
これは正常な動作なので、心配する必要はありません。むしろ、このコストがセキュリティを高めています。
まとめ
password_verify関数は、PHPで安全なパスワード認証を実装するための必須ツールです。以下のポイントを押さえておきましょう。
- パスワードは必ず
password_hashでハッシュ化して保存する - ログイン時の検証には必ず
password_verifyを使用する - 平文のパスワードを直接比較しない
password_needs_rehashでハッシュを定期的に更新する- エラーメッセージで不要な情報を漏らさない
これらの基本を守ることで、ユーザーのパスワードを安全に管理できるシステムを構築できます。セキュリティは妥協してはいけない重要な要素です。正しい方法でパスワード認証を実装しましょう!
