DEV3 Technologies home

DEV3 Developer Forum

How do I store and verify passwords correctly in plain PHP?

PHP helpSolved0 views
dev3 ·

I currently store md5($password) in my users table. I read that is bad. What is the correct way in PHP 8 to hash passwords, verify them on login and upgrade old hashes?

#php#security#password

0

1 reply

dev3 · Accepted answer

Never use md5, sha1 or plain hash('sha256') for passwords. They are fast, so attackers can test billions of guesses per second. Use PHP's built-in password API, which handles salting and the work factor for you.

Hashing at registration

$hash = password_hash($password, PASSWORD_DEFAULT);   // currently bcrypt; may change over time
// store $hash in a VARCHAR(255) column

PASSWORD_DEFAULT can change to stronger algorithms in future PHP versions, which is why the column must be at least 255 chars. You may use PASSWORD_ARGON2ID if your PHP build supports it.

Verifying at login

$stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email = ?');
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);

if ($user && password_verify($password, $user['password_hash'])) {
    if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT)) {
        $new = password_hash($password, PASSWORD_DEFAULT);
        $pdo->prepare('UPDATE users SET password_hash = ? WHERE id = ?')->execute([$new, $user['id']]);
    }
    session_regenerate_id(true);
    $_SESSION['user_id'] = $user['id'];
}

password_verify is timing-safe and reads the algorithm and salt from the stored hash.

Migrating old MD5 hashes

Option 1: on each successful login, check the legacy hash, then re-hash with password_hash and overwrite. Option 2 (immediately): wrap the old hash: store password_hash(md5_hash, ...) and verify password_verify(md5($input), $stored), then upgrade to a pure hash on the next login.

Pitfalls

  • Do not truncate or trim passwords. bcrypt only uses the first 72 bytes, so very long passphrases are cut; pre-hashing is rarely needed with Argon2.
  • Do not write your own salt, the salt option was removed in PHP 8.
  • Return the same error for unknown user and wrong password, and rate limit attempts.
  • Use random_bytes() for reset tokens, store only a hash of the token.

You can try hashes with the bcrypt generator and checker.

0

Your reply

Every post is reviewed by DEV3 before it appears. Usually reviewed within 24 hours.

Markdown supported. Use ``` for code blocks. Images are not allowed.