Slack was hacked. Reading the thread on HN I decided to discuss why hashing only passwords is impractical if attackers get read access to your
users table or entire database.
Because it’s not the only way to hijack your account. reset_token, api_key and other kinds of tokens are usually stored in plain text and can be used as authentication credentials to set a new password or do something bad with account (transfer Bitcoins etc).
Attackers can reset victim’s password, fetch the reset_token value from database they have read access to and visit
example.com/users/password/edit?reset_password_token=VALUE_FROM_DB to set a new password.
Use following approach for all sign-in attempts, API requests, even for session cookies (if you store session_id in the database)
type = password, reset_token, api_key, access_token, session_id etc
user_id = 123, homakov or email@example.com
value = plain text value
- find user by provided
valuewith all the salt/pepper dance (read how to do it properly somewhere else). You need it to slow down dictionary/bruteforce attacks. For other random tokens
md5(token)will be fine. Just don’t store it in plain text
- now compare it with
type_hash column. As a bonus no need for constant time comparison - timing attack is mitigated by default.
Now even if your database is compromised you don’t need to rotate api_keys and other tokens.
Quick update - actually I was very wrong. There is no way to tell if someone hashes tokens with blackbox because they could hash it before searching and there’s also no need for user_id parameter.