I am redesigning a password reset email mechanism because the existing implementation scares the hell out of me. My goal is to generate reset codes that are:
- Expired
- Tamper Resistant
- Single Use
- Resistant to replay attacks
The format of the reset code I have come up with is:
B64( B64( iv ) || B64( E( uid || timestamp ) ) )
Where B64
is base64 encoding and E
is for symmetric encryption. The iv
is the initialization vector used for encryption (randomly generated), and is in the clear. The ||
indicates concatenation and I actually use the string "||"
as the separator. The uid
is an opaque identifier of the user so I can look them up in the database.
The timestamp
allows for a code to be expired.
I am using AES 256 in Galois Counter Mode which should provide protection from modified ciphertext attacks.
The encryption key is stored on the server, and used to perform the encryption.
The iv
has a dual use. When the reset code is generated the iv
is stored with the user in the database. This is used to enforce single-use of a reset code (it gets cleared when a reset code is processed) as well as prevent use of reset codes generated previously. If the iv
in the database does not match the iv
used to decrypt the reset code, the request is refused. This may also help with security in the event that the encryption key on the server is somehow compromised, as the attacker has to inject the iv
into the database as well to make it work. Of course if the attacker is on the host we are pretty much toast...
Are there any weaknesses in this design I am not seeing, or does it needs additional improvement?