0

I'm trying to help anonymise users but still give them some controls. So this is different from say, anonymising a data set where you never need to go back to the original users.

Let me give you an example. I want a user to come in with his or her email address to log into my system, but I don't want to store the email address. I want to assign that user a UUID that I will then use. However, if the user loses the random identifier they need to be able to re-enter their email address and then get back into the system.

The simplest answer to this is a hash. Of course I could store the hash next to the UUID. If they lose the UUID they can come back and I can re-hash the password. On the other hand, if my system is broken into, the bad guys can simply do a dictionary attack on a list of email addresses and then re-identify the UUIDs.

Firstly, is there a standard way of doing this? bcrypt and PBKDF2 pop to mind, but I obviously can't store a tuple of <email, salt, iterations> without making the intruders job even easier.

I don't like to invent new security stuff, but I have had one idea. Basically I store <SHA512(email), salt, iterations> and then I store <PBKDF2(email, salt, iterations), UUID>.

That way they have to first dictionary attack the first table, and then use the results of that to do individual dictionary attacks on each row of the second table, which should slow things down.

11
  • What are you trying to protect?
    – Skyl3r
    Commented Jul 7, 2016 at 17:53
  • 2
    First of all, asking users to remember a UUID would be bad for usability of your solution. Secondly, can you tell us what is the risk associated with attackers identifying the UUID?
    – Limit
    Commented Jul 7, 2016 at 17:53
  • The behaviour of the user could be tracked via the UUID. For example, I might store a history of web interactions that includes the UUID. I'm trying to make it so that if someone attacks the site, it is very hard for them to then figure out who did what. Its probably a little paranoid, but that is a good thing right? Commented Jul 7, 2016 at 18:07
  • one can't "simply do a dictionary attack" on bcrypt in a reasonable amount of time.
    – dandavis
    Commented Jul 7, 2016 at 19:22
  • 1
    @LieRyan: that assumes the attacker already knows which 7 billion email addresses to try, not very realistic, and even then it takes a year with hundreds of computers (>$25k) and lots in electricity (~$20k). If it's worth about 50 grand and that much effort, I can't help but think that someone would just beat the info out of you instead.
    – dandavis
    Commented Jul 9, 2016 at 8:10

2 Answers 2

1

Keep things simple, if you want anonymity, don't use email for logins.

Make your user generate an asymmetric key pair to authenticate to your system, call this the login key.

When the user want to post a message to your system, the user generates a new asymmetric key pair, call this the data key. The user would then sign their message with the data key and encrypt the private data key with their public login key addressed to themselves. The user would submit the encrypted private data key, the public data key, and the signed message.

To edit the posted message, your user downloads the encrypted data key, decrypt it with their private key. And then they sign the message with the private data key. Your system knows that the new message are generated by the same user as the original, because both messages are signed by the same public data key.

Be careful:

  1. many implementations of asymmetric cryptography attaches the encrypting key's ID to an encrypted message, to make it easy for the recipient of a message to identify which key to use to decrypt the message. You want to make sure you encrypt the private data key without adding these metadata.

  2. an attacker that manages to observe who downloaded which encrypted private key would be able to observe which user downloaded which key and make reasonable guess as to who is doing what. You can prevent this by forcing all users to download all encrypted private keys (or a reasonable chunk of it).

Basically I store <SHA512(email), salt, iterations>

If you're storing the SHA512 of the users' email, wouldn't identifying the users be as easy as creating a SHA512 rainbow table?

12
  • "If you're storing the SHA512 of the users' email, wouldn't identifying the users be as easy as creating a SHA512 rainbow table?" => No, because he's using a salt.
    – Nathan
    Commented Sep 7, 2016 at 8:08
  • 1
    @Nathan: salt makes little difference here. Password is intended to be a secret, but email address, you give this to everyone you meet, you post them on websites, publicly on mailing lists, and SMTP sends them as plain text. Any anynomization strategy need to take into account that the possible key space of email address is much smaller than possible key space of passwords, so reversing email hashes is much easier than reversing password hashes.
    – Lie Ryan
    Commented Sep 8, 2016 at 4:13
  • It makes all the difference: you can't use a rainbow table if the plaintext being attacked is salted. It might be a reduced keyspace before being salted but when you salt your email address before hashing it then the keyspace is now however large you decide to make your salt (+ the the original email keyspace)... So, the simple fact is that salt = no rainbow table. If you believe otherwise, then you need to go learn about rainbow tables, how they're built and their space/time tradeoff.
    – Nathan
    Commented Sep 8, 2016 at 11:11
  • 1
    @Nathan - Salt makes little difference because the search space of email addresses is small enough that you don't need a rainbow table. It is particularly annoying that you've made condescending comments when you are peddling misleading information. No wonder Lie Ryan didn't bother to correct you.
    – paj28
    Commented Oct 7, 2016 at 9:16
  • 1
    @paj28 - No problem - and yes, the conversation is at a bit of a tangent to to the original question. Lie Ryan kind of chucked the rainbow table comment in at the end: "If you're storing the SHA512 of the users' email, wouldn't identifying the users be as easy as creating a SHA512 rainbow table?" - and that's what I responded to... his response was that "reversing email hashes is much easier than reversing password hashes" which is patently incorrect when the plaintext is correctly salted... hence my response which may have come off as condescending... but as you say, it's correct.
    – Nathan
    Commented Oct 7, 2016 at 12:16
0

If the user loses the random identifier they need to be able to re-enter their email address and then get back into the system.

To acquire that feature, you have no choice but to store a hash of the email address. Also the Salt cannot be secret because the user has already forgotten everything except their email address.

Email Addresses are Low-Entropy like Passwords, so use a (Slow) Password Hash

  • BCrypt is commonly recommended, but be sure to run a quick SHA-2 hash on the input data, so super-long input will not be truncated by BCrypt
  • PBKDF2 but that is less GPU-resistant because it has lower Memory requirements.
  • SCrypt is better than BCrypt if you have a high enough work factor. Otherwise it is worse against GPUs. (again, because of higher or lower Memory requirements)
  • The winner of the Password Hashing Competition may be even better than the aforementioned, but has not yet stood the test of time, so don't use it just yet. It's called Argon2, and has separate Work Factor settings for CPU time and Memory load. (nice!)
  • Repetitive SHA-2 can be used instead of PBKDF2 (still not GPU resistant), but this is more tricky to implement the repetition efficiently (i.e. to be brute-force resistant) because SHA-2 is actually a General Purpose (Fast) Hash.

Most of these options generate random Salt by default, but you should verify whether this is the case!

It is best to include some Pepper (~72 bits of entropy) before the Input prior to hashing. The Pepper can be the same for all your users, but should be stored in a file outside of the database so that component cannot be found via SQL Injection.

Make sure your Work Factor requires about 100ms (with appropriate DoS protection) on your target hardware (knowing that attackers will use faster hardware for Brute force)

Keep in mind that very simple email addresses might be brute-forced anyway, so don't let the hash get stolen! Also relevant, is that an attacker could quickly check for presence of a particular email address.

It would be wise to look at other security precautions you can make such as Separation of SQL Database Users to contain potential SQLi attacks. Also make sure your system is secure overall.

I don't like to invent new security stuff, but I have had one idea. Basically I store <SHA512(email), salt, iterations> and then I store <PBKDF2(email, salt, iterations), UUID>.

There's no benefit to adding complexity here. If you want the attacker to have to do more work, just raise the Work Factor and use Pepper as described.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .