The single most important design decision in credential storage is whether passwords are hashed before storage. The difference in security outcome is absolute β not a matter of degree.
| Attribute | Plaintext Storage | Hashed Storage |
| What is stored | The password as typed: Sunflower2019 | Fixed-length digest: 8c6976e5b5410415... |
| Reversible? | Yes β it is the password | No β one-way transformation; mathematically infeasible to reverse |
| Impact of database breach | All credentials immediately usable; no further effort required | Attacker must crack each hash individually β time and compute required |
| Authentication method | Compare stored password to input | Hash the input; compare digest to stored digest |
| Exam verdict | Never acceptable; replace the application | Required minimum; use strong algorithms with salting |
Key hash property for password storage: pre-image resistance. Given only the hash output, it is computationally infeasible to determine the input. This means stored digests are useless to an attacker without brute forcing β which is why algorithm choice (MD5 vs. bcrypt) matters enormously for offline attack feasibility.
The avalanche effect ensures that even a single character change in the input produces a completely different digest β making it impossible to infer anything about the original password from the hash:
| Password | SHA-256 Hash (stored) |
| 123456 | 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 |
| 1234567 | 8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414 |
| password | 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 |
| Password | e7cf3ef4f17c3999a94f2c6f612e8a888e5b1026878e4e19398b23bd38ec221a |
One character change ("password" β "Password") produces a completely different 256-bit hash. An attacker cannot use the hash to determine the password, its length, or any structural pattern. They can only test candidate passwords forward through the hash function.
Prerequisite: Attacker has a list of valid usernames β obtained via LDAP enumeration, email harvesting from company website, or public directory
Step 1 β Select spray password: Choose the single most common password: 123456. If any account uses this password, one attempt will find it
Step 2 β First pass across all accounts: Attempt 123456 against every account in the list. Each account receives exactly one failed attempt β well below any lockout threshold (typically 5β10)
Step 3 β No alerts, no lockouts: The authentication system sees distributed failures across many accounts β not concentrated failures on one account. Standard brute force detection based on per-account failure counts is not triggered
Step 4 β Second pass (if needed): Wait a period, then attempt password across all accounts β still only 2 total attempts per account
Step 5 β Hit: One account using 123456 grants access. The attacker has authenticated with one low-rate attempt β no lockout, no alert
Exam distinction: Spraying = few passwords Γ many accounts. Traditional brute force = many passwords Γ one account. Spraying is the lockout-evasion variant
Speed figures are approximate for a single modern GPU; actual performance varies by hardware and implementation. The orders-of-magnitude difference between MD5 and bcrypt is what matters for exam purposes.