Skip to content

The account database must not become a password oracle

10 min read By NT²

An account table can contain no plaintext vault items and still create a dangerous password-testing surface. We removed the columns that could turn a database copy into an offline guessing or recovery system.

The account database must not become a password oracle

Claim: an account row must not help unlock the vault

A cloud account database is useful. It can tell a service which public identity owns an account, whether that account is disabled, and whether a subscription permits a paid feature. It can help verify a signed request without receiving the private signing key.

It must not tell the service—or someone who steals a copy of the database—how to test guesses for a person's master password.

That is the boundary NT² applies to its Cloudflare D1 account table: the table holds service identity and entitlement facts, but nothing designed to unlock a vault, validate a master password, or bootstrap recovery without the user.

This distinction matters because a database does not need to contain the master password to become a password oracle. A salt is not a password. A verifier is not a password. An encrypted recovery package is not necessarily a password. Yet if those pieces can be combined to answer “was this guess correct?”, they form a password-testing system.

The risk is especially easy to miss when every individual column sounds harmless. One field helps a new device find its derivation settings. Another lets an API reject a wrong password. Another makes customer support recovery more convenient. None is labeled “password.” Together, however, they may give an attacker everything needed to run guesses privately, without calling the real service again.

We do not want a breach of the account database to become a breach of the password boundary. So the cloud account table is designed around a negative capability: it cannot help open the vault.

Constraint: helpful recovery columns become an offline oracle

Online password checks can be defended in ways that offline checks cannot. A service can rate-limit requests, detect unusual traffic, delay repeated failures, block abusive networks, alert an account owner, and record an audit trail. Those controls are imperfect, but the defender remains in the loop.

An attacker with a copied password-checkable bundle removes the defender from the loop.

The attacker can take a candidate password, run the configured key-derivation function with the corresponding salt, and try the result against a known verifier or recovery object. A clear authentication success means the guess was right. Failure means try another. The loop runs on the attacker's hardware, at the attacker's pace, without generating another request to NT².

A deliberately expensive key-derivation function raises the cost of every attempt. A unique salt prevents one precomputed table from serving every account. Both are necessary protections. Neither turns a weak password into a strong one, and neither restores server-side rate limiting after the relevant values have been copied.

That is why centralizing “non-secret” unlock helpers changes the threat model. The salt may be public by design, but a central collection of salts connected to reliable password verifiers is operationally valuable to an attacker. Add recovery bootstrap material, and the same database may also reveal how to reconstruct or request the next step after a successful guess.

Historical account schemas often accumulate exactly this kind of material. The reasons are usually understandable:

  • make first-time setup on a new device feel automatic;
  • let support confirm whether a user remembers the right password;
  • offer a familiar “forgot password” path;
  • keep recovery available after every local copy has disappeared;
  • reuse the account database as the source of truth for both cloud identity and local unlock.

Those conveniences are not free metadata. They make the provider part of the unlock design.

Calling such fields “escrow,” “bootstrap,” or “verification metadata” does not reduce what they can do. Security properties come from the questions a dataset can answer. If a database export can answer whether a master-password guess is correct, it is an offline password oracle. If it can advance recovery without a user-held factor or device, it is a recovery authority.

NT² previously considered more helpful cloud-side account material. We removed the columns that would let someone test passwords offline or begin reconstructing recovery from the account database. Removing them was not cosmetic cleanup. It reduced what a stolen D1 snapshot can be used to do.

The remaining database is still sensitive. Public keys, account state, billing facts, timestamps, and request metadata deserve access control, monitoring, retention limits, and careful operations. “Not a password oracle” does not mean “not worth protecting.” It means one especially dangerous power is absent even after the database boundary fails.

Design: D1 binds public identity, while unlock stays local

NT² gives the edge a narrower job.

The cloud account is bound to a Vault Key DID: a public cryptographic identity associated with the vault's signing key. During authentication, the edge sends a fresh challenge. The device signs that challenge with its private key, and the edge verifies the signature with registered public material.

D1 can therefore hold the facts needed for this relationship:

  • the Vault Key DID that identifies the cloud account;
  • public signing and key-agreement material;
  • whether the account is disabled;
  • Premium entitlement and billing state;
  • timestamps and limited service metadata needed to operate the account.

These facts let the service answer a remote authorization question: “Does this requester control the cryptographic identity registered for this account?”

They do not answer the local unlock question: “Is this the user's master password, and can it open the vault?”

The two questions use different material because they grant different powers. A valid challenge signature can authorize the transport of encrypted replica data. It does not derive the vault's local encryption key. A paid subscription can permit synchronization. It does not make ciphertext readable. A billing email can receive a receipt. It does not become a root identity or an unlock factor.

The password side stays in the local vault profile. The device keeps the KDF salt and the password verifier with the vault it unlocks. When the user enters a master password, the device performs derivation locally and checks the candidate key against local authenticated ciphertext. The edge does not receive the password, return the salt, check the verifier, or announce whether the guess was correct.

That local verifier is useful precisely because it gives the app a clear result. The device can distinguish a usable password-derived key from a wrong one before attempting to open the rest of the vault. But password-checkable material is hazardous when centralized. A mechanism that is appropriate beside one user's local encrypted vault becomes a high-value guessing target when copied into an account table for every user.

Storage location therefore expresses authority. The local profile has enough information to attempt local unlock because the user already possesses that vault copy. The account service has enough public information to verify cloud identity because it must authorize network requests. Neither side receives extra material merely because keeping everything in one database would be convenient.

This also keeps registration honest. Registering for cloud features means binding a public identity and recording service state. It does not mean depositing an unlock kit. The edge can recognize the vault later without learning how the master password protects it.

For encrypted sync, that separation fits the larger relay design. The cloud can authenticate a vault, check entitlement, accept opaque replica frames, and return encrypted updates. It does not need the local password path to perform any of those tasks. Identity binding answers who may move the ciphertext; local unlock answers who may interpret it.

Trade-off: support cannot manufacture access

The strongest evidence for this boundary is what NT² cannot do.

Support cannot inspect an account row, verify a master password, and unlock a user's vault. It cannot send a magic email that changes the password protecting local ciphertext. It cannot rebuild missing unlock state from D1 after every device, backup, and recovery factor has been lost.

That is a real loss of convenience. People are accustomed to online accounts where support can restore access after enough identity checks. For server-owned data, the provider already controls the authoritative copy and can choose a new account credential. A local encrypted vault is different: changing an account record does not recreate a key that the server never held.

The product must respond by making user-controlled continuity practical. Backups need to be understandable and portable. Device handoff must be explicit. Recovery factors must be presented before they are urgently needed. Warnings about losing the final usable copy cannot be softened into generic account language.

The distinction should also be clear during support conversations. Support can help diagnose account status, subscription entitlement, authentication errors, transfer failures, and whether encrypted objects reached the service. It can explain recovery paths that the user already established. It cannot cross the cryptographic boundary and produce an unlock capability from billing history or personal documents.

This can be frustrating at the worst possible moment. It is still more honest than advertising zero-knowledge while retaining a provider-operated recovery path capable of opening the vault.

The trade is concrete: the user accepts responsibility for keeping a usable recovery path, and the provider gives up the power to create one later. The absence of that power protects every account from the same centralized failure mode.

What we refuse

We will not store the KDF salt and password verifier in D1. Neither value is the master password, but together with the derivation settings they can support offline testing. Keeping them out of the account table prevents a single database copy from becoming a catalog of password-checkable targets.

We will not store recovery-oracle material that lets the account service bootstrap vault access without a user-held factor. An encrypted blob is not automatically safe to centralize; if it provides a reliable next step after a password guess or account takeover, its role matters more than its label.

We will not build “forgot password” around a server-held unlock helper. Email, a support ticket, or control of a cloud session may establish facts about an account. They cannot be allowed to substitute for the cryptographic material that opens local vault data.

We will not quietly reintroduce these powers as support tooling. A private admin endpoint that validates a master password is still a password oracle. A recovery service available only to employees is still a recovery authority. Restricting access to a dangerous capability is weaker than not collecting the material required to create it.

And we will not claim that encrypted replica storage alone makes the whole system blind. Registration, account storage, recovery, support, and unlock all participate in the trust boundary. A service is not zero-knowledge if another part of its backend can assemble the ingredients needed to test the user's secret.

These refusals narrow what NT² can promise after catastrophic loss. They also narrow what an attacker gains from compromising the cloud account database. That is the point.

Close: keep the account useful and deliberately incomplete

A well-designed account table should be useful for the service it supports. D1 can bind a Vault Key DID, verify public-key authentication, enforce account status, and record Premium entitlement. It can help the edge coordinate blind transport without becoming a readable copy of the vault.

It should also be deliberately incomplete.

The missing KDF salt, local verifier, and recovery-oracle material are not overlooked features. Their absence prevents the account database from becoming a master-password testing rig or a hidden escrow system. Registration remains identity binding, not a deposit of the means to unlock.

The surrounding boundaries are explored in The KDF salt stays on your device, Unlocking a local vault is not logging in to the cloud, and Blind replica sync on the edge.

If you want a vault whose cloud service can recognize and relay for it without gaining the power to open it, explore NT² Vault.

Last updated 2026-08-15

Related stories