Spring Security
Password Encoding — BCrypt and Why Plain Passwords Are Dangerous
Cryptographic hashing, salt additions, and password matching processes.
Introduction
One of the most fundamental rules of software security is: never store user passwords in plain text. Passwords must be cryptographically hashed using mathematically irreversible, slow hashing algorithms before being stored in databases.
Why It Matters
If a database compromise occurs, plain text or weakly hashed passwords (e.g. MD5 or SHA-256) are easily exposed or cracked using precomputed lookup lists called rainbow tables or high-speed hardware tools. Fast hashing algorithms (like SHA-256) allow attackers to attempt billions of password combinations per second. Modern standard hashing systems, such as BCrypt, introduce a salt to negate rainbow tables and utilize a configurable cost factor to intentionally slow down execution speeds, making brute-force attacks computationally impractical.
Real-World Analogy
Think of hashing passwords like burning a document into ash. You can easily burn a specific sheet of paper to ashes, but you cannot reconstruct the original text of the paper from the resulting ash. Using a fast hash like SHA-256 is like burning paper into simple black ash, which can be quickly guessed if the input was short. Using BCrypt with a salt is like mixing the paper with a random chemical (salt) and burning it inside a concrete furnace that takes 100 milliseconds to complete. Even if a thief steals all the furnaces, trying to test every possible paper combination takes an eternity because of the chemical reaction speed.
Detailed Mechanics
1. One-Way Hashing
Unlike symmetric or asymmetric encryption, which are two-way (reversible using decryption keys), hashing is a mathematically irreversible function. Spring Security's PasswordEncoder interface manages this through two key methods:
2. Salting: Defending Against Rainbow Tables
If two users choose the identical password "P@ssword123", a basic hashing function generates the exact same hash value. Attackers exploit this by pre-calculating hashes of common passwords (rainbow tables).
Salting solves this by generating a unique random string (the salt) for every password submission, appending it to the password, and hashing the combination. The salt is stored directly inside the final output hash string, ensuring that identical passwords always result in completely different hashes.
3. Adaptive Work Factor
BCrypt implements the key derivation algorithm based on the Blowfish cipher. It includes a logarithmic work factor (cost) parameter. A cost factor of 10 means the algorithm performs 210 (1,024) hashing iterations. Increasing the cost factor by 1 doubles the calculation time. This allows developers to adjust hashing speeds over time as computer hardware becomes faster.
4. Spring Security's DelegatingPasswordEncoder
Modern Spring Security versions do not enforce a single algorithm. Instead, they use DelegatingPasswordEncoder. Password hash strings stored in the database are prefixed with their algorithm identifier in curly braces, for example:
{bcrypt}$2a$10$dXJszbOuy3P54G...(BCrypt hash){argon2}$argon2id$v=19$m=16384,t=2,p=1...(Argon2 hash){pbkdf2}987654...(PBKDF2 hash)
This dynamic lookup prefix allows Spring Boot to match passwords using multiple legacy or modern encoders simultaneously, supporting seamless background upgrades without breaking user passwords.
Practical Example
The configuration below establishes BCrypt as the standard password encoder bean and demonstrates how it is used during registration and verification flows:
Quick Quiz
Q1: How does PasswordEncoder.matches know how to verify the password if the salt is generated randomly for each encoding operation?
A) The salt is saved in a secondary database table.
B) The salt is embedded directly within the generated BCrypt hash string (the first 22 characters of the hash parameter represent the salt).
C) The salt is hardcoded inside Spring Security source files.
D) The server prompts the browser to provide the original salt.
Answer: B — BCrypt hashes incorporate the algorithm type, cost factor, and random salt values directly in the output string (e.g., $2a$10$saltvaluehashvalue).
Q2: Why shouldn't fast hashing algorithms like SHA-512 or MD5 be used directly for storing passwords?
A) They are too slow for enterprise applications.
B) They do not support numeric passwords.
C) Fast calculation speeds make them highly vulnerable to rapid off-line brute force attacks, especially using GPU-accelerated computing rigs.
D) Spring Boot does not compile if they are configured.
Answer: C — Passwords require algorithms designed to be slow (adaptive work factor) to make brute force attacks economically/computationally prohibitive.
Scenario-Based Challenge
Production Scenario:
You join an organization with a legacy application that has stored user passwords using plain text SHA-1. You must migrate the storage mechanism to BCrypt. However, you cannot ask all 100,000 active users to reset their passwords on day one. How do you implement a seamless migration without locking anyone out?
View SolutionUse Spring Security's DelegatingPasswordEncoder to transition user records dynamically:
- Configure a
DelegatingPasswordEncoderthat maps{sha1}to a SHA-1 encoder (for historical password validation) and sets the default/current encoder to{bcrypt}. - Perform a database update query to prepend
{sha1}to all existing raw SHA-1 password entries. - In your authentication filter/logic, intercept logins: when a user logs in successfully using the legacy hash, extract the plain text password, re-encode it using the default BCrypt encoder, and save the new
{bcrypt}hash back to their database record. - Over time, active users are transparently migrated to BCrypt without administrative resets or site outages.
Interview Questions
1. Conceptual: What is the risk of using symmetric encryption instead of hashing for passwords?
Symmetric encryption is reversible. If an attacker gains access to the database AND the encryption key (which must be stored somewhere in configuration files or key storage), they can decrypt all passwords back to plain text. Hashing is mathematically irreversible; even with full database access, passwords can only be guessed, not directly decrypted.
2. Concept: How can you safely configure the cost factor for BCryptPasswordEncoder in Spring Boot?
Measure the time it takes to verify a password on production-level hardware. The ideal cost factor is one where a single validation takes between 100ms and 200ms. If validation is too fast (e.g. under 10ms), brute-force is easier. If validation is too slow (e.g. over 1000ms), it causes high CPU usage and acts as a Denial of Service (DoS) vulnerability under normal login volumes.
Production Considerations
Keep BCrypt cost factors in sync with hardware advancements. Upgrade to Argon2 or PBKDF2 if required by specific compliance guidelines (e.g., PCI-DSS, SOC2). Never print, log, or log-audit raw passwords in telemetry or debug filters. Ensure the garbage collector sweeps memory containing CharSequence instances as early as possible.