4

In my system each user can have multiple api keys. I want to hash api keys and store in a database their hashes. I'm using comeonin for this.

1) is it sensible to store hashes of api keys rather than their plain, original values?

2) when an api request comes in, there's only a plain api key value in it and no user email along with it -- this is my system is designed.

How should I check if an api key is valid? Will I have to do this -- recalculate a hash?

given_api_plain_key = get_key_from_request()

# re-hash it again
# but how about the original salt???

given_api_hash_key = Comeonin.Bcrypt.hashpwsalt(given_api_plain_key)


case Repo.get_by(ApiKey, key_hash: given_api_hash_key) do
  nil -> IO.puts("not found")
  a -> IO.puts("gooood")
end

Or is there a better way?

Jily
  • 39
  • 1
  • 1
    bcrypt is a great solution for passwords: because passwords tend to have low entropy, bcrypt slows down brute force attacks by making the hash computation slow. But API keys can have high entropy, and therefore brute force attacking is not such a risk. Conclude: a much faster function such as SHA256 may be more suitable for your design. – TheGreatContini Aug 16 '17 at 01:34
  • @TheGreatContini, ok, that's a part on an answer. – Jily Aug 16 '17 at 01:44
  • Following up on @TheGreatContini's comment, I don't see any need of hashing at all as long as you're generating the API keys and they're long and random. See also https://security.stackexchange.com/questions/18572/is-it-okay-for-api-secret-to-be-stored-in-plain-text-or-decrypt-able. – Dogbert Aug 16 '17 at 08:03
  • Thoughts on hashing API keys: https://stackoverflow.com/questions/43173877/should-api-secrets-be-hashed – TheGreatContini Aug 16 '17 at 20:49

1 Answers1

4

(1) is it sensible to store hashes of api keys rather than their plain, original values?

Your goal seems to be to protect against grand compromises that might happen if somebody gets access to your database (for example, via SQL injection, command injection, or reverse shell on your system). In this case, yes it is sensible, especially if each user has a different API key. However this link is worth reading for other considerations that might affect your decision.

(2) How should I check if an api key is valid?

It is clear that you need to hash the input and see if matches to something in your database.

(3) Implementation.

You do not want to apply the same protection that you use for passwords. Passwords tend to have low entropy by nature, and therefore we need tools like bcrypt to process them. Bcrypt is slow by design (to prevent brute force attacks), and uses salts to help the security of poorly chosen passwords.

API keys should not have low entropy, and therefore you do not need slow functions to process them. In fact, slow functions bring DoS risks, so you definitely do not want to do a bcrypt every request that comes in. Salting also complicates your use case (salts work when you know who is providing the input, but in your case you do not know who it is coming from beforehand).

Short answer: Just use SHA256, no salt needed.

TheGreatContini
  • 6,429
  • 2
  • 27
  • 37
  • According to this, https://stackoverflow.com/a/9316461/719689, SHA is one-way encryption with no decyption mechanism. So what is the point of storing an API key that way? – AlxVallejo Oct 31 '18 at 14:35
  • @AlxVallejo the point of storing hashed keys is to prevent the keys from being trivially exploited in case the stored data leaks. With high-entropy input, such as proper API keys, it would be very difficult to restore the original API keys if they are hashed with a good algorithm. Validating them is fairly low cost (get the key from caller, hash it, query DB for hashed value). – MasterAM Jul 27 '23 at 09:21