5

I have a PHP login script with salt on the database, but in my register script I see:

$qry = "INSERT INTO accounts(username, firstname, lastname, password) " . 
VALUES('$username','$fname','$lname','" . md5($_POST['password']) . "')";

and for the login:

$qry="SELECT * FROM accounts WHERE username='$username' AND password='" .
md5($_POST['password']) . "'";

Is there some code that can replace the MD5? Something more secure?

I've heard of SHA1 or something.

Danny Beckett
  • 20,529
  • 24
  • 107
  • 134
  • 1
    1. Use an exiting auth library 2. Use an existing auth .. there are multiple problems that are beat to death on SO - search for ["php hash password"](http://stackoverflow.com/search?q=php+hash+password) and have a good read: SQL injection; inappropriate hash choice (SHA-1 is not appropriate either); no salt (despite what is claimed); other issues elsewhere .. ? – user2246674 May 01 '13 at 23:09
  • ive read the md5 is horrible anymore, sha256 and salt was reccomended, but im gonna take a look at what you posted and do some research. thank you for the point in the right direction sir. – DxVerm aka Xeno May 01 '13 at 23:24
  • 1
    MD5 should not be used for password hashing; but the exact same reason for why not applies to SHA-1. [MD5 hashing still *cannot* be feasibly "unhashed"](http://stackoverflow.com/a/13446272/2246674) - however, *both MD5 and SHA-1 are vulnerable to brute-forcing because they are both very fast and can be parallelized*. Using salt can prevent rainbow table attacks (and not using salt is a worthless hashed password because of rainbow table attacks), but it cannot prevent brute force attacks which is why SHA-1 is *no more suitable* than MD5 for this particular task. – user2246674 May 01 '13 at 23:27
  • I like [this answer about the Do's and Don'ts of passwords](http://stackoverflow.com/a/401684/2246674). Note that all of the recommended password hashing (scrypt, bcrypt, PBKDF) strategies are designed to mitigate brute-force attacks. While one can "loop SHA-1 10000 times", this particular hashing issue has *already been solved* - in a better way. Also, *do not forget the salt*. – user2246674 May 01 '13 at 23:31
  • However, like all systems, only the weakest link needs to be broken - brute-forcing requires access to the hashed passwords, which already means there has been at least a read-access compromise to the datastore. In this case I would actually suspect that the system could be compromised through SQL injection; but that would require seeing the larger context. (Also, don't forget the importance of using HTTPS connections and preventing XSS - or someone could acquire passwords in plaintext trivially.) – user2246674 May 01 '13 at 23:40
  • the passwords are stored in a database, no plaintext, and i do have a proper snippet for preventing sql injection, I just need to do a bit more reading on password security. – DxVerm aka Xeno May 01 '13 at 23:48
  • I recommend pdo or mysqli and placeholders - they are tidier and guaranteed. Unless your "proper snippet" is `mysql_real_escape_string` it is suspect at best. – user2246674 May 02 '13 at 00:19
  • see im reading php and mysql from novice to ninja and im learning about pdo, OOP right? and thats where i got the mysql_real_escape_string from to prevent sql injection. – DxVerm aka Xeno May 02 '13 at 00:21
  • While `mysql_real_escape_string` will work (and it is much better than various other "sanitization" incarnations), [its use should be discouraged in favor of placeholders](http://stackoverflow.com/a/60496/2246674). Placeholders have multiple advantages: 1. no need to *ensure* `mysql_real_escape_string` everywhere as placeholders will "just work" to prevent SQL injection; 2. no need to include quotes in query and it also yields a simpler query with less string interpolation noise; 3. less need to special-case NULL handling or differences with string/number. – user2246674 May 02 '13 at 00:34
  • looks like i've got some reading to do, thank you again user! – DxVerm aka Xeno May 02 '13 at 00:46

3 Answers3

3

Short answer

Use bcrypt not md5 or sha1

Longer answer

Using the crypt() is hard. There is a new PHP password hashing API coming in PHP version 5.5, you can read about it here:

https://gist.github.com/nikic/3707231

It uses bcrypt and makes the whole process very easy. Of course php 5.5 isn't ready yet, so in the meantime there is a library to provide this new API right now:

https://github.com/ircmaxell/password_compat

Edit: See this thread for a much more thorough answer on the topic:

How do you use bcrypt for hashing passwords in PHP?

Community
  • 1
  • 1
jszobody
  • 28,495
  • 6
  • 61
  • 72
1

In consideration of @jszbody post, you should also update your password field to tell you want scheme you're using.

Where you have an MD5 hash now, you might have just "BAC232BC1334DE" or something.

When you go to SHA or whatever, you should change it to: "SHA:YOURSHAHASHHERE".

Because you can't change any of your existing passwords right now. This will make it more backward compatible, since now you can support both schemes.

Since you get the original password during login, you can dynamically upgrade your passwords in place as people login.

You get your user record, check the password. If there is no scheme, use MD5, and compare passwords. If they're correct (i.e. they can log in), you can update their old MD5 password to the new SHA password.

Also, it seems you are not salting your passwords. You must salt your passwords so that when Mary Sue uses "ilovekittens" for her password, and Big Jake Mahoney uses "ilovekittens" as his password, you don't get the same has for identical passwords.

You can store the salt in the password as well: "SHA:RANDOMSALTCHARACTERS:YOURSALTEDHASHHERE".

Salting is highly recommended. Unsalted, it pretty much doesn't matter a whole lot what scheme you use.

Will Hartung
  • 115,893
  • 19
  • 128
  • 203
  • ***There is little to no benefit to using SHA-1 over MD5 for password hashes***. A good salt value (or an equivalent) should be used in all cases or the hashing is *useless* due to rainbow tables and time/space tradeoffs - I would thus say "Salting is required." – user2246674 May 01 '13 at 23:42
0

Try using the following class:

<?php
    class PassHash {  

        // blowfish  
        private static $algo = '$2a';  

        // cost parameter  
        private static $cost = '$31';  

        // mainly for internal use  
        public static function unique_salt() {  
            return substr(sha1(mt_rand()),0,22);  
        }  

        // this will be used to generate a hash  
        public static function hash($password) {  

            return crypt($password,  
                        self::$algo .  
                        self::$cost .  
                        '$' . self::unique_salt());  

        }  
        // this will be used to compare a password against a hash  
        public static function check_password($hash, $password) {  

            $full_salt = substr($hash, 0, 29);  

            $new_hash = crypt($password, $full_salt);  

            return ($hash == $new_hash);  

        }  

    }  

?>

include it in your page with the following:

include_once('passhash.class.php');

Hash the password by:

PassHash::hash("test");

And check it with:

 if (PassHash::check_password($databasepassword, $formpassword)){
  // do stuff
 } 

This function uses Blowfish encryption. For more information on Blowfish goto PHP.net/crypt

Blowfish is considered the most effective yet most powerfull way of encrypting passwords. Do not use MD5 or SHA1 without using a salt!

Benjamin de Bos
  • 4,334
  • 4
  • 20
  • 30
  • Do not use MD5 or SHA1 (for this) at all! – user2246674 May 01 '13 at 23:47
  • i agree, however. For small security looking systems you could use md5 with a salt. As long as you keep the salt away from your httpd dir. Otherwise i still dont see why you shouldnt use md5. Since it's not reversable though... – Benjamin de Bos May 02 '13 at 00:09
  • *Neither* MD5 nor SHA1 are suitable (with or without a salt) because they *can be brute-forced*. See my comments on the main post. I agree entirely with using bcrypt (as shown) because it is resistant to brute-force attacks. (This answer also shows a *good* salt generation function.) – user2246674 May 02 '13 at 00:18