I have users for which I use a SHA-1 hash as an API key. These are urandom feed into SHA-1 so you can assume they're fully random. There is no username sent along with the API key. I don't want to store the API keys in plaintext, if my server is compromised, but I can't use a salt because the username is the password here. SHA-1 hashes are 20 bytes, that's 2^160 possibilities.

In normal password authentication schemes you may have a user, Frank. Frank logs in with his username and password. With the username it is possible to look up Franks salt and hash in the database.

I can't store a salt because I don't know what row the SHA-1 hash corresponds to. I can only hash it and see if it matches something. If I randomly store a salt along the hash, how will I know which salt belongs to which plaintext API KEY?

scrypt or bcrypt without a salt are vulnerable to rainbow tables attacks, right? Is there a work-factor high enough that I don't need to worry?

  • $\begingroup$ Personally I'm so confused about your question here that I don't even know what to ask. Maybe you can give an example of your current database entries? And maybe you can explain why you hash the name of your users? If you can store a hash of a password, you can store a salt with it, right? $\endgroup$
    – Maarten Bodewes
    Sep 29 '16 at 20:31
  • $\begingroup$ I apologize, basically this isn't a username/password scheme. It is just an API key that has no mapping to any user or password, and I don't want to store it in plaintext. $\endgroup$
    – David
    Sep 29 '16 at 20:47
  • $\begingroup$ Made extensive edits to make the question more clear. Please edit if I got anything wrong. $\endgroup$
    – Maarten Bodewes
    Sep 29 '16 at 20:58
  • $\begingroup$ @David, Okay, so it's hashes of random data (not e.g. usernames). But why the hash then in the first place, instead of just using the random keys as-is? $\endgroup$
    – ilkkachu
    Sep 29 '16 at 22:42
  • $\begingroup$ Legacy reasons, before I arrived at my company :) $\endgroup$
    – David
    Sep 29 '16 at 23:07

Well, first let's clarify what you are asking, what I understood is: You have some system that takes key as only validation data. This key serves as password, but there is no username, so you simply check if given key is in authorized list.

The answer to your question is fairly simple: assuming that your keys are truly 160-bit random, and you don't have millions of users, then your security is still well over 128-bit security. This is much better than traditional username-password setup. Of course if key gets leaked then it won't be secure. You additionally hash it (once more, if i understand), which makes it secure against reading data off server.

Simply said: There is nothing to do unless you have trillions of users. No scrypt/bcrypt is neeeded, only one hash. 160-bit password is secure even without username and with thousands of users. However, all that is assuming you use TLS. Otherwise, your protocol is as simple to hack as being MitM when someone sends key.

  • $\begingroup$ Yes, my main concern is, if someone breaches my server and steals all my API keys: how can I store the hashes of the API keys? I'm guessing I use scrypt or bcrypt, which work-factor do I use? Aren't I still vulnerable to rainbow tables b/c I can't use a salt? $\endgroup$
    – David
    Sep 29 '16 at 20:53
  • $\begingroup$ You only need to hash it once before using it to lookup/store in database. Then attacker won't be able to figure out what client has (what was before hashing). bcrypt/scrypt isn't relevant when your key is 160bit. We only use those and work-factor when keys aren't sufficiently long (i.e. passwords, since users are weak points). $\endgroup$
    – axapaxa
    Sep 29 '16 at 20:56
  • $\begingroup$ This makes me quite happy, I shouldn't worry about rainbow tables or an attacker iterating over 2^160 unsalted hashes because 2^160 is so huge? :) $\endgroup$
    – David
    Sep 29 '16 at 20:57
  • $\begingroup$ Yes, we assume that 2^128 is too big for offline attacks. 2^160 gives you some offset for multiple users you have. This all assumes that your keys are indeed truly random. Only thing then to do is hash it once, to prevent attacker getting your keys when server compromise occurs. Also: Sha-1 is weak and you should replace it with something else, but you can still truncate output to 160bit. $\endgroup$
    – axapaxa
    Sep 29 '16 at 21:00
  • 1
    $\begingroup$ There is no purpose whatsoever in feeding the output of /dev/urandom to SHA-1 for this scheme. The output of /dev/urandom is already random. I strongly recommend trying to find a trusted library for your language that does this sort of thing out of the box, instead of rolling your own. Authentication systems are hard to get right, and there's no reason to reinvent the wheel. $\endgroup$ Sep 29 '16 at 21:26

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.