Pattern: Password Reset
Problem: A user has forgotten her password. You need to generate a password reset token to send in an email to confirm her identity before allowing her to establish a new password.
Context: You are developing a web application requiring user password authentication and using a password salt and hashing algorithm to store passwords. You are reluctant to create a random nonce for your password reset token, since this needs to be stored in your database. But this seems inefficient; in most cases, the user object doesn’t need to hold a nonce, but this seems like such a trivial problem to create an entirely new password reset nonce table to associate with the user object.
Solution: You can generate a unique password reset token by hashing the internal state of the user object, including the user’s password salt and password hash. Because you are including the password salt and hash as part of the hash to produce the reset token, the reset token has the following properties:
- It can be computed only by the server (the password salt and hashes should not be externalized),
- It can be computed at any time by the server (it does not need to be stored in your database),
- It is constant until the user changes their password (i.e., an attacker cannot cause it to be invalidated), and
- It is guaranteed to change whenever the password is actually changed (since the user object’s internal state change from the password update will cause the reset token hash to change), so that an attacker who later discovers the token cannot exploit it.
If you desire to limit the viability of a password reset token to a certain period of time (e.g., 24 hours), you can include an expiration timestamp in the token and also as input to the hashing operation:
reset_token = timestamp + hash(timestamp + user.password_salt + user.password + ...)
By including the timestamp in the token, you provide an indication to your application when the token expires. By including the timestamp as input to the hash portion of the token, you ensure that it is not possible for an attacker to take a stale token and manufacture a valid token.