Edit to answer actual question
Sorry for misunderstanding your question. That being said, here is an answer. If your goal is to make it so that no one knows what email addresses you have registered on the system (which is a good goal), then the simple answer is that you can't verify before hand if the email address is taken, and then let them know if so. Regardless of what security measures you put in place, you will still have a place where people can come and ask "is this person on your site?". Even rate limiting doesn't really do it, as a malicious attacker might be targeting a specific user. If they already know the person's email address before hand they only have to go to your register form and see if the email is taken. Instead, you are going to have to adjust your registration step:
- You can always validate that it is an actual email address (telling them that the email address isn't an email address will protect against typos and obviously won't reveal anything sensitive).
- Make them type the email address twice: this will, again, help prevent typos, which will be a big aid in the next step:
- Force the user through an email validation process, like I outline below: send them an email with a key that they must use to activate their account. This provides a couple layers of protection. If someone is using your registration form to sniff for used email addresses, they won't find out anything. In all cases your registration form returns a generic message "Please check your email for instructions on how to activate your account". If the email address already exists then, rather than sending activation instructions, you can send a "You just tried to register but already exist in our system. Click here to recover your password. If you didn't try to register, please click this link and then disregard this message" (or something like that). Otherwise, do the normal email validation message.
Since you don't want to tell people if they try to register with an email that already exists, you will get three different people who submit your registration form:
- New people who are trying to register (these people will have no problem at all)
- Old people who are already registered but forgot: your alternate email will help them get in very quickly.
- People who are trying to scrape your email list. They will get nothing at all.
As a result, malicious actors will be shut out and your legitimate users will get through without problem. A good mix between usability and security (IMO).
previous answer: mildly applicable
Proper email validation shouldn't result in someone being able to check for the existence of email addresses in your system. You may need to include more details about what exactly you are doing. That being said, it's always worth explaining what the answer should be:
Email validation should be done by a cryptographically-secure and random key that is emailed to the user. At that point in time validation can be as simple as clicking a link that you send to the user that looks something like:
Things to keep in mind:
- Make sure the validation key is generated with a proper CSPRNG: if the user can guess the key from their input, it is useless.
- The user's email address is not part of the input and is not needed at any step in the validation process. The fact that the user knows the random key, which is only handed out via email, is what verifies that the user owns the email address. As a result, there should be no opportunity for people to scrape a list of email addresses.
- Make sure that your key has enough random bytes that it can't feasibly be guessed
- Normally you would only use
POST requests for making changes, and therefore you want to make sure that this endpoint has CSRF protection. However, that is not necessary here. The reason is that the key itself is sufficiently random that no one but the person with the email should have it anyway. So it's better to make the validation process happen via a simple link and
GET request. Even if the user just has to copy and paste the key from their email to a web form, someone will get it wrong. The way this works is simple enough and secure enough that (IMO) a simpler UI is perfectly reasonable.
- Worth mentioning because I see this all the time: hashes don't add randomness to a string. Use an actual CSPRNG to make the random string. Don't just take some otherwise simple data and hash it. Hashes don't add entropy: they must make it look random.