/PHP/ Store and retrieve passwords

25/07/2006 | Filed under Discover > Security / Develop > PHP

Think your data is safe? Paul Hudson suggests you think again…

Poor security can be fatal, as Mary Queen of Scots would tell you if she were still alive today (although she’d probably only speak French so you might not understand her). Along with some Catholic noblemen, she plotted to overthrow the rule of her cousin, Queen Elizabeth, in what has become known as the Babington Plot, after the lead conspirator. Mary exchanged letters about the plot with Babington using hollow beer barrel stoppers, with each letter kept safe from prying eyes by the use of a substitution cipher made up of 59 symbols representing letters and common phrases.

Unfortunately for Mary, Sir Francis Walsingham – Elizabeth’s spymaster – intercepted each barrel, extracted the letter, decoded it, and replaced it so the conspirators were unaware of his intrusion. Found guilty of treason, Mary was sentenced to death, and her 14 co-conspirators were hanged, drawn and quartered.

Most of you – we hope – won’t be working in such life-anddeath situations, but we all know that securing your customer data is the number one priority of any online business. If you’re storing your passwords in plain text, you’ve got several big problems to overcome. First, anyone with the most basic network sniffer can watch your web packets whizz by along with user passwords as needed. Second, anyone with access to your databases – and this is probably many people, if you’re using a shared host solution – can simply read the passwords straight from your tables. Third, anyone with access to your backup tapes/ discs/servers (particularly if you’re using an online backup solution) can extract passwords just as easily as if they had the databases locally.

If you’re already using HTTPS then your data is safe during transfer, but completely vulnerable in storage – and we can use PHP to fix that. The quickest and easiest way to protect passwords is using a hash, which you can think of as being oneway encryption; what goes in, can’t come back out. If you’re thinking this is useless, think again – it’s a highly secure way to save passwords when you don’t actually want to know what the password is in plain text. For more advanced protection, PHP has a complete encryption system capable of locking out even the most capable hackers.

Please keep in mind that encryption is only as good as your implementation. A couple of years ago, WinZip incorporated AES encryption into its product, and branded it as a huge step forward for security. Sadly its implementation was poor, and could be hacked in minutes – just using the AES buzzword didn’t save them. As Bruce Schneier said: “cryptography is hard, and simply using AES in a product does not magically make it more secure.” So, while our implementation is quite strong here, don’t knock it: it’s better to be safe than sorry!

Both hashing and encryption have their uses: hashes are very fast to do, require very little code and are extremely hard to break. Encrypted text is two-way (meaning that you can get the plain text back from the cipher text) but is CPU-heavy and if someone discovers your encryption key then they can read all your data. PHP has built-in support for hashes, whereas it requires the mcrypt library to be able to do encryption. If you’re on Windows, remove the semi-colon from the ;extension=php_mcrypt.dll line in your php.ini file. On Unix, compile your PHP with --with-mcrypt.

1. The easiest way to secure your passwords is using a hash, with the most common hash being MD5 (MD is short for Message Digest). When you pass a string into the md5() function, you’ll get a 32-character hash back that uniquely identifies that string.

There are several important things you need to know about hashes. First, a given string will produce the same hash every single time; the hash doesn’t vary. Second, a slightly different string – even just capitalising one letter that was lowercase before – will produce a vastly different hash value. Third, no matter how long the input string is, the output hash will always be a fixed size. In the case of MD5, this is 32 characters. Fourth, it’s very hard to find another string that will generate the same hash value as your input string. When this happens (if “hello, Mary” and “hello, Bob” were to generate the same hash value, for example), it’s called a ‘collision’, but the chances of finding a collision with MD5 hashes is terrifically small – approximately one in 340 billion billion billion billion billion billion billion billion billion billion. So quite tiny, then.!


<?php
$string1 = “Hello, world!”;
$string2 = “Hello, World!”;
$string3 = “abc”;
$string4 = “This is a very
long string oh yes it is”; echo md5($string1), “\n”;

echo md5($string2), “\n”;
echo md5($string3), “\n”;
echo md5($string4), “\n”;
?>

That will generate the following output:

6cd3556deb0da54bca060b4c394798
39
65a8e27d8879283831b664bd8b7f0a
d4
900150983cd24fb0d6963f7d28e17f
72
33ee255a4220346f442efd09325642
06

If you didn’t see the source code, it’d be impossible to tell which hash came from which string – that’s the beauty of hashes.

Now, the problem with MD5 is that it has now been broken – researchers can generate an MD5 collision in a little over an hour using a very powerful computer. This isn’t a huge problem if you don’t expect legions of hackers to band together to crack your data, but if you want to be extra sure, there’s an even stronger hash system called SHA1. This stands for Secure Hash Algorithm, and was developed by the National Security Agency in the US for governmental use. This works in the same way as MD5, except it generates a 40-character hash. That might not sound much stronger, but behind the scenes it’s the difference between a 128-bit hash value (MD5) and a 160-bit hash value (SHA1), with each extra bit effectively doubling the time it takes to do a brute-force hack. So, what takes researchers an hour to crack in MD5 would take them approximately four billion hours using SHA1.

SHA1 is not perfect. Four billion hours is the best case scenario, where a hacker has to try every combination. In reality, they’re likely to find it in about half that time using brute force, and a lot less time again if they use a more sophisticated attack. Don’t let that put you off: SHA1 is more than secure enough to protect you against everything except large governments. And you don’t have anything to hide from them, do you?

2. Now you know how hashing works, we can knock together a quick password authentication system that protects passwords using SHA1. What we need to do is accept user input from a form (their username and password), hash the password, then check it against the database of hashed passwords to see if there’s a match. Remember, a given string (in this case, the user’s password) will always generate an identical hash value, so we store hashes in the database and check them against hashed user input. Once the user authenticates successfully, we’ll want to store their user ID as a session value so we know who they are. Here’s how it looks in PHP:

<?php
if (isset($_POST
“username”])) {
$user = $_POST [“username”];
$password = sha1($_POST [“username”]);
$result = mysql_query
(“SELECT ID FROM users WHERE
Username = $user AND
Password = $password ;”);
if (mysql_num_rows ($result))
{ extract(mysql_fetch_
assoc($result), EXTR_ PREFIX_ALL,
“user”); $_SESSION[“USER_ID”] =
$user_ID;
echo “Authentication was successful!”;
}
else { echo “Authentication failed.”; }
} ?>

You’ll need to set up the users table yourself – Username should be a VARCHAR field of at least 15 characters (some people like really long usernames!), and Password should be a CHAR field of exactly 40 characters.

3.Another thing hashes are good for are for verifying that files have been downloaded properly. Any Unix-based computer (probably Linux) will come with the program md5sum, which takes a filename as its parameter and returns the MD5 hash of that file. This essentially treats the entire content of the file as a string, which means that if the last part of the file was corrupted during download it will have a different MD5 sum to the original.

In practice sites that offer large downloads, such as Linux distro DVD ISOs, which are usually at least 2GB, provide MD5 sums for the ISO so that downloaders can check they have received the ISO properly and not some corrupt copy, or worse, some virus-ridden replacement a hacker managed to insert on to the server. Rather than making you convert a file to a string then hash it yourself, PHP provides the md5_file() and sha1_file() functions, which take a filename as their only parameter and return the hash of that file. For example:

echo md5_file(“fedoracore5.
iso”);

Yes, sha1_file() does make longer hashes that are much more secure, but in this function it’s not about security, it’s just about checking that a file is safe.

4. The last thing we’re going to look at is proper, two-way encryption. Unlike hashes, ‘real’ encryption enables you to retrieve the original password from the database in plain text. In practise, the difference is this: when someone forgets their password, you need to reset it to a random password if you’re using a hash, but with encryption you can just email them their password.

This is where it gets hard: if you’re going to do encryption, you need to do it properly. That entails selecting an encryption algorithm and block cipher mode, producing an initialisation vector, generating a key, then performing the actual encryption. Encryption algorithms are the meat of this process: they decide how strong the encryption process is. For this example we’ll be using 256-bit Rijndael, also known as the Advanced Encryption Standard (AES). It can easily be argued that other encryption algorithms are stronger than Rijndael (if you want the strongest possible, try 256-bit Serpent), but the advantage of Rijndael is that it is the standard for encryption as adopted around the world. If some day it gets hacked, you don’t have to worry that you chose an unusual algorithm – no one ever got sacked for choosing a standard.

The Block Cipher mode decides how individual chunks of your plain text get encrypted. The most popular system is Cipher Block Chaining (CBC), which splits your plain text into chunks, encrypts the first block, then uses the plain text from the first block to XOR against the second block of plain text, and so on. The advantage of this over simply encrypting each block individually is that it makes duplicate chunks of plain text look different. Finally, initialisation vectors (IVs) are there to whiten plain text – to make it look more random.

To create a properly random encryption key, we’re going to use a sha1() hashed string chopped to the exact size required by AES. With that in mind, here’s a working example of encryption using PHP:

<?php
$td = mcrypt_module_open
(MCRYPT_RIJNDAEL_256, ,
MCRYPT_MODE_CFB, );
$iv = mcrypt_create_iv
(mcrypt_enc_get_iv_size ($td),
MCRYPT_RAND); $key = substr(sha1
( frosties ), 0, mcrypt_
enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
$ciphertext = mcrypt_ generic($td,
See you at 9pm );
mcrypt_generic_deinit ($td);
mcrypt_module_close($td); print
trim($ciphertext) . “\n”; ?>

To decrypt, you need all the same code, with the only exception being that you use mdecrypt_generic() instead of mcrypt_generic(). Yes, you do need to open deinit() and close() mcrypt before you decrypt. Yes, that’s an awful lot of code to do a very simple task, but it’s bulletproof – as long as your key and IV remain secure. Hopefully, we can leave that much in your capable hands.

 

Comments

Garrett / 07/09/2006 / 18:15

I have forgotten my password to a hotmail account. After useing my "secret question" option and resetting my password is there any way of retrieving the original password? I use a different computer as well.

Oriol / 21/09/2006 / 16:44 / http://www.dilc.org

No, it's not possible, since passwords in Hotmail are hashed, and as the article says: "...oneway encryption; what goes in, can’t come back out"

Matt / 01/11/2006 / 12:07

Hello, thats WAY over my head. I've a Lexar Jumpdrive which is 110% mine and had it for a few years now but have completely forgotten my password and now am trying to hack into the drive so I can gian full use of the drive, I've got 7mb which is less than 5% of the drive. I didn't uinderstand any of that up there do I have any alternatives to get my drive back ?

Thank you for your time.

mike hall / 07/11/2006 / 11:39 / http://www.mypill.co.uk

Thanks for a great article, I'm just about to launch a new business site that will keep and use customer data, this tutorial on PHP has sold me on using it. I'm an old programmer who has never used PHP, but the text was very clear, thanks again.

mark / 20/11/2006 / 07:11 / http://free-playstation-3.blogspot.com/

Thanks! Didn't know too much about sha1, but I'm gonna try to use it now.

veppa / 28/11/2006 / 16:48 / http://www.veppa.com

Great tutorial, always wondered about file hashes, didn't know how they are used. and to way encryption is really helpful, but very tricky in case you move your host and someone or yourself change encryption key, then it will be disaster for all registered users.

Gareth Heyes / 08/02/2007 / 15:39

This article contains SQL injection vunrabilites and shouldn't be trusted for reliable information

I've found a better tutorial on this blog:-
http://www.thespanner.co.uk/?p=16

susan shak / 27/02/2007 / 17:38 / http://www.squidoo.com/lensmasters/seoguy

im not sure i believe you gareth

NoWay! / 26/03/2007 / 16:57 / http://your-free-iphone.com/

Interresting, I might use that someday..

Shaun Morgan / 08/04/2007 / 19:45 / http://Dating.ShaunMorganOnline.com

Great article. I've been using md5 Didn't know much about sha.

Carl / 17/04/2007 / 01:45 / http://digitalgreenlight.com

Gareth is right Susan, the 2nd bit of code has an injection vulnerability where it SELECTs the username using $user. To fix it you would need to run mysql_escape_string on it, and anything else you will be showing to the database.

But of course you just wanted to spam to your squidoo page to make some money, just like Mr Free Iphone.

Gareth Heyes / 27/04/2007 / 12:26

Actually I'm not trying to make money, just to point out that the article is flawed and I can provide better information.

Carl / 14/05/2007 / 22:38 / http://digitalgreenlight.com

@ Gareth

I didn't mean you were trying to make money, I meant that Susan was leaving an empty comment just so she could spam her site. She probably just wanted a link, so she left a comment that wasn't obvious spam. (Just like a few of the other comments on this page.)

Your link wasn't spammy at all, and you have a good eye to spot that kind of stuff in other people's code. :)

Yedhu / 17/05/2007 / 06:33

how to control filessize in php?

David / 21/05/2007 / 16:36 / http://www.girlznight.co.uk

Good article. I've got a problem now where I've got a load of old md5 passwords stored in my database for customers and no way of retrieving them for them if they forget :-( Not sure whether to change now, or just generate a random reset password.

frEE / 22/05/2007 / 16:44 / http://www.your-free-iphone.com/

If you could retrieve md5 passwords so easily, you would have to worry about hacker ..

Jen / 01/07/2007 / 03:27 / http://www.freebiescout.com/ps3guide.html

I know about checksums to check for integrity, but I am wondering what use is there for SHA and MD5 checksums? I've never come across SHA checksums (used MD5s on various Linux distros). What do people use the PHP sha1_file for if it's almost impossible to reverse?

anonymous / 21/01/2008 / 09:55

>For this example we’ll be using 256-bit Rijndael, also known as the Advanced Encryption Standard (AES).

This statement is wrong. AES is based on128 bit Rijndael. AES-256 and 256-bit Rijndael are two different beasts.

Frank Thompson / 24/01/2008 / 21:21 / http://www.gojiking.co.uk/

Good article. yes. MD5 is not secure any more, some mathmatician already demonstrated how to break it. I have tried the code snippets in this article and it works very well. For Garrett, the authentication process is to take your input password, encrypt it and then compare with the stored and encrypted password. So once the password is stored into database table as encrypted format, nobody can find out the real password. So usually what the website administrator can do for you is to reset your password (of course, they have to identify you in the first instance)

Jeremy Miller / 20/06/2008 / 21:01 / http://www.teratask.com

For hashing, I use the PHP hash() function and choose SHA512 which will be stronger than SHA1.

Andy / 18/09/2008 / 19:08 / http://www.hair-extensions-united-kingdom.co.uk

I have recently been learning php and MySQL and picking it up quite well, although I am aware about attacks and msql injection. I have to develop that side of things yet in order to make my projects more secure.

To answer a comment from a post above, if you have passwords stored in your own database, you should be able to access them via something like phpmyadmin.

Andy

Thomas / 15/02/2009 / 01:09

Thanks a lot for this, I was looking for a way to encrypt/decrypt gMail, FB, etc... passwords for my UI, and I have also changed the login process from MD5 to SHA1 after reading this!

sathyanarayana sastry chamarthi / 05/11/2009 / 11:10

Hi,

I google for the password protection although I know about md5 and sha1 but I found this to be the very best article in simple english.

Good work. Keep going on.

Thanks for the opportunity of expression.

@comments from various people,

after going through, I want to say a word "Knowledge is endless" please use this to get others (who are less knowledged) up. Please don't write in a quarelling mode. Be soft.

Add a comment

Your name:


Your email: (Not displayed)


Your website: (optional)


Enter your comment here:

.net magLatest issue Buy it

Issue 200

Discover the past, present and future of web design, and get a free .biz domain! Find out more ...

» Fantastic subscription offers
» Buy issue 200
» Get a corporate subs
» Join us on Facebook

 
Signup for our newsletter

Enter your email address and start receiving our new-look weekly email newsletter!