
SCRAM authenticates users without sending passwords, stores only derived keys (not plaintext), and prevents replay attacks with nonces and salts. It’s a modern alternative to legacy password schemes and is available via SASL in many servers and clients.
What Is SCRAM?
Salted Challenge Response Authentication Mechanism (SCRAM) is a password-based authentication protocol standardized by the IETF (commonly used as a SASL mechanism). Instead of transmitting the user’s password, SCRAM proves knowledge of it through a challenge-response exchange using:
- a salt (unique per account),
- a nonce (unique per session),
- an iteration count (work factor),
- and a key-derivation function (e.g., PBKDF2 with HMAC-SHA-256).
Common variants: SCRAM-SHA-1, SCRAM-SHA-256, and SCRAM-SHA-512 (some deployments also use channel binding for MITM protection).
How SCRAM Works (Step-by-Step)
Notation: H() = hash (e.g., SHA-256), HMAC(k,m), KDF(password, salt, iterations) = PBKDF2-HMAC.
- Client → Server: client-first-message
Sends username and a fresh client noncenc. - Server → Client: server-first-message
Looks up user’s stored auth data, returns:- salt
s(from account record), - iteration count
i, - server nonce
ns(fresh, often concatenated withnc).
- salt
- Client computes keys locally
SaltedPassword = KDF(password, s, i)ClientKey = HMAC(SaltedPassword, "Client Key")StoredKey = H(ClientKey)- Builds an auth message transcript (the exact strings of the three messages).
- Client → Server: client-final-message
Sends:- combined nonce (
nc+ns), - ClientProof =
ClientKey XOR HMAC(StoredKey, AuthMessage)
(This proves the client knows the password without sending it.)
- combined nonce (
- Server verifies
- Recomputes
StoredKeyfrom its stored data, verifiesClientProof. - If valid, computes
ServerKey = HMAC(SaltedPassword, "Server Key")andServerSignature = HMAC(ServerKey, AuthMessage).
- Recomputes
- Server → Client: server-final-message
Returns ServerSignature so the client can verify it’s talking to the real server.
What the server stores: never the plaintext password. It stores salt, iteration count, and either the SaltedPassword or the derived StoredKey and ServerKey (or values sufficient to recompute/verify them).
Main Features & Components
- Salting: Unique per-user salt thwarts rainbow tables.
- Key Derivation with Work Factor: Iterations make brute force slower.
- Challenge-Response with Nonces: Prevents replay attacks.
- Mutual Authentication: Client verifies the server via
ServerSignature. - No Plaintext Passwords in Transit or at Rest: Only derived values are stored/transmitted.
- Channel Binding (optional): Binds auth to the underlying TLS channel to deter MITM.
Benefits & Advantages
- Strong security with passwords: Better than Basic/Digest/PLAIN (without TLS).
- Minimal leakage if DB is stolen: Attackers get salts and derived keys, not plaintext.
- Replay-resistant: Nonces and signed transcripts block replays.
- Standards-based & widely supported: Kafka, PostgreSQL, MongoDB, IMAP/SMTP, XMPP, LDAP, etc.
- No PKI dependency: Works with or without TLS (though TLS is strongly recommended).
When & How to Use SCRAM
Use SCRAM when you:
- need password-based auth with solid defenses (microservices, message brokers, DBs),
- require mutual verification (client also verifies server),
- want a drop-in option supported by SASL frameworks and libraries.
Pair it with TLS in any hostile network. Prefer SCRAM-SHA-256 or stronger. Enable channel binding where client/server stacks support it.
Real-World Use Cases
- Message brokers: Kafka clusters using SASL/SCRAM for client-to-broker auth.
- Databases: PostgreSQL and MongoDB deployments using SCRAM-SHA-256.
- Email/XMPP/LDAP: SASL SCRAM to avoid password exposure and replays.
- Enterprise gateways: Reverse proxies terminating TLS and relaying SCRAM to backends.
Implementation Blueprint (Server-Side)
Account creation / password change
- Generate random
salt(16–32 bytes). - Choose
iterations(e.g., 65,536+; tune for latency). - Compute
SaltedPassword = KDF(password, salt, iterations). - Derive and store either:
StoredKey = H(HMAC(SaltedPassword, "Client Key"))ServerKey = HMAC(SaltedPassword, "Server Key")- plus
salt,iterations,username - (Optionally store
SaltedPasswordif your library expects it, but avoid storing plaintext or unsalted hashes.)
Authentication flow (pseudocode)
# server-first-message
record = lookup(username)
nonce_s = random()
send { salt: record.salt, iter: record.iter, nonce: client_nonce + nonce_s }
# client-final-message arrives with ClientProof and combined nonce
authMsg = transcript(clientFirst, serverFirst, clientFinalWithoutProof)
# Verify proof
ClientSignature = HMAC(record.StoredKey, authMsg)
ClientKey = XOR(ClientProof, ClientSignature)
StoredKey' = H(ClientKey)
if StoredKey' != record.StoredKey: reject
# Success: send server signature for mutual auth
ServerSignature = HMAC(record.ServerKey, authMsg)
return { server_signature: ServerSignature }
Storage schema (example)
users(
id PK,
username UNIQUE,
salt VARBINARY(32),
iterations INT,
stored_key VARBINARY(32 or 64),
server_key VARBINARY(32 or 64),
updated_at TIMESTAMP
)
Security Best Practices
- Always use TLS, and enable channel binding if your stack supports it.
- Strong randomness for salts and nonces (CSPRNG).
- High iteration counts tuned to your latency budget; revisit yearly.
- Rate-limit and lockout policies to deter online guessing.
- Audit and rotate credentials; support password upgrades (e.g., SHA-1 → SHA-256).
- Side-channel hygiene: constant-time comparisons; avoid verbose error messages.
Integrating SCRAM into Your Software Development Process
1) Design & Requirements
- Decide on algorithm (prefer SCRAM-SHA-256 or higher) and iterations.
- Define migration plan from existing auth (fallback or forced reset).
2) Implementation
- Use a well-maintained SASL/SCRAM library for your language/runtime.
- Centralize KDF and nonce/salt generation utilities.
- Add feature flags to switch mechanisms and iteration counts.
3) Configuration & DevOps
- Store salts/keys only in your DB; protect backups.
- Secrets (e.g., TLS keys) in a vault; enforce mTLS between services where applicable.
- Add dashboards for auth failures, lockouts, and latency.
4) Testing
- Unit-test transcripts against known vectors from your library/docs.
- Property/fuzz tests for parser edge cases (attribute order, malformed messages).
- Integration tests with TLS on/off, and with channel binding if used.
5) Rollout
- Canary a subset of users/services.
- Monitor failure rates and latency; adjust iterations if needed.
- Backfill/migrate user records on next login or via scheduled jobs.
Comparison Cheat Sheet
| Mechanism | Sends Password? | Server Stores | Replay-Resistant | Mutual Auth | Notes |
|---|---|---|---|---|---|
| Basic (over TLS) | Yes (base64) | Plain/Hash (app-defined) | No | No | Only acceptable with strong TLS; still weak vs replays if tokens leak. |
| Digest | No | Hash of password | Partially | No | Outdated; weaker KDF and known issues. |
| PLAIN (over TLS) | Yes | App-defined | No | No | Only safe inside TLS; still exposes password at app layer. |
| SCRAM | No | Salted keys | Yes | Yes | Modern default for password auth; supports channel binding. |
| OAuth 2.0/OIDC | N/A | Tokens | Yes | Yes (via TLS + signatures) | Token-based; different tradeoffs and flow. |
Developer Quick-Start (Language-Agnostic)
- Pick a library that supports SCRAM-SHA-256 and (if possible) channel binding.
- Server config: enable the SCRAM mechanism; set minimum iteration count and required hash.
- Client config: select SCRAM mechanism; supply username/password; verify server signature.
- Migrations: on user login, if you detect an old scheme (e.g., SHA-1), re-derive keys with SHA-256 and higher iterations and update the record.
FAQs
Do I still need TLS with SCRAM?
Yes. SCRAM protects passwords and gives mutual auth, but TLS protects confidentiality/integrity of all data and enables channel binding.
Which hash should I choose?
Use SCRAM-SHA-256 or stronger. Avoid SHA-1 for new systems.
How many iterations?
Start with a value that adds ~50–150 ms on your hardware per attempt, then adjust based on throughput/latency targets.
Final Checklist
- SCRAM-SHA-256 enabled on server and clients
- Unique salt per user, secure CSPRNG
- Iterations set and documented; metrics in place
- TLS enforced; channel binding on where supported
- Tests cover transcripts, edge cases, and migrations
- Monitoring, rate-limiting, and lockouts configured
Recent Comments