CYBER SECURITY • IDENTITY AND ACCESS MANAGEMENT

Password Storage — Explain password storage concepts (hashing + salting) at a high level

Why modern systems never store your password and how cryptographic hashing and salting protect credentials at rest.

Historical Context & Motivation

The earliest multi-user computer systems of the 1960s faced a deceptively simple problem: how does the machine verify that the person sitting at a terminal is who they claim to be? The first solution was equally simple — store every user's plaintext password in a file on disk and compare it character-by-character at login. This approach worked until attackers (or curious insiders) gained read access to the file, instantly compromising every account on the system. The history of password storage is, in essence, a series of increasingly sophisticated responses to the fundamental insight that storing secrets in recoverable form is inherently dangerous.

1961
CTSS at MIT
The Compatible Time-Sharing System introduced password-based login for multiple users, storing passwords in a plaintext file. A 1966 software bug accidentally printed the password file as the message of the day, exposing every credential on the system.
1976
Unix crypt(3)
Robert Morris Sr. and Ken Thompson replaced plaintext storage on Unix with a one-way hash function based on a modified DES cipher. The system stored only the hash; the original password could not be recovered from it.
1976
Introduction of Salts
The same Unix team added a 12-bit random salt to each password before hashing, multiplying the attacker's work by a factor of 4096 and defeating precomputed lookup tables.
1999
bcrypt Published
Niels Provos and David Mazières introduced bcrypt, which incorporated an adjustable cost factor so that hashing could be slowed down as hardware grew faster — a concept known as adaptive key stretching.
2015
Argon2 Wins PHC
The Password Hashing Competition selected Argon2 as the recommended standard. Argon2 resists GPU and ASIC attacks by requiring tunable amounts of both CPU time and memory, setting the current state of the art.

This timeline reveals a recurring pattern: each generation of password storage was broken by advances in attacker capability — faster CPUs, GPUs, rainbow tables, or commodity cloud computing — and the security community responded with stronger primitives. The central question this lesson addresses is: how can a system verify a password without ever storing it, and what makes modern hashing and salting schemes resistant to both brute-force and precomputation attacks?

Core Principles & Definitions

Secure password storage rests on a small set of interlocking principles, each of which addresses a distinct threat. Understanding these principles individually is essential before seeing how they compose into a complete defense.

1

One-Way Transformation

A cryptographic hash function maps an input of arbitrary length to a fixed-size digest such that computing the hash is efficient, but inverting it (recovering the input from the digest) is computationally infeasible. This is preimage resistance.
2

Avalanche Effect

Changing even a single bit of the input should flip approximately half the bits of the output. This avalanche effect ensures that similar passwords produce wildly different hashes, preventing attackers from inferring password similarity from hash similarity.
3

Salting

A salt is a unique, randomly generated value concatenated with the password before hashing. It ensures that two users with the same password produce different hashes, defeating rainbow tables and cross-account comparison.
4

Computational Cost

General-purpose hashes like SHA-256 are designed to be fast. Password hashing functions like bcrypt, scrypt, and Argon2 intentionally introduce a tunable work factor (iterations, memory, parallelism) to slow down brute-force attacks without noticeably degrading the user experience.
5

Determinism

Given the same input and the same salt, the hash function must always produce the same output. Without determinism, the server could never verify a returning user's password against the stored hash.
KEY TAKEAWAY
Think of hashing like feeding a document through a cross-cut shredder: the output (confetti) is uniquely determined by the input (document), and you can verify a match by shredding a candidate document and comparing the piles, but reconstructing the original from the confetti is practically impossible. A salt is like spraying a unique dye on each document first — even identical documents produce distinctly colored confetti, so precomputed 'confetti catalogues' are useless.

Visual Explanation — The Hashing & Salting Pipeline

The top row shows the registration flow: the plaintext password is concatenated with a random salt, then passed through a slow hash function; only the resulting digest (and the salt) are stored. The bottom row shows the login flow: the server retrieves the stored salt, re-hashes the login attempt with the same parameters, and performs a constant-time comparison.

Several details in the diagram deserve emphasis. First, the plaintext password never persists beyond the duration of the hashing operation — it exists in volatile memory only long enough to compute the digest. Second, the salt is stored in the clear alongside the hash (often encoded into the same string, as in bcrypt's $2b$12$... format). This is not a vulnerability: the salt's purpose is not to be secret but to ensure uniqueness. Third, the cost factor parameterises the hash function's internal loop count, allowing administrators to tune the computation time per hash — typically targeting 100–500 ms per verification on current hardware.

How It Works — The Mathematics of Hashing & Salting

Although this lesson treats password storage at a high level, a precise understanding of the relevant mathematical properties is essential for reasoning about security guarantees. Cryptographic hash functions are built from compression functions composed iteratively (Merkle–Damgård construction) or from sponge functions (as in SHA-3), but their security rests on three core properties expressed below.

HASH FUNCTION DEFINITION
H : {0, 1}* → {0, 1}ⁿ
A hash function H maps an arbitrary-length binary string to a fixed-length n-bit digest. For SHA-256, n = 256; for bcrypt's internal Blowfish state, n = 192.
PREIMAGE RESISTANCE
Given h, finding m such that H(m) = h requires O(2ⁿ) work
An attacker who possesses only the hash digest h must try on the order of 2n candidate inputs to find one that hashes to h. For n = 256 this is computationally infeasible.
SALTED HASH COMPUTATION
digest = H(salt ‖ password, cost)
The operator ‖ denotes concatenation. The salt is a random value (typically 128 bits). The cost parameter controls the number of internal iterations. In bcrypt, cost = 12 means 212 = 4,096 rounds of the Blowfish key schedule.
BRUTE-FORCE SEARCH SPACE WITH SALT
Work = |S| × |P| × C(cost)
Without salts, an attacker precomputes hashes for the password space |P| once and reuses them against all accounts. With a unique salt per account, the attacker must redo the computation for each of the |S| distinct salts. C(cost) represents the time for one hash evaluation, which grows exponentially with the cost parameter.

The interplay of these equations reveals the defense-in-depth strategy. Preimage resistance prevents inversion of a single hash. Salting multiplies the attacker's workload across multiple accounts. The adjustable cost factor ensures that even as hardware improves, the time per hash attempt can be increased to maintain a constant security margin. Together, they transform password cracking from a feasible table lookup into an economically prohibitive brute-force search.

Modern Password Hashing Algorithms

Not all hash functions are appropriate for password storage. General-purpose functions like MD5 and SHA-256 are designed for speed, which is the opposite of what defenders need when an attacker is trying billions of guesses per second. The following diagram and table compare the major password-specific hashing algorithms and their resistance properties.

This chart compares five common algorithms across three dimensions: CPU cost, memory cost, and GPU/ASIC resistance. General-purpose hashes (MD5, SHA-256) cluster at the unsafe end because they lack tunable cost and memory hardness. Modern password hashing functions (bcrypt, scrypt, Argon2) offer adjustable resistance that scales with attacker hardware improvements.

The distinction between memory-hard and purely CPU-bound algorithms is crucial to understanding modern threat models. GPUs excel at parallel, compute-intensive tasks — an NVIDIA RTX 4090 can compute roughly 164 billion MD5 hashes per second. However, GPU cores have limited local memory, so algorithms like scrypt and Argon2 that require megabytes of RAM per hash evaluation cannot be efficiently parallelised across thousands of GPU cores. The Argon2id variant, specifically, combines data-independent memory access patterns (resistance to side-channel attacks) in a first pass with data-dependent patterns (resistance to time-memory tradeoff attacks) in subsequent passes, making it the current OWASP recommendation for password storage.

Worked Example — Storing and Verifying a Password with bcrypt

Let us walk through a complete registration and login cycle using bcrypt. This example illustrates the concrete data transformations at each stage and shows why an attacker with full database access still cannot recover the original password.

Registering and Authenticating a User with bcrypt
1
Step 1 — User Submits PasswordThe user registers with the password C0mput3r$ci. The server receives this over a TLS-encrypted channel and holds it in memory.
2
Step 2 — Generate a Random SaltThe server generates a 128-bit cryptographically secure random salt using a CSPRNG (e.g., /dev/urandom on Linux or os.urandom() in Python). Suppose the base-64 encoded salt is WpKe3IAjF8QYlz2UOkDayO.
salt = WpKe3IAjF8QYlz2UOkDayO
3
Step 3 — Choose a Cost FactorThe administrator has configured a cost factor of 12, meaning 212 = 4,096 iterations of the Blowfish key schedule. On current server hardware, this yields approximately 250 ms per hash — fast enough that legitimate users barely notice, but slow enough to cripple offline brute-force attacks.
cost = 12 → 4,096 iterations ≈ 250 ms/hash
4
Step 4 — Compute the HashThe server passes the password, salt, and cost to the bcrypt function. Internally, bcrypt initialises the Blowfish cipher with the password as the key, then repeatedly encrypts a constant string ("OrpheanBeholderScryDoubt") through 4,096 rounds of key expansion incorporating both the password and the salt. The output is a 192-bit hash.
hash = $2b$12$WpKe3IAjF8QYlz2UOkDayOeFH0RqKXnP6Gq5F0vL7z3dGbKhX1W2u
5
Step 5 — Store the Hash StringThe full bcrypt string $2b$12$WpKe3IAjF8QYlz2UOkDayOeFH0RqKXnP6Gq5F0vL7z3dGbKhX1W2u encodes the algorithm identifier ($2b$), the cost (12), the salt (first 22 characters after the cost), and the hash (remaining characters). This single string is stored in the user database. The plaintext password is zeroed from memory.
Database stores: $2b$12$WpKe3IAjF8QYlz2UOkDayO... — the plaintext password is discarded.
6
Step 6 — Login VerificationWhen the user later logs in with C0mput3r$ci, the server retrieves the stored hash string, extracts the algorithm, cost, and salt, then recomputes the hash with the submitted password. If the resulting digest matches the stored digest in a constant-time comparison (to prevent timing side-channels), access is granted.
bcrypt_verify("C0mput3r$ci", stored_hash) → TRUE — access granted
Why Constant-Time Comparison?
A naïve string comparison function returns false as soon as it encounters the first mismatched byte, meaning wrong guesses that match more prefix bytes take slightly longer. An attacker measuring response times in microseconds can exploit this to guess the hash byte-by-byte — a timing attack. Constant-time comparison functions always inspect every byte, leaking no information about which bytes match.

Strengths, Limitations & Common Pitfalls

No defensive technique exists in isolation; understanding the boundaries of hashing and salting is as important as understanding the mechanisms themselves. The table below contrasts correct practices with common deployment errors and their consequences.

Comparison of password storage approaches and their security trade-offs.
PracticeStrengthsLimitations / Pitfalls
Salted, adaptive hash (bcrypt, Argon2)Defeats rainbow tables; brute-force cost is tunable; each account is independently protected.Does not protect against phishing, credential reuse, or weak passwords. Cost factor must be periodically re-tuned as hardware improves.
Unsalted fast hash (MD5, SHA-1)Trivial to implement; extremely fast verification.Vulnerable to rainbow tables and GPU brute-force. A single leaked database compromises all identical passwords across accounts.
Encryption (AES) of passwordsPasswords can be recovered if necessary (e.g., for migration).If the encryption key is compromised, all passwords are exposed instantly. Violates the principle that verification should not require the ability to recover the secret.
Pepper (server-side secret key)Adds a layer of defense even if the database is fully compromised; attacker also needs the pepper.Key management complexity; if the pepper is lost, all stored credentials become unverifiable. Not a replacement for salting.
KEY TAKEAWAY
Hashing and salting protect credentials at rest — they are the seatbelt in the security car. But seatbelts do not prevent collisions: password complexity policies, multi-factor authentication, breach monitoring, and rate limiting form the rest of the safety system. A well-hashed database that was breached still means users who reuse passwords elsewhere are at risk, underscoring that password storage is one layer of a multi-layered identity strategy.

Connection to Advanced Theory & Emerging Techniques

Password hashing is only one piece of the broader authentication landscape. As threats evolve, the field is moving toward techniques that reduce or eliminate the need to store password-derived secrets altogether. Understanding how classical hashing relates to these emerging approaches provides important context for where the industry is heading.

How classical password hashing connects to advanced and emerging authentication techniques.
Classical (This Lesson)Advanced / Emerging
Server stores salted hash of password; verifies by re-hashing the candidate.OPAQUE / aPAKE: Asymmetric password-authenticated key exchange. The server never sees the plaintext password, not even during registration. The protocol uses an oblivious PRF so the server stores a credential file it cannot evaluate without the client.
Security relies on the computational cost of hash inversion.Passkeys (FIDO2/WebAuthn): Eliminates passwords entirely. Authentication uses public-key cryptography; the server stores only the public key. Phishing-resistant by design since credentials are origin-bound.
Argon2 is memory-hard to resist GPU/ASIC attacks.Balloon Hashing: A provably memory-hard function analysed in the random oracle model. Provides formal security guarantees that Argon2's heuristic analysis cannot.
Cost factor is manually tuned by administrators.Adaptive re-hashing on login: When a user authenticates, the system transparently re-hashes with a higher cost factor or upgrades to a newer algorithm (e.g., from bcrypt to Argon2id) without requiring a password reset.

The trend is clear: the security community recognises that any scheme requiring users to create and remember high-entropy secrets is fundamentally limited by human behaviour. Passkeys and asymmetric protocols represent a paradigm shift away from shared secrets. Nevertheless, password-based authentication will persist in legacy systems for years, making a solid understanding of hashing and salting an essential part of every security engineer's toolkit.

Practice Problems

PROBLEM 1CONCEPTUAL
Explain why storing passwords using reversible encryption (e.g., AES-256-CBC) is considered less secure than storing them as salted hashes, even if the encryption key is kept in a hardware security module (HSM).
PROBLEM 2BASIC CALCULATION
A system uses bcrypt with a cost factor of 14. Each hash evaluation takes approximately 1 second. An attacker obtains the database and attempts to crack a single account by brute-forcing all 6-character lowercase alphabetic passwords (a–z). How many candidate passwords exist, and approximately how long would the brute-force attack take?
PROBLEM 3INTERMEDIATE
A developer argues that using a single, application-wide secret value (a 'pepper') concatenated with each password before hashing with SHA-256 is equivalent to using bcrypt with per-user salts. Identify at least two critical flaws in this reasoning.
PROBLEM 4APPLIED
You are designing a password storage system for a web application expecting 10,000 concurrent login requests per second at peak load. You want to use Argon2id with parameters that take 250 ms per hash on your server hardware. Calculate the number of CPU-seconds required per second at peak, and propose an architectural strategy to handle this without degrading user experience.
PROBLEM 5CRITICAL THINKING
Quantum computers running Grover's algorithm can search an unstructured space of N elements in O(√N) time, effectively halving the bit-security of hash functions. Discuss how this affects password storage: does Grover's algorithm make salted Argon2 obsolete? What additional defences, if any, should a forward-looking security architect consider?

Summary — Password Storage Concepts

Secure password storage transforms the authentication problem from 'protect a secret' to 'store only an irreversible fingerprint.' A cryptographic hash function provides preimage resistance — the one-way property that prevents recovering the password from the digest. A unique, random salt per account ensures that identical passwords produce different hashes, defeating rainbow tables and cross-account correlation. The avalanche effect prevents similarity-based inference, and an adjustable cost factor keeps brute-force attacks economically infeasible even as hardware improves.

Modern best practice centres on Argon2id (or bcrypt as a well-understood alternative), both of which incorporate built-in salting and tunable CPU/memory costs. Password hashing is a critical defence-in-depth layer but does not replace multi-factor authentication, complexity policies, or the emerging shift toward passwordless authentication (FIDO2/passkeys). Understanding these foundations equips you to evaluate, audit, and design authentication systems that protect user credentials even when everything else has been compromised.

Varsity Tutors • Cyber Security • Password Storage — Explain password storage concepts (hashing + salting) at a high level