TL;DR
Never store passwords directly in your database. Use strong hashing algorithms like bcrypt or Argon2id with salts to protect them. Always validate user input and use prepared statements to prevent SQL injection.
1. Why Not Store Passwords Directly?
Storing passwords plainly is a huge security risk. If your database gets hacked, all your users’ passwords are compromised. Hashing makes it much harder for attackers to get usable passwords even if they access the database.
2. Hashing Algorithms: bcrypt vs Argon2id
Both bcrypt and Argon2id are good choices, but Argon2id is generally considered more secure against modern attacks (like GPU cracking). bcrypt is still widely used and a significant improvement over storing passwords in plain text.
- bcrypt: A well-established algorithm. Relatively slow, which makes brute-force attacks harder.
- Argon2id: More memory intensive than bcrypt, making it even more resistant to cracking attempts. Requires PHP 7.3 or later.
3. Using bcrypt with PHP
PHP provides the password_hash() and password_verify() functions for easy bcrypt integration.
- Hashing a Password: Use this when a user registers or changes their password.
- Verifying a Password: Use this when a user logs in.
4. Using Argon2id with PHP
Argon2id requires the password_hash() function with the PASSWORD_ARGON2ID constant.
- Hashing a Password:
You can also specify options like memory cost, time cost and parallelism. See the PHP documentation for details: https://www.php.net/manual/en/password.hash.php
5. Salting (Automatic with Modern Functions)
Salts are random strings added to passwords before hashing. They make rainbow table attacks much harder. password_hash() automatically generates and stores a unique salt for each password, so you don’t need to manage salts manually.
6. Input Validation
Always validate user input to prevent unexpected data from being hashed or stored in your database.
- Length: Ensure passwords meet minimum length requirements (e.g., 8 characters).
- Characters: Consider requiring a mix of uppercase, lowercase, numbers and symbols.
- Sanitization: Remove potentially harmful characters.
7. Prepared Statements
Use prepared statements with your MySQL queries to prevent SQL injection attacks.
prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$_POST['username']]);
$user = $stmt->fetch();
?>
Important: Never directly embed user input into your SQL queries.
8. Database Security
- Least Privilege: Grant only the necessary permissions to your database user.
- Regular Backups: Back up your database regularly in case of a security breach or data loss.

