Skip to main content

User enumeration protection

User enumeration is a technique where attackers use failed authentication attempts to determine whether a specific user account exists in an application.

For example, if a user signs in with an identifier that doesn't match an existing account, Clerk tells them no account was found. This is helpful for legitimate users who may not remember if they signed up. However, a malicious actor can exploit these error messages to check whether accounts exist for specific identifiers (like emails or phone numbers) and build a list of valid accounts. This is user enumeration.

Configure user enumeration protection

To remove the ability to determine whether an account exists:

  1. In the Clerk Dashboard, navigate to the Rules page under Protect.
  2. In the User enumeration protection row, select Manage.
  3. Select Bulk user enumeration protection or Strict user enumeration protection based on your needs, then select Save.

How user enumeration protection works

Bulk protection adds rate limiting while preserving the normal sign-in experience. Strict protection hides whether accounts exist, which provides stronger security but means legitimate users won't get helpful feedback if they use the wrong identifier or misremember their account. Choose strict protection if preventing account discovery is a priority for your application.

Bulk user enumeration protection

Bulk user enumeration protection enforces rate limits to prevent attackers from checking registered email addresses and phone numbers in bulk. Targeted attacks against individual accounts are still possible.

Clerk's existing PII protections still apply. For example, during sign-in, even though bulk protection reveals whether a user exists, Clerk redacts personal information, showing only enough to be recognizable (e.g., "Send SMS code to +********00").

Strict user enumeration protection

With Strict user enumeration protection, users won't receive feedback about whether their identifier matches an existing account until they verify their identity.

Note

Under strict, the <SignIn /> component supports sign-in-or-up: a sign-in for an identifier that doesn't exist proceeds to verification, then transfers to sign-up once the user verifies their identity. Refer to Sign-in-or-up under strict protection for the requirements, including the restriction on password. The Account Portal hosts sign-in and sign-up on separate pages, so it can't transfer to sign-up under any setting — render <SignIn /> in your own app for a sign-in-or-up experience.

Strict enumeration covers:

  • Sign up: If you sign up with an email or phone number that already exists, Clerk won't send a verification code. Instead, it sends a notification (email or SMS) letting the account owner know someone tried to sign up, and suggesting they sign in instead.
  • Sign in: When the account exists, sign-in proceeds normally. When it doesn't, Clerk's behavior depends on the strategy:
    • Password or Web3 wallet: The attempt is rejected without revealing whether the account exists.
    • Email code or email link: Clerk shows the verification screen and sends a notification to the address explaining that someone attempted to sign in to a non-existent account.
    • Phone code: Clerk shows the verification screen as if a message was sent, but doesn't actually send anything.
      • Because SMS can be costly during an enumeration attack, Clerk doesn't send SMS for sign-in attempts to non-existent accounts. We're exploring ways to add SMS notifications as we develop our anti-fraud protections.
  • User profile: If you add an email or phone number to your profile that already exists, Clerk shows the verification screen as if a message was sent, but doesn't actually send anything. Clerk doesn't send notifications in this case, since these attempts are more likely to be attacks than real updates.

Sign-in-or-up under strict protection

Because a sign-in for an unknown identifier proceeds to verification instead of failing, <SignIn /> can create the account after the user verifies their identity, as long as your instance allows public sign-ups. Unlike a standalone strict sign-in — which withholds verification from unknown identifiers, as described above — the sign-in-or-up flow sends a real verification code or email link so a new user can verify and continue to sign-up. No code changes are required. Refer to sign-in-or-up for how to render the flow, and to the signUpIfMissing custom flow to build the same behavior yourself.

Three limits apply under strict that don't apply otherwise:

  • Your instance must allow public sign-ups. In restricted or waitlist sign-up mode, <SignIn /> falls back to a standalone strict sign-in. Sign-up can't succeed under either setting, but strict also withholds the reason: the "you need an invitation" response shown otherwise appears only when no account exists, so it's an enumeration signal that strict has to suppress. An uninvited visitor reaches the verification screen, receives only the notification described above, and isn't told why they can't continue.
  • Password can't be the starting strategy. A password step has no safe exit under strict: a visitor who doesn't have an account is sent to a password screen they can't get past. A visitor who types in an incorrect password can't be redirected to sign up, because Clerk can't reveal whether the identifier they tried matches an existing account. Either disable Password, or set the instance's preferred sign-in strategy to OTP. On development instances, <SignIn /> logs a console warning (sign_up_if_missing_password_preferred) when it detects this combination.
  • Username identifiers aren't supported. Clerk can't contact a user for verification with a username alone.

Password sign-in for accounts created with a social provider

If Password is enabled and a user signs up with a social provider, they'll have an account with no password set. This is true even when password is required at sign-up, because social sign-up bypasses the password setup step.

If that user later tries to sign in with the email on their social account instead of signing in through the social provider, Clerk will prompt them to enter a password. If the user tries entering a password, strict enumeration protection prevents Clerk from revealing that no password is on file so, instead, the attempt fails with the same response as an incorrect password, which can be confusing.

Clerk's prebuilt components handle this in two ways: the sign-in start screen highlights the user's most recently used strategy with a Last used badge when they return on the same device, and the password screen always offers a Use another method option to switch strategies. In a custom sign-in flow, after calling signIn.create(), read SignIn.supportedFirstFactors to detect which strategies the user has, and always offer the social provider as an alternative.

Feedback

What did you think of this content?

Last updated on