Skip to content
Blog

How to prevent account enumeration in login and recovery

Find account-existence leaks in login, signup, and password recovery. Review messages, status codes, redirects, and timing while keeping account recovery usable.

Frederick Jahn
Frederick JahnSeptember 5, 2026
How to prevent account enumeration in login and recovery

Account enumeration occurs when someone can determine which accounts exist from the application's responses. Prevent it by reviewing the whole response contract across login, signup, and recovery, then removing disclosures the product does not intend to make public.

A generic error message is useful, but it is only one part of that contract. Status codes, redirects, response size, and processing time can still separate an existing account from a nonexistent one.

OWASP's account-enumeration testing guide also notes an important scope decision: some products deliberately make usernames public. Define what should remain private before calling every discoverable identifier a vulnerability.

Decide which facts the interface may disclose

Write down the intended behavior for an existing account, a nonexistent account, and an account that cannot currently sign in. Include email aliases and federated identities if they are supported.

A public profile directory may intentionally disclose a username while keeping its associated email private. A support-assisted workflow may disclose information after identity verification that the public recovery endpoint should not disclose.

Those are different policies. Do not attempt to solve them with one error string copied across every screen.

Compare the complete response

Use synthetic accounts in an environment you are authorized to test. Keep the request count low and avoid sending recovery messages to real users.

SurfaceCompareCommon review gap
LoginBody, status, and redirect“Wrong password” differs from “unknown user”
Password resetAcknowledgment and processing pathOne response waits for a delivery provider
SignupDuplicate-account handlingEmail existence is disclosed without a product decision
Identity lookupPublic fields and response shapeA helper endpoint bypasses the main form's policy
ThrottlingBehavior before and after limitsLimits reveal account state indirectly

The table is a review method, not a checklist whose completion proves the absence of every side channel. Record what you actually compared.

Make timing less dependent on account existence

If one branch performs expensive work and the other immediately returns, identical visible text may conceal a predictable difference. Review the server-side path before adding arbitrary sleep delays.

For password recovery, OWASP recommends consistent responses and response times, with asynchronous processing among the available approaches. Keep that processing bounded: a uniform acknowledgment must not create an unlimited delivery queue.

Measure several synthetic requests under comparable conditions. One slow request is not a timing channel, and one fast test is not proof that the paths are equivalent. Capture the application version and observation point so another engineer can repeat the comparison.

Keep useful feedback behind a trusted boundary

People still need to recover access. A neutral acknowledgment can explain that instructions will be sent if the account is eligible, while providing a safe support route for people who cannot receive them.

Avoid changing account state simply to make public responses look consistent. The recovery service should not lock an account because someone requested a reset for its address.

Where the product intentionally discloses an existing account during signup, document that decision and the reason. Then bound automated probing rather than pretending the disclosure has been eliminated.

Use bot controls as a supporting layer

Rate limits and automation controls can reduce repeated probing, but they do not repair a response that reveals account existence in one request. Fix the disclosure first, then check whether public endpoints can be queried excessively.

Keep controls consistent across web and API paths. A protective frontend does not help when a second endpoint exposes the same lookup directly.

Review password-reset abuse for delivery flooding, which is a related but different problem. The account takeover use case provides the product context for automated account attacks. Bring the response comparison to the implementation review so disclosure fixes and traffic controls remain separate work items.