For developers
Designing a signup form that does not leak who is registered
18 July 2026 · 2 min read · TMailr
A signup form has to tell a legitimate user what went wrong and tell an attacker nothing about who is already registered. Those pull in opposite directions, and the default behaviour of almost every framework picks the wrong side.
The leak is the error message
That address is already registered is friendly and it is an oracle. Anybody with a list of addresses can find out which of them have accounts with you, one request at a time. For most services that is mildly bad. For a dating site, a health service or a political organisation it is the whole risk.
The fix is to say the same thing either way and move the difference into the mail. Accept the signup, then send one of two messages: welcome, confirm your address, or somebody tried to sign up with your address and you already have an account, here is how to sign in. The user who owns the mailbox is helped in both cases, and the person at the keyboard learns nothing.
The other oracles
- Timing: if a taken address returns in 40ms and a free one in 300ms because it sends mail, the timing is the answer. Send asynchronously so both paths cost the same.
- Status codes: 409 for taken and 201 for created is the same leak in a different field.
- The password reset form, which usually leaks what the signup form was carefully protecting.
- Rate limit messages that only appear for real accounts.
Validate without being clever
Refuse addresses that cannot receive mail, and be careful that your idea of cannot is right. A regular expression written from memory rejects real addresses: internationalised domains are real, plus signs are real, and long top-level domains are real. Turning away a paying customer because your pattern predates the last decade of DNS is a worse outcome than accepting one bad address.
What is worth checking is the shape and the domain: syntax, whether the domain resolves and has an MX, and whether it is a known typo of a large provider. Offering did you mean gmail.com saves more failed signups than any amount of validation.
Confirm before you rely on it
Until somebody has clicked the link, the address is a claim. Do not send anything else to it, do not use it to identify the account, and do not let it be added to a mailing list. This is also what stops your form being used to mail-bomb a third party: an unconfirmed address gets exactly one message.
The short version
- One response for taken and free, in body, status and timing.
- Move the difference into the message you send to the mailbox.
- Validate the shape, not your memory of the rules.
- Nothing depends on the address until it has been confirmed.
- Cap confirmations per destination, uniformly, so the threshold reveals nothing.
More on for developers