Resetting the passwords

In some applications on the web, you are required to log in to view content, post content, or any other sort of thing. With this, there will be times when a member of the site/application/whatever will forget the password they used, and it will have to be reset for them. So there are different methods of doing this, and different ideas behind what should make this work. I am going to give you one that I use, which will use a close to random method for resetting the passwords.

First off, we need to discuss the pros and cons of storing passwords in plain text. Presumably, the passwords are stored in a table in the database. Are they stored plain text, or are they hashed? Does it matter? Maybe it does, maybe it does not. Some of the pros and cons of this are based on the way that the data is used. What does your site do, who looks at the table, what access level do other people in the department have? Does the password allow access to other areas of personal data that people should not have available? These are questions that would need to be answered in a case by case basis. On one project I worked on, it was a local non profit organization who had members from other businesses. Their profile contained only contact info for the members at their place of business. No personal info (ie SSN, credit card numbers, etc) were stored, and any transactions done went to the Pay Pal check out. They wanted the passwords to be emailed if they forgot, so storing the passwords in plain text were fine, and if the data got out, then all they would have is business info for the members. However, in another project, there was going to be personal info, including credit card and transaction history. There would be a team of DBAs who would have access to the table, as well as a team of developers. Certain managers also had access. When the question of password storage came up, there was no question or debate, the passwords would be hashed. So answer this question on your own according to the project.

Second, now that we have answered that question, we need to answer the question of what to do when a user forgets their password. Reset it or send them an email with the password? If you are storing in plain text and an email with the password is what is wanted, then you can stop reading here. You now need to email that password. If not, or the password is hashed, then we need to reset the password. Now we get to the meat of the code that would do this.

*** As a note, do not forget to get business rules/requirements for what kicks this code off. Is just the email enough, or do they also need the username, and possibly a security question? Make sure that is defined to validate who is requesting the reset.

Now, in the function, or object or whatever to reset the password we need to do a couple of things:
1. Find the account to reset
2. Get a random password generated
3. Store it in the table for later use

For the purpose of my long-windedness, and the focus of the code, the query on the table, the email being sent after the password is changed will not be posted here.

1. Finding the account is simple. Based on business rules, do a query to find the account that matches ALL the required input. If that is just username, then find that. If it is email address plus security question, match both items up. The query should return data you will need, like email address, username and/or full name (for personalization of the email), etc. I also request the current password, especially if it is hashed and the modified date (for the day it was last modified).

2. The new password can be based on anything. My random password is generated on a few things, all revolving around the current password and special characters. In this example, the password has been hashed and stored in the table. I am going to take this password, take a substring, and hash it using SHA1:

// Randomize a password based on the current one in three steps
// Take the current encrypted password, substring a random 10 chars, then SHA1 it
$temp1 = sha1(substr($r_user['Member']['password'], rand(1, 20), 10));
// Now get only 5 characters from this new string
$temp1 = substr($temp1, rand(1, 20), 5));

Since the hashed password is always going to be the same length, I am going to be safe with my random parameters. I want to grab only 10 characters from the original hashed password. This will give me some numbers and letters, with uppercase included, and I am going to take that string of characters, and hash it up with SHA1, then grab only a random 5 characters from that. The next step is to get some random special characters.

// Create a string of characters, then randomize to get 3 special characters
$string_spec = "!@#$%^&*()_+"      //12 characters
$temp2 = substr($string_spec, rand(0, 11), 1) . substr($string_spec, rand(0, 11), 1) . substr($string_spec, rand(0, 11), 1);

So that will give me three random special characters. I then need to get the next portion of the reset:

// Take a random 5 chars from the encrypted temp1 string
$temp3 = substr($temp1, rand(1, 20), 5);

Much like the first temp variable, I am just doing the same, only I am taking a substring of the first temp, which was hashed. Now I get the modified day, based on a YYYY-MM-DD hh:mm:ss format. I am going to add up the Month and the Day, then Modulus (or remainder) divide those by 2 to see if it is an even or odd. I do this because I am going to put the special characters at the end of the string, or in the middle, dependent on the modify date.

// For this example $date = the value of the modify_date column in the table
$check = ( substr($date,5,2) ) + ( substr($date,8,2) );
if ( $check % 2 == 0 ) {
	// concatenate the strings with the special chars in the middle
	$reset_pass = $temp1 . $temp2 . $temp3;
} else {
	// concatenate the strings with the special chars at the end
	$reset_pass = $temp1 . $temp3 . $temp2;
}

Now one thing left to do, that is make sure the password is hashed going into the table, so that the user can log back in.

$new_pass = sha1($reset_pass);

3. Make sure that you UPDATE the table with the $new_pass and not the $reset_pass. We will send the $reset_pass to the end user. Also make sure to change the modify_date (if you have that field). Then send the email out upon successful UPDATE.

That is all I do. Seems like a lot, but it really is not. It is more of my explaining why I am doing things that makes it seem long. Will this work for everyone? Maybe not, but it is a step in making sure the new password is somewhat random, has numbers, uppercase and lowercase number, and special characters.

If you wanted to make this more secure, you could also add an expiration date to the reset pass, or check for last reset date, then fire off certain rules if the reset date is too close to the previous reset date. Things like that may help more, or could hurt. Remember that every application is different and what is best for the end user experience will dictate a lot of what goes in the requirements.