My web application issues API keys to clients and I want to hash the API key in my database. However, it seems that if I follow best practices, whereby every hash has a different salt, then I cannot look up a user by their API key in my DB.
So my question is, which hashing algorithm is the "state of the art" in 2023 for this?
Example:
Suppose my user has been issued an api key, my-api-key
. If I used argon2id for storing that password in the database, I have the following hash:
argon2id$19$8192$1$1$7eMA1uCGC2U$aIqS05xbn0DXdXNUKbDG1A
When the user makes an API call, it might look like this (in practice it would not be passed as a query param):
GET https://www.example.com?api_key=my-api-key
I want to be able to say (pseudocode):
hashed_api_key = hash("my-api-key")
SELECT * FROM users WHERE api_key = hashed_api_key
However, each time I call hash()
, a random salt is used, and thus I have a different output:
argon2id$19$8192$1$1$vrDlrC4Qkmo$1lEogx/KgrJyPVS40Xgd+Q
argon2id$19$8192$1$1$D/8tzjThXNo$OsX5f4HjdJM5h5aENFk4DQ
What is the best practice for using hashed API keys for database lookups? Shall I use argon2 without a random salt? Or is there a different best practice?
If you prepend a unique and unhashed prefix to each issued api key, that provides a key to look up the salt. This also allows users to distinguish between keys in a key management ui (as suggested here).
{prefix}.{api key}
However, it may not be worth the added complexity, because, unlike passwords, you can ensure api keys have sufficient entropy, obviating the need for a salt.