bcintegrity
bcintegrity

Reputation: 213

Salting and hashing functions returning nothing

I found a script that salts, hashes, and then some, but my $password returns as the original string when I submit my form. This seems above my "pay grade"... My goal is to insert the returned salt, hash, and other outputs from the script into my db. I'm not even sure what column values I need in the db (MYSQLi). The script is in if($valid) before the sql:

<?php
if ($_SERVER["REQUEST_METHOD"] == "POST")
{$valid = true;

if (empty($_POST["password"]))
{$passwordErr = "Password must be set!"; $Epassword = "nope"; $valid = false;}
if (strpos($_POST["password"], " ") !== false)
{$passwordErr = "No spaces are allowed"; $Epassword = "nope"; $valid = false;}
if(strlen(utf8_decode($_POST['password'])) < 10 || strlen(utf8_decode($_POST['password'])) > 30)
 {$passwordErr = "Password must be between 10 and 30 characters"; $Epassword = "nope"; $valid = false;}
$password = htmlspecialchars($_POST['password']);

$tod = date("Y-m-d H:i:s");
$setdate = date("Y-m-d H:i:s", strtotime("-4 hours", strtotime($tod)));
$today = date("Y-m-d", strtotime("-4 hours", strtotime($tod)));

}

if($valid){

/*
 * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
 * Copyright (c) 2013, Taylor Hornby
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 */

// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTE_SIZE", 24);
define("PBKDF2_HASH_BYTE_SIZE", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

function create_hash($password)
{
// format: algorithm:iterations:salt:hash
$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTE_SIZE, MCRYPT_DEV_URANDOM));
return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" .
    base64_encode(pbkdf2(
        PBKDF2_HASH_ALGORITHM,
        $password,
        $salt,
        PBKDF2_ITERATIONS,
        PBKDF2_HASH_BYTE_SIZE,
        true
    ));
}

function validate_password($password, $correct_hash)
{
$params = explode(":", $correct_hash);
if(count($params) < HASH_SECTIONS)
   return false;
$pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
return slow_equals(
    $pbkdf2,
    pbkdf2(
        $params[HASH_ALGORITHM_INDEX],
        $password,
        $params[HASH_SALT_INDEX],
        (int)$params[HASH_ITERATION_INDEX],
        strlen($pbkdf2),
        true
    )
);
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
$diff = strlen($a) ^ strlen($b);
for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
{
    $diff |= ord($a[$i]) ^ ord($b[$i]);
}
return $diff === 0;
}

/*
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
 * $algorithm - The hash algorithm to use. Recommended: SHA256
 * $password - The password.
 * $salt - A salt that is unique to the password.
 * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
 * $key_length - The length of the derived key in bytes.
 * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
 * Returns: A $key_length-byte key derived from the password and salt.
 *
 * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
 *
 * This implementation of PBKDF2 was originally created by https://defuse.ca
 * With improvements by http://www.variations-of-shadow.com
 */
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
$algorithm = strtolower($algorithm);
if(!in_array($algorithm, hash_algos(), true))
    trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
if($count <= 0 || $key_length <= 0)
    trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);

if (function_exists("hash_pbkdf2")) {
    // The output length is in NIBBLES (4-bits) if $raw_output is false!
    if (!$raw_output) {
        $key_length = $key_length * 2;
    }
    return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
}

$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);

$output = "";
for($i = 1; $i <= $block_count; $i++) {
    // $i encoded as 4 bytes, big endian.
    $last = $salt . pack("N", $i);
    // first iteration
    $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
    // perform the other $count - 1 iterations
    for ($j = 1; $j < $count; $j++) {
        $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
    }
    $output .= $xorsum;
}

if($raw_output)
    return substr($output, 0, $key_length);
else
    return bin2hex(substr($output, 0, $key_length));
}

$sqli = @mysqli_connect("host", "db", "pass","empty");
if (mysqli_connect_errno($sqli)) { echo "Failed to connect to MySQL: " . mysqli_connect_error(); }

mysqli_query($sqli, "INSERT INTO `table`(`password`) VALUES ('$password')");
?>

Note: the query inserts the original $password value into the db.

The query inserts the original $password value into the db.

[Note] @Masoud answered my question, but I ended up using `password_hash($_POST['password'], PASSWORD_DEFAULT)' suggested by @martinstoeckli as follows:

if($valid){

$hashed_password = password_hash($_POST['password'], PASSWORD_DEFAULT);

mysqli_query($sqli, "INSERT INTO `table`(`password`) VALUES ('$hashed_password')");

}

Upvotes: 0

Views: 159

Answers (2)

martinstoeckli
martinstoeckli

Reputation: 24071

This problem can be solved much easier with PHP's function password_hash(). This function will return a 60 character string, including the hash, the salt and the cost factor, you can store this string into a single field in your database. To check against a stored hash you would use the function password_verify().

// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);

// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);

In your code you are also checking certain conditions and escape the password with htmlspecialchars(). This is not necessary and should not be done, the only requirement that makes sense for passwords is a minimum length. Escaping is not necessary because you will feed the content to the hash function and its output will always be safe (however it would have to be done for SQL not for HTML output). Variables in $_POST will already be decoded by PHP.

if (!isset($_POST["password"]) || strlen($_POST["password"]) < 10)
{
  // password is invalid show error message
}

To prevent a misunderstanding, password hashing is always done server side, so the password will be transported plain text between client and server. The usual solution to handle this problem is using HTTPS, SSL will then encrypt the password before sending it over the internet.

Upvotes: 1

Masoud
Masoud

Reputation: 1351

It must insert original $password to database, because you are not change $password from start to end of your code. one way is to call the "pbkdf2" function like this before last line of your code (before "mysqli_query(..." ) :

<?php
$hashed_password = pbkdf2(PBKDF2_HASH_ALGORITHM,$password,'dsafdsa',10,5,false)
?>

And change last line of your code like this:

mysqli_query($sqli, "INSERT INTO `table`(`password`) VALUES ('$hashed_password')");

Upvotes: 1

Related Questions