Passwords are the keys to your users’ accounts. A short list of rules, with links to the lessons that explain each.
Storing passwords
- Hash with
password_hash()andPASSWORD_DEFAULT. See password_hash(). - Verify with
password_verify(), and upgrade withpassword_needs_rehash(). See password_verify(). - Never use
md5(),sha1(),sha256()or a home-made scheme. - Never store, log, email or display a plain password.
<?php
function storePassword(#[\SensitiveParameter] string $password): string
{
return password_hash($password, PASSWORD_DEFAULT);
}
$hash = storePassword("correct horse battery staple");
echo str_starts_with($hash, '$2y$') ? "hashed" : "not hashed";
hashed
The #[\SensitiveParameter] attribute (PHP 8.2+) tells PHP to hide this argument in stack traces, so it does not appear in logs if an exception happens.
Choosing a policy
- Minimum length of 8 to 12 characters or more.
- Allow all characters and long passphrases.
- No arbitrary composition rules, no forced regular changes.
- Reject passwords that appear in lists of known leaked passwords.
- Show a strength hint, but do not rely on it.
<?php
function passwordProblems(string $password, string $email): array
{
$problems = [];
if (mb_strlen($password) < 10) {
$problems[] = "Use at least 10 characters.";
}
if (strtolower($password) === strtolower($email) || str_contains(strtolower($password), "password")) {
$problems[] = "Do not use your email or the word \"password\".";
}
return $problems;
}
print_r(passwordProblems("password123", "isha@example.com"));
Array
(
[0] => Do not use your email or the word "password".
)
In the form
<input type="password" autocomplete="new-password">when creating one,autocomplete="current-password"when logging in.- Do not block paste. Password managers rely on it.
- Do not put the password back in the field after an error.
Transport
- HTTPS only, for the form and the whole site.
- Use
POST, never put passwords in a URL.
Reset and change
- Reset tokens are random, hashed in the database, short-lived and used once. See Password Reset.
- Ask for the current password when changing the password or email.
- After a change: end other sessions, delete remember-me tokens and email the user.
Protect the login form
- Rate limit attempts, and slow down repeated failures.
- One generic error message.
- Offer two-factor authentication.
Never email a password
If you can email a user their password, you stored it in plain text or reversibly. A forgotten password is always reset, never recovered.
Admin accounts
- Unique, long passwords for hosting, database and admin panels, stored in a password manager.
- Two-factor authentication on hosting, Git and email accounts.