I think I have a basic understanding of security concerns, but I feel like if I don't know everything, I'm going to implement lousy solutions. Below is my current strategy; Have I overlooked obvious best practices?
I've read Can anyone provide references for implementing web application self password reset mechanisms properly? and think that I have the relevant parts covered.
Flow
- An administrator triggers a password reset for a user in the system.
- The system locks out the user from the system, generates a 40 characters random alphanumeric string as a authtoken, and stores this hashed in the database with SHA1, together with the encrypted username, the time is was created and the hash of the old password. If the user have any previous authentication tokens in the db, it deletes them.
- The system sends a link to the email connected to the user having it's password triggered for resetting.
- The user clicks the link, which is over https, and the system reads the authtoken.
The system hashes it again with SHA1, searches the database for it:
- The authtoken does not match: Error page.
Please contact admin
- The authtoken was created more then 4 hours ago:
Please contact admin
- The authtoken is found and active: Goto 6
- The authtoken does not match: Error page.
The client gets presented a dialogue to select a new password and posts the new password with the same authtoken (in a hidden field) back to the server.
- The system checks again that the authToken exists, is active and that the password is not the same as the old one. If success, the system decrypts the username stored together with the authtoken, changes the password for this user to the new, unlocks the user, and removes the authToken from the database.
- The system redirects the user to the login page.
So
The only agent triggering the reset process is an authenticated administrator: No problem with maliciously locked-out users. All communication between client and server is going over HTTPS, with the possible and uncontrollable exception of the user's email solution.
This is how the authToken is generated:
const string authtokenAlphabet = abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ1234567890_-.";
const int authtokenSize = 40;
var authToken = string.Empty;
while (authToken.Length < authtokenSize)
authToken += authtokenAlphabet[_random.Next(0, authtokenAlphabet.Length - 1)];
Although I use the Random
class, which is discouraged due to less than optimal randomness, would this really be a problem considering that only an an authorized and authenticated administrator can trigger the reset?