Handling passwords safely in PHP
(Page 1 out of 2)Introduction
If you're ever going to create a script that involves users or passwords, which is very likely, you'll probably run across security issues with handling the passwords. You can't just store the passwords in clear text in your database, and great care must be used when managing the passwords (for example during login).
In this article I will show you everything that you have to think about when handling passwords in PHP, and how to solve some common problems.
Storing passwords
As I said just now, you can't store passwords in clear text, and they must be encrypted or hashed. The best way to store passwords is to hash them, and not encrypt them. The difference between hashing and encrypting is that with hashing it's completely impossible to get the original value, whereas with encryption it's possible to get the original value (by decrypting the encrypted string).
You should always hash important passwords and other sensitive data, because it means that no-one, not even a system administrator, can retrieve the original data. Of course, if you need to retrieve the original data, you'll have to use encryption, but for a password this isn't necessary.
The easiest way to hash a password is with the md5() function, which comes standard with PHP, and is very secure. It's used like this:
$secure_password = md5($password);
?>
Another standard hashing function that comes with PHP is the sha1() function, which does pretty much the same thing as the md5 function, except with a different algorithm and it's slightly more secure. It's used in the same manner:
$secure_password = sha1($password);
?>
Now it's already much safer to store passwords, but there is still one additional step you can take to really make it secure.
Instead of running the plain password through the hashing function, you should instead run the plain password with another small string, called a salt, through the hashing function, which makes the password really unique. Each user should have his own salt, and which means that each user has a completely unique hashed password, even if their plain password is the same. This also makes it harder to "crack" the hashed password, because it makes brute-force dictionary cracking almost impossible. Here's a simple example of what I mean:
// ... do some form handling, like validation, filtering, etc
$password = $_POST['password'];
// Generate a random salt
$salt = substr(md5(uniqid(rand(), true)), 0, 5);
// Hash password
$secure_password = md5($salt . md5($password));
// Store password AND hash in database
// ...
?>
As you can see in the above example, I used the uniqid() function to generate a random salt, and then used the salt to hash the password.
It's important to store the hash in the database as well, because you'll need it later when the user logs in, e.g.
// ... do some form handling, like validation, filtering, etc
$username = $_POST['username'];
$password = $_POST['password'];
// Get user from database
$user = getUser($username);
// Compare password
if ($user->password != md5($user->salt . md5($password))) {
die ('Wrong username or password!');
}
// ... user entered correct password, do something
?>
February 7th, 2006 at 2:45 am
Nice article. You can set up a SSL certificate without paying for it, but browsers will pop up a warning to users letting them know it is not signed by a trusted authority. Obviously not something you want to use on an e-commerce site, but it would be appropriate for a login to a personal system.
February 7th, 2006 at 8:23 pm
Wasn’t SHA1 cracked a while back?
http://www.schneier.com/blog/archives/2005/02/cryptanalysis_o.html
What about the crypt() function?
February 8th, 2006 at 9:44 pm
The advantage of crypting is that you’ll be able to decrypt the password in order to send it to the user in case he forget it. You can’t do this with hashing.
Mysql function ENCODE and DECODE are nice to do this way.
February 8th, 2006 at 11:13 pm
Bubba: SHA1 wasn’t really cracked, but some researchers found a quicker way to find collisions, but it’s still very secure, and safe to use.
Maxence: you’re right, that’s another advantage of encryption, but it’s still possible to use hashing. When a user forgots his password, the app has to generate a new one, and send it to the user. Not as nice maybe, but it is more secure.
February 9th, 2006 at 5:31 pm
I am new to all this
1. If there is a md5 hash function then is there an inverse of that. I mean if you can generate a hash of a string can you inversley get a string from a hash?
2. Also when the user enters the password and you post it with submit it is sent to your php script. has the password not just been sent to the server ready for for hashing? and if so could a hacker not retieve this as it is sent.
3. What is a secure connection (when IE asks if you want to go to a secure connection etc) and how can I create one.
February 10th, 2006 at 5:53 am
Where to get this md5 lib you showed us in the js?
February 10th, 2006 at 6:28 am
[…] PHPit - Totally PHP » Handling passwords safely in PHP (tags: php security) […]
February 10th, 2006 at 6:37 am
Ok I found one here http://pajhome.org.uk/crypt/md5/ put this with your web docs but you will need to replace the md5() function call for hex_md5().
February 10th, 2006 at 7:35 pm
The md5() lib can be found at: http://block111.servehttp.com/js/md5.js
February 13th, 2006 at 6:24 am
Guardando y manejando passwords en PHP
PHPit tiene un buen articulo con todos los detalles que hay que tener en cuenta para manejar y guardar passwords en PHP.
Tiene detalles interesantes como encriptar los passwords con un modificador unico por usuario, para que no se pueda hacer brute fo…