10

I'm building a website and am trying to figure out a way to encrypt the user's email addresses. It would be nice if, in case my database was stolen, the emails of my users weren't in plain text. I figured the user needs the email to login obviously, so if I encrypt it with their password as the key, or even the email itself, then I could still build a login function, that would work.

Problem comes with situation when I need the user's email address. For example, in a situation as a password reset or sending out a newsletter. I could have a second field in the database with the user's email address, encrypted with a key that I know.

Can I encrypt something, where two keys work? Like a master key..... or am I being stupid?

  • 5
    If you have the user's email address, encrypted with a master key, and you have the master key, why would you also need a separate encryption of it? – user253751 Dec 1 at 17:38
  • Why encrypt each username individually? – schroeder Dec 1 at 17:43
  • 17
    It sounds like you are trying to apply solutions without a problem to solve. First, work out the thing you are trying to protect, then devise solutions. – schroeder Dec 1 at 17:44
  • I agree with most of the answerers and commenters on this page, in that I'm also questioning the reasoning for encrypting users' email addresses. However, if you really want to do this, most RDBMS systems have built-in functions for AES encryption and decryption (in mysql, see AES_ENCRYPT() and AES_DECRYPT). – mti2935 Dec 1 at 22:36
  • @mti2935 - the problem with using (for example) AES_ENCRYPT() on MySQL/MariaDB is that, when encrypting the login field ("the user needs the email to login") then to read/verify it you need to decrypt the entire table and the more users you have the longer that takes. If you're going to encrypt the email address, you're better off using something else (like a plaint text username) to be the login field. – CD001 Dec 2 at 13:34
17

If you only need to verify the email when the user provides it, then hash it, like vidarlo suggests, in the same way you would hash a password. No need for encryption here. The flip side with this approach is that you can never recover the email, even if you really need it (e.g. to contact your users in case of a compromies, as suggested in comments).

If you need to be able retrieve the emails, then encrypt them once with a key you control. No need to encrypt the email twice for different purposes here - just do it once. The tricky part with this solution is where to store the key.

  • If you hash the e-mail address, then you have no way of receiving it back. Perhaps, in an event of compromise, this is what you want or are legally required to do. I'd rather an attacker had my e-mail address and I'd be told this account is possibly compromised than an attacker not having my e-Mail address. And even then, my e-mail is probably easy to guess. – MechMK1 Dec 2 at 7:27
  • I'm not sure hashing provides all that much protection here (though I guess non-zero). If someone steals the hashes and wants to recover the emails, that's probably pretty easy. People don't generally use strings of random characters in their email addresses. – Chris Hayes Dec 2 at 8:16
  • @ChrisHayes My guess is that users in general have about the same amount of entropy in passwords as in emails - a less than great amount. But since emails are less valuable than passwords I would say that the cost - benefit analysis of an attacker works in our favor here. So I agree that it is non-zero. – Anders Dec 2 at 8:35
11

Do you really need the users e-mail address?

If you store e-mail address as hash(e-mail + salt):salt you can trivially verify the e-mail address supplied by the user. If the user requests a password reset, simply verify that the e-mail address matches, and send the e-mail to the user supplied e-mail.

If you want to allow lookups in the database based on e-mail address (username being e-mail for instance), this model will obviously not work. However, as pointed out by Conor Mancone, a global pepper for all addresses will work, but will somewhat reduce the security.

This removes the possibility for you to send newsletters, obviously. But it means that a database breach will not reveal the e-mail addresses.

  • i need to be able to send emails to my users – Jason Dec 1 at 18:41
  • @Jason in which cases would you need to email users? – Ángel Dec 1 at 18:59
  • @Jason do you really need this? Could it be a opt in? If it's a opt-in, it's easier to defend if GDPR is a topic of your concern. – vidarlo Dec 1 at 19:01
  • 4
    I'm not sure if this will work anyway. The issue is that it isn't about verifying the email address but finding the account. Follow through and answer this question: you have 1,000,000 users, each with an email hashed with a random salt and recorded in the database with their email. Someone is trying to login and gives you their email address. How do you figure out which record is theirs? To hash the email to find the record you first need the hash, but to get the hash you need to first find the record. – Conor Mancone Dec 1 at 22:36
  • 1
    If you used one hash for all emails (aka a pepper) then this could work at a cost to security. – Conor Mancone Dec 1 at 22:39
6

An alternative to existing answers: put the emails on a separate machine & application and store only the hashes in your main application DB.

Use the hash for verifying the emails.

When you need to send an email your application will communicate with this other service passing on the hash + email message and the other application will send the email.

Obviously a hacker could hack the secondary machine and gain access to the emails but you are still adding security because:

  1. The secondary machine need not be exposed to the internet
  2. The secondary machine can be extremely minimal, it only has to expose a very limited API to your application and have a service to send emails
  3. It can be locked down a lot since it does only a very specific thing

So the attack surface of the secondary machine can be way smaller than your application's.

Bonus points: you can apply the other answers to your secondary machine, increasing the security even more (although it wont ever be perfect if you assume you are already hacked...)

  • 1
    This is a great addition to the list of options. Welcome! – Conor Mancone Dec 2 at 11:23
2

Logging in requires looking up the email address, which isn't scalable if they're hashed properly – with 10k users in your system, you'll have to hash a user's email on average 5k times on every login, due to salts.

May I question your premise?

"I figured the user needs the email to login obviously"

Why not issue them (or let them select) a username instead? This then won't tie back to their email unless they choose a username very close to their email.

Obviously you still need their email if you're doing a password reset, but you could not offer this, use a alternative method (e.g. a OTP code given at signup), or keep a separate DB of usernames to emails.

  • currently its set up so one can login with either username or email. i would like to keep it that way. reading all the comments to my question, it seem that i might be over complicating things though. – Jason Dec 3 at 20:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.