When dealing with user authentication, the absolute golden rule is: never store passwords in plain text. Imagine a data breach where your database is compromised. If passwords are unhashed, every user's account is immediately vulnerable, not just on your platform but potentially on others if they reuse passwords. Password hashing is a one-way cryptographic function that transforms a plain text password into an unreadable, fixed-length string of characters (the hash). This process is irreversible, meaning you cannot reconstruct the original password from its hash. This way, even if an attacker gains access to your stored hashes, they can't directly log in as your users or discover their actual passwords.
While many hashing algorithms exist, not all are suitable for passwords. Fast algorithms like MD5 or SHA-256 are designed for data integrity checks, not password security, as they can be quickly brute-forced or exploited with rainbow tables. For passwords, we need a slow, adaptive algorithm like bcrypt. Bcrypt incorporates two crucial features: salting and a work factor. A unique, random "salt" is generated and combined with each password before hashing, ensuring that even identical passwords result in different hashes. This protects against rainbow table attacks. The "work factor" deliberately makes the hashing process computationally intensive, slowing down brute-force attempts significantly. As hardware improves, you can increase the work factor, making your system adaptive to future threats.
Once a password is hashed using bcrypt, you store only this generated hash string (which typically includes the salt and work factor) in your database. When a user attempts to log in, you take their provided password, apply the exact same hashing process (using the stored salt and work factor embedded in the hash string), and then compare the newly generated hash with the one stored in your database. If they match, authentication succeeds. You never decrypt or try to reverse the hash. For secure storage beyond the database, ensure your database connection strings and other secrets are managed via environment variables, and that your database server itself is properly secured, limiting access and applying regular security patches.
Key Takeaways
- Never store plain text passwords; always hash them.
- Use a strong, deliberately slow, and adaptive hashing algorithm like bcrypt.
- Ensure each password hash uses a unique, random salt to prevent rainbow table attacks.
- Store only the password hash (containing the salt and cost factor) in your database.
- Authenticate by hashing the provided password and comparing the resulting hash to the stored hash.
Code Example
How this code works
This code provides the essential functions for securely handling user passwords in an application. Its main job is to transform a plain-text password into an unreadable, irreversible hash for storage, and then to safely check if a user-provided password matches that stored hash. The bcrypt library is imported to perform these cryptographic operations. A crucial element is saltRounds, which defines the "cost factor" for hashing. A higher number like 10 makes the hashing process intentionally slower, which makes it much harder for attackers to guess passwords quickly, improving security at the cost of a tiny bit more processing time.
The hashPassword function takes a user's plain password and uses bcrypt.hash with the specified saltRounds to generate a unique, secure hash. This hash is what gets saved in the database, never the original password. Because hashing is a one-way process, the original password cannot be recovered from the hash. When a user tries to log in, the verifyPassword function comes into play. It takes the password the user just typed and the storedHash from the database. Crucially, bcrypt.compare then securely re-hashes the provided password using the salt and cost factor embedded within the storedHash, and compares this new hash to the storedHash. It returns true if they match, signifying a correct password, or false otherwise, all without ever revealing or processing the original plain password directly.