0

This is my first attempt in securely storing passwords and I would like to make sure that everything is done correctly. I was advised to use SHA-256 hashing alongside salt.

Assuming user submitted their password thorough form, we get the password via

$password = $_POST["password"];

What is correct way to salt $password and use SHA-256 hashing on it, so it can than be stored in a password field "password CHAR(64)" in a database?

Once done and stored how would I than compare value stored in a database to one user entered in a login form? Lets assume $loginPassword = $_POST["loginPassword"]; is what user entered.

Ilja
  • 44,142
  • 92
  • 275
  • 498
  • Advice to use SHA-256? That's not [what I've heard](http://es1.php.net/manual/en/faq.passwords.php#faq.passwords.fasthash). – Álvaro González Mar 03 '14 at 16:39
  • @ÁlvaroG.Vicario Interesting, what would you suggest as an alternative hashing method used today? Preferably secure and fast? I assume it can be applied via same logic as SHA ? – Ilja Mar 03 '14 at 16:41
  • It cannot be secure *and* fast. The tip to use Blowfish in the linked manual page is fine IMHO. (There're some other similar algorithms but they're specifically designed to be slow, just like Blowflish.) – Álvaro González Mar 03 '14 at 16:43
  • possible duplicate of [Secure hash and salt for PHP passwords](http://stackoverflow.com/questions/401656/secure-hash-and-salt-for-php-passwords) – Álvaro González Mar 03 '14 at 16:45
  • Use the [`crypt()`](http://php.net/crypt) function; it takes care of the salt for you, if you're interested. – Funk Forty Niner Mar 03 '14 at 16:46
  • Also see Openwall's [PHP password hashing framework](http://www.openwall.com/phpass/) (PHPass). Its portable and hardened against a number of common attacks on user passwords. The guy who wrote the framework (SolarDesigner) is the same guy who wrote [John The Ripper](http://www.openwall.com/john/) and sits as a judge in the [Password Hashing Competition](http://password-hashing.net/). So he knows a thing or two about attacks on passwords. – jww Oct 12 '14 at 00:38

2 Answers2

2

Instead of using SHA family methods, you can use the crypt() function to salt it for you.

Here is an example script (save and login) using PDO.

Save password in DB

<?php
// Set the password
$password = 'mypassword';

// Get the hash, letting the salt be automatically generated
$hash = crypt($password);

echo $hash; // for testing purposes only

$mysql_username = 'username'; // for DB
$mysql_password = 'password'; // for DB

$dbh = new PDO('mysql:host=localhost;dbname=database_name', $mysql_username, $mysql_password);

$stmt = $dbh->prepare("INSERT INTO table_name (name,pass) VALUES (:name,:pass)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':pass', $pass);

// insert rows
// $name = $_POST['name'];
// $name = $_POST['pass'];

$name = "username";
$pass = $hash;
$stmt->execute();

Login script

<?php
$mysql_username = 'username'; // for DB
$mysql_password = 'password'; // for DB

$dbh = new PDO('mysql:host=localhost;dbname=database_name', $mysql_username, $mysql_password);

/*
$username = $_POST['username'];
$password = $_POST['password'];
*/

$username = "username";
$password = "mypassword";

$sql = "SELECT * FROM table_name WHERE name=:username";
$statement = $dbh->prepare($sql);
$statement->bindValue(':username',$username,PDO::PARAM_STR);

if($statement->execute())
{
    if($statement->rowCount() == 1)
    {
        $row = $statement->fetch(PDO::FETCH_ASSOC);

 if (crypt($password, $row['pass']) === $row['pass'])

        {
            $username = $row['name'];
            $email = $row['email'];

echo "Stage 1";

echo "<hr noshade size=\"1\">";

echo "Hello " .$username;

            exit;
        }
        else
        {
            // include "error_login.php";

echo "Stage 2 - ERROR";

        }
    }
    else
    {
       // include "error_login.php";

echo "Stage 3 error";
    }
}
Funk Forty Niner
  • 74,450
  • 15
  • 68
  • 141
  • Just few questions, in Login script when you bindBalue :username what is PDO::PARAM_STR used for? similarly PDO::FETCH_ASSOC ? This is first time I see those. – Ilja Mar 03 '14 at 17:46
  • 1
    [`This page`](http://www.php.net/manual/en/pdo.constants.php) will explain it in better detail. @Ilja and the [`PDO manual`](http://www.php.net/PDO) – Funk Forty Niner Mar 03 '14 at 17:48
  • 1
    Crypt used in this form will generate a weak DES hash without a safe salt. You should definitifely use the function [password_hash()](http://php.net/manual/en/function.password-hash.php) instead. Have a look at Anti-weakpasswords answer. – martinstoeckli Mar 04 '14 at 08:34
  • I agree, yet as I stated in my answer, it's an example script (and can be modified). If the OP has PHP 5.5 on the server then by all means to use PHP's `password()` function. @martinstoeckli – Funk Forty Niner Mar 04 '14 at 14:38
  • @Fred-ii- - Note that there is also a [compatibility pack](https://github.com/ircmaxell/password_compat/blob/master/lib/password.php) in form of a simple PHP file for PHP version 5.3.7 and higher. For versions before 5.3.7 i wrote [example code](http://www.martinstoeckli.ch/php/php.html#bcrypt) to use BCrypt. – martinstoeckli Mar 04 '14 at 16:45
  • Thanks @martinstoeckli - I did see it before but didn't have a chance to try it out yet. Then each time I had some free time on my hands (to test), I was quickly distracted by another job therefore forgetting; again. (lol) I will make of "note" of it now so this time I won't forget. Thanks again for the info, cheers. And thanks for the example code :) – Funk Forty Niner Mar 04 '14 at 16:48
  • @Fred-ii- - Well that's the problem with free time... good luck anyway. – martinstoeckli Mar 04 '14 at 16:51
  • I've saved both of them now and in my "to do" folder as we speak. @martinstoeckli Thanks again. – Funk Forty Niner Mar 04 '14 at 16:52
2

If you're on PHP 5.5 or later, there's the built-in password_hash() and password_verify() with Bcrypt - if you're on PHP 5.3.7 or later, there's the password_compat compatibility library; all this is per the PHP.net Safe Password Hashing FAQ entry.

Essentially, on PHP 5.3.7 and above, replace the old crypt() with password_hash() and password_verify().

See my answer to PHP Secure password generation and storage for some more details on cost choice, but it boils down to the very simple:

<?php
/**
 * In this case, we want to increase the default cost for BCRYPT to 12.
 * Note that we also switched to BCRYPT, which will always be 60 characters.
 */
$options = [
    'cost' => 12,
];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n";
?>

to generate the hash, then you store the output string, and then verify with:

<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

if (password_verify('rasmuslerdorf', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}
?>

Both examples come from the PHP.net Password Hashing page.

Anti-weakpasswords
  • 2,604
  • 20
  • 25
  • awesome, only bit I don't understand is the $hash bit, do I simply set its value to a random password generated by pasword hash? – Ilja Mar 04 '14 at 14:03
  • 1
    Per the docs, "The used algorithm, cost and salt are returned as part of the hash. Therefore, all information that's needed to verify the hash is included in it." - therefore in the verify, $hash is set equal to whatever the initial password_hash() was; you'd use password_hash() only when a user sets or changes a password - password_verify() is what you use when the log in, passing in first the password they typed, and second the previous password_hash() result which you stored in your database/text file/whatever storage you're using. – Anti-weakpasswords Mar 04 '14 at 14:38