Authentication

SCRAM: How to Prove a Password Without Sending It

June 20, 2026 9 min read Haven Team

When you log in to PostgreSQL, MongoDB, or an XMPP server, your password usually never crosses the wire. Instead the two sides run a short challenge-response dance called SCRAM, and each proves something to the other without revealing what it knows. The design is older than most people realize and quietly correct in ways many login systems still are not.


The obvious way to log in is to send your password to the server, which hashes it and compares it to a stored hash. This works, and a great deal of the internet still does exactly this over TLS. But it has a structural weakness: at the moment of login, the server holds your plaintext password in memory. A compromised server, a verbose log line, or a debugging proxy can capture it. And the server has to be trusted to hash it correctly rather than just store what it received.

SCRAM, the Salted Challenge Response Authentication Mechanism defined in RFC 5802, takes a different route. The client proves it knows the password by performing a computation that only the password-holder could perform, and the server verifies that proof against stored values that are not the password and cannot be replayed. Neither side ever transmits the secret.

What the Server Actually Stores

SCRAM never stores your password, and it does not even store a single hash of it. For each user, the server keeps four things: a random salt, an iteration count, a StoredKey, and a ServerKey.

These derive from the password through a chain. The password and salt feed a slow, iterated function (PBKDF2) to produce a SaltedPassword. From that, an HMAC produces a ClientKey, and a single hash of the ClientKey produces the StoredKey. A separate HMAC produces the ServerKey. The server keeps StoredKey and ServerKey; it discards ClientKey and the password.

The asymmetry that matters

The server stores enough to verify a login but not enough to perform one. Someone who steals the SCRAM database cannot turn around and authenticate as a user without first cracking the salted, iterated password. This is the same reason we hash passwords with slow functions instead of storing them, pushed one step further into the login protocol itself.

The Four-Message Handshake

A SCRAM exchange is four messages. Each carries a nonce, a random value that makes every session unique and kills replay attacks.

  1. Client first: the client sends its username and a client nonce.
  2. Server first: the server replies with the salt, the iteration count, and a combined nonce (the client nonce with server randomness appended).
  3. Client final: the client computes its proof and sends it, along with the combined nonce.
  4. Server final: the server verifies the proof and returns a server signature, proving to the client that the server also knew the stored values.

The clever part is the third message. The client derives the ClientKey from the password, computes a ClientSignature by HMAC-ing the full transcript of the exchange under the StoredKey, and XORs the two together into a ClientProof. It sends the proof, not the key.

The server takes the proof, XORs it with its own recomputed ClientSignature to recover the candidate ClientKey, hashes that, and checks whether the result equals the StoredKey it has on file. If they match, the client proved it knew the password. The server never reconstructed the password, only confirmed the proof was consistent with its stored verifier.

Mutual Authentication, For Free

SCRAM is not just the client proving itself to the server. The fourth message runs the logic in reverse: the server computes a ServerSignature over the transcript using the ServerKey and sends it back. The client, which can derive the ServerKey from the password, checks it.

This means a fake server cannot complete the handshake. An attacker who intercepts the exchange and tries to impersonate the server fails at the final step, because it does not hold the ServerKey and cannot produce a valid ServerSignature. The client learns it is talking to the real server, not just the other way around. Few password protocols give you that property without extra machinery.

Why the Transcript Is Signed

Both signatures are computed over the entire message exchange so far, called the AuthMessage, not just over a single challenge. This binds the proof to this specific conversation. An attacker who records one login cannot replay the captured proof in a later session, because the nonces and therefore the transcript differ every time. It also means a man-in-the-middle cannot quietly alter the negotiated parameters without invalidating both signatures.

Threat Plain password-over-TLS SCRAM
Server sees plaintext password Yes, at login Never
Stolen database lets attacker log in directly Depends on hashing No, must crack first
Replay of a captured login Possible if TLS fails Blocked by nonces
Client verifies the server Only via TLS cert Built into the handshake

The Honest Limits

SCRAM is a real improvement, but it is not a force field, and it pays to know its edges.

First, it is not a substitute for a secure channel. SCRAM should run inside TLS. The "channel binding" variants (SCRAM-SHA-256-PLUS) tie the SCRAM exchange to the underlying TLS connection, which defends against a relay attack where someone sits between client and server on separate TLS sessions. Without channel binding, a sufficiently positioned attacker can still relay the handshake. Use the PLUS variant when both sides support it.

Second, the stored verifiers are offline-crackable. If an attacker steals StoredKey, ServerKey, and the salt, they can mount an offline guessing attack against the password, just as they could against any password hash. The iteration count is your defense, and the protocol lets the server choose it. Too low, and cracking is cheap. This is why a strong, high-entropy password still matters even with SCRAM in front of it. A long passphrase is what makes the offline attack hopeless.

Third, SCRAM authenticates a password. It is not multi-factor authentication, and it does not protect against phishing of the password itself at a fake login page outside the protocol. For the strongest account security, password-based mechanisms like SCRAM are increasingly paired with, or replaced by, public-key methods such as passkeys that have no shared secret to steal at all.

Where SCRAM sits

It belongs to the same family as SRP and the newer OPAQUE protocol: password authentication that keeps the secret off the wire and out of the server's hands. SCRAM is the pragmatic, widely deployed member of that family, which is why you find it under PostgreSQL, MongoDB, Kafka, and XMPP rather than in a research paper.

Why This Pattern Matters

The recurring theme across modern security design is the same: minimize what any single party has to be trusted with. SCRAM applies that to login. The server is trusted to verify you, not to hold your password. The wire carries a proof, not a secret. The stored data confirms a guess, it does not enable an impersonation.

At Haven, the same instinct runs through the whole system. Your passphrase derives keys on your device and never reaches our servers, not even as a hash. We would rather hold as little of your secret as the math allows, because the safest thing to do with a secret is never to receive it. SCRAM is one small, well-worn example of that principle working in practice. If you want the broader picture, our overview of privacy-preserving authentication connects the dots.

Try Haven free for 15 days

Encrypted email and chat in one app. No credit card required.

Get Started →