11

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?

schroeder
  • 125,553
  • 55
  • 289
  • 326
Jason
  • 219
  • 2
  • 4
  • 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 01 '19 at 17:38
  • Why encrypt each username individually? – schroeder Dec 01 '19 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 01 '19 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 01 '19 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 02 '19 at 13:34
  • CD001, I agree. I would only do something like this if there was another field that uniquely identified each user, such as a username. – mti2935 Dec 02 '19 at 13:48
  • @schroeder isn't the thing the OT is trying to protect the email addresses in case of a compromise of the server? I'm assuming the decryption key (the OT's *master key*) would be on a different system. Or - assuming both information are on the same system - in case the compromise happened due some SQL injection, it would still protect the mail addresses from being exposed if the decryption key is held not in the DB(?) (I'm in no way a sec expert; This might be nonsense.) – DarkTrick Jan 28 '23 at 01:18
  • @DarkTrick usernames and email addresses are classed as public info. So, my comment stands: what problem needs to be solved here? Until that is defined, solutions are a stab in the dark, at best. – schroeder Jan 28 '23 at 13:19

4 Answers4

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.

Anders
  • 65,052
  • 24
  • 180
  • 218
  • 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. –  Dec 02 '19 at 07: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 02 '19 at 08: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 02 '19 at 08:35
  • I was thinking of a divided system: One that contains the business logic (so your main functionality) and one for communication purpose only: Input: encrypted mail + text content, output: actually send the mail. This system would hold the private key for decryption. I like to believe this system would be more difficult to compromise, as its interface and functionality is extremely small and simple. Does that make sense? – DarkTrick Jan 28 '23 at 01:15
  • @DarkTrick Can't speak to the details here, but in general it is a sound strategy to minimize the attack surface and only have sensitive information accessible where you really need it. – Anders Jan 28 '23 at 07:22
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.

vidarlo
  • 14,890
  • 2
  • 43
  • 56
  • i need to be able to send emails to my users – Jason Dec 01 '19 at 18:41
  • @Jason in which cases would you need to email users? – Ángel Dec 01 '19 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 01 '19 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 01 '19 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 01 '19 at 22:39
  • @ConorMancone Good point. I was thinking of login with a nick/username when I wrote this. A pepper should, as you write, allow you to do this with e-mails, at a slightly reduced security :) This, however, applies to encrypting the identifier as well. If you want to look it up in a database, you'll need deterministic encryption. Thank you for the comments :) – vidarlo Dec 02 '19 at 07:16
  • i appreciate all the comments to my question. my database is stored on a different machine than the website. so i guess if i store the key(and only use one, that i am in control of, not the users) on my website server, that is at least a bit added security if someone breaks in to "only" the database server. (am using a dbaas) – Jason Dec 02 '19 at 13:12
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...)

Giacomo Alzetta
  • 161
  • 1
  • 4
  • 1
    This is a great addition to the list of options. Welcome! – Conor Mancone Dec 02 '19 at 11:23
  • I would go for "keep a private key on the second machine to decrypt mails from the main machine". That way you don't keep a full list of clear text addresses (is this actually more secure?). – DarkTrick Jan 28 '23 at 01:24
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 03 '19 at 20:57