0

I'm attempting to store my users passwords using phpass but I'm struggling to implement it correctly. I had working code but I was storing my password as text, which I've learned is not safe. According to phpbestpractices.org the most secure way is phpass, so away I went. I tried reading their tutorial but it is a little too far ahead of my skill range. The changes I made from my original code can be seen here: http://www.diffchecker.com/6gw4g2ps I apologize ahead of time if I completely butchered it, I'm still in the learning process, but you can trust I'll take any advice I can follow! I have attempted to move the include and $hash_obj down into the else statement, but then it tells me:

Warning: include(/home/carlton/public_html/PHPproject/includes/PasswordHash.php): failed to open stream: Permission denied in /home/carlton/public_html/PHPproject/forms/registersecure.php on line 40

Warning: include(): Failed opening '/home/carlton/public_html/PHPproject/includes/PasswordHash.php' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /home/carlton/public_html/PHPproject/forms/registersecure.php on line 40

Fatal error: Class 'PasswordHash' not found in /home/carlton/public_html/PHPproject/forms/registersecure.php on line 41"

<?php
include ('/home/carlton/public_html/PHPproject/includes/PasswordHash.php');
$hash_obj = new PasswordHash(8, false);
error_reporting(E_ALL); ini_set('display_errors', 1); 
if (empty($_POST)){
?>
<form name="registration" action="registersecure.php" method="POST">
<label for "username">Username: </label>
<input type="text" name="username"/><br />
<label for "password">Password: </label>
<input type="password" name="password"/><br />
<label for "fname">First Name: </label>
<input type="text" name="fname"/><br />
<label for "lname">Last name: </label> 
<input type="text" name="lname"/><br />
<label for "email">Email: </label>
<input type="text" name="email"/><br />
<button type="submit">Submit</button>
</form> 
<?php 
}
else{

$form = $_POST;
$username = $form['username'];
$password = $form['password'];
$fname = $form['fname'];
$lname = $form['lname'];
$email = $form['email'];
$user = 'root';
$pass = 'pdt1848!';

if (strlen($password)>72){die("Password must be less than 73 characters.");
}

$hash = $hash_obj->HashPassword($password); 
//if (strlen($hash)>=20){

  //  $sql = "INSERT INTO users (password)VALUE($hash) ";
  //  $query = $db->prepare($sql);
  //  $query->execute(array(':password'=>$hash));
//};

$db = new PDO('mysql:host=localhost;dbname=phpproject', $user, $pass);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$sql = "INSERT INTO users (username, password, fname, lname, email)VALUES(:username, :password, :fname, :lname, :email)";
$query = $db->prepare($sql);
$result = $query->execute(array(':username'=>$username, ':password:'=>$hash, ':fname'=>$fname,
    ':lname'=>$lname, ':email'=>$email));

if ($result){
    echo "Thanks for registering with us!";
} else {
    echo "Sorry, an error occurred while editing the database. Contact the guy who built this garbage.";
};
};
?>

PasswordHash.php

<?php

class PasswordHash {
var $itoa64;
var $iteration_count_log2;
var $portable_hashes;
var $random_state;

function PasswordHash($iteration_count_log2, $portable_hashes)
{
    $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

    if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
        $iteration_count_log2 = 8;
    $this->iteration_count_log2 = $iteration_count_log2;

    $this->portable_hashes = $portable_hashes;

    $this->random_state = microtime();
    if (function_exists('getmypid'))
        $this->random_state .= getmypid();
}

function get_random_bytes($count)
{
    $output = '';
    if (is_readable('/dev/urandom') &&
        ($fh = @fopen('/dev/urandom', 'rb'))) {
        $output = fread($fh, $count);
        fclose($fh);
    }

    if (strlen($output) < $count) {
        $output = '';
        for ($i = 0; $i < $count; $i += 16) {
            $this->random_state =
                md5(microtime() . $this->random_state);
            $output .=
                pack('H*', md5($this->random_state));
        }
        $output = substr($output, 0, $count);
    }

    return $output;
}

function encode64($input, $count)
{
    $output = '';
    $i = 0;
    do {
        $value = ord($input[$i++]);
        $output .= $this->itoa64[$value & 0x3f];
        if ($i < $count)
            $value |= ord($input[$i]) << 8;
        $output .= $this->itoa64[($value >> 6) & 0x3f];
        if ($i++ >= $count)
            break;
        if ($i < $count)
            $value |= ord($input[$i]) << 16;
        $output .= $this->itoa64[($value >> 12) & 0x3f];
        if ($i++ >= $count)
            break;
        $output .= $this->itoa64[($value >> 18) & 0x3f];
    } while ($i < $count);

    return $output;
}

function gensalt_private($input)
{
    $output = '$P$';
    $output .= $this->itoa64[min($this->iteration_count_log2 +
        ((PHP_VERSION >= '5') ? 5 : 3), 30)];
    $output .= $this->encode64($input, 6);

    return $output;
}

function crypt_private($password, $setting)
{
    $output = '*0';
    if (substr($setting, 0, 2) == $output)
        $output = '*1';

    $id = substr($setting, 0, 3);
    # We use "$P$", phpBB3 uses "$H$" for the same thing
    if ($id != '$P$' && $id != '$H$')
        return $output;

    $count_log2 = strpos($this->itoa64, $setting[3]);
    if ($count_log2 < 7 || $count_log2 > 30)
        return $output;

    $count = 1 << $count_log2;

    $salt = substr($setting, 4, 8);
    if (strlen($salt) != 8)
        return $output;

    # We're kind of forced to use MD5 here since it's the only
    # cryptographic primitive available in all versions of PHP
    # currently in use.  To implement our own low-level crypto
    # in PHP would result in much worse performance and
    # consequently in lower iteration counts and hashes that are
    # quicker to crack (by non-PHP code).
    if (PHP_VERSION >= '5') {
        $hash = md5($salt . $password, TRUE);
        do {
            $hash = md5($hash . $password, TRUE);
        } while (--$count);
    } else {
        $hash = pack('H*', md5($salt . $password));
        do {
            $hash = pack('H*', md5($hash . $password));
        } while (--$count);
    }

    $output = substr($setting, 0, 12);
    $output .= $this->encode64($hash, 16);

    return $output;
}

function gensalt_extended($input)
{
    $count_log2 = min($this->iteration_count_log2 + 8, 24);
    # This should be odd to not reveal weak DES keys, and the
    # maximum valid value is (2**24 - 1) which is odd anyway.
    $count = (1 << $count_log2) - 1;

    $output = '_';
    $output .= $this->itoa64[$count & 0x3f];
    $output .= $this->itoa64[($count >> 6) & 0x3f];
    $output .= $this->itoa64[($count >> 12) & 0x3f];
    $output .= $this->itoa64[($count >> 18) & 0x3f];

    $output .= $this->encode64($input, 3);

    return $output;
}

function gensalt_blowfish($input)
{
    # This one needs to use a different order of characters and a
    # different encoding scheme from the one in encode64() above.
    # We care because the last character in our encoded string will
    # only represent 2 bits.  While two known implementations of
    # bcrypt will happily accept and correct a salt string which
    # has the 4 unused bits set to non-zero, we do not want to take
    # chances and we also do not want to waste an additional byte
    # of entropy.
    $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

    $output = '$2a$';
    $output .= chr(ord('0') + $this->iteration_count_log2 / 10);
    $output .= chr(ord('0') + $this->iteration_count_log2 % 10);
    $output .= '$';

    $i = 0;
    do {
        $c1 = ord($input[$i++]);
        $output .= $itoa64[$c1 >> 2];
        $c1 = ($c1 & 0x03) << 4;
        if ($i >= 16) {
            $output .= $itoa64[$c1];
            break;
        }

        $c2 = ord($input[$i++]);
        $c1 |= $c2 >> 4;
        $output .= $itoa64[$c1];
        $c1 = ($c2 & 0x0f) << 2;

        $c2 = ord($input[$i++]);
        $c1 |= $c2 >> 6;
        $output .= $itoa64[$c1];
        $output .= $itoa64[$c2 & 0x3f];
    } while (1);

    return $output;
}

function HashPassword($password)
{
    $random = '';

    if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
        $random = $this->get_random_bytes(16);
        $hash =
            crypt($password, $this->gensalt_blowfish($random));
        if (strlen($hash) == 60)
            return $hash;
    }

    if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
        if (strlen($random) < 3)
            $random = $this->get_random_bytes(3);
        $hash =
            crypt($password, $this->gensalt_extended($random));
        if (strlen($hash) == 20)
            return $hash;
    }

    if (strlen($random) < 6)
        $random = $this->get_random_bytes(6);
    $hash =
        $this->crypt_private($password,
        $this->gensalt_private($random));
    if (strlen($hash) == 34)
        return $hash;

    # Returning '*' on error is safe here, but would _not_ be safe
    # in a crypt(3)-like function used _both_ for generating new
    # hashes and for validating passwords against existing hashes.
    return '*';
}

function CheckPassword($password, $stored_hash)
{
    $hash = $this->crypt_private($password, $stored_hash);
    if ($hash[0] == '*')
        $hash = crypt($password, $stored_hash);

    return $hash == $stored_hash;
}
}

?>    
  • 1
    What exactly is the problem? – AJReading Apr 24 '14 at 03:37
  • I'm getting a blank page when I open ~/registersecure.php – carlgoodtoseeyou Apr 24 '14 at 03:42
  • Is the first file you copied in your post named `registersecure.php`? – nstCactus Apr 24 '14 at 03:47
  • Maybe unrelated to your problem but you have something wrong in that line `$sql = "INSERT INTO users (username, password, fname, lname, email)VALUES(:username, $hash, :fname, :lname, :email)";`. Try replacing it with `$sql = "INSERT INTO users (username, password, fname, lname, email)VALUES(:username, :password, :fname, :lname, :email)";`. The query didn't need to be changed. You just needed to change which variable is bound to the parameter `:password`, which you did next with `':password:'=>$hash` – nstCactus Apr 24 '14 at 03:53
  • @nstCactus I had that originally, I was just trying that out to see if any changes were done. I'll change it back now thank you. – carlgoodtoseeyou Apr 24 '14 at 03:55
  • And your problem happens after you had submitted the form or before the form even show? – nstCactus Apr 24 '14 at 03:58
  • I tried your code on my computer and everything works except the database stuff as I didn't set any DB. – nstCactus Apr 24 '14 at 04:03
  • @nstCactus If "$hash_obj = new PasswordHash(8, false);" is right below the include then I get a blank page. If I move it down into the else then I get the form but when I submit I get the error: Fatal error: Class 'PasswordHash' not found in /home/carlton/public_html/PHPproject/forms/registersecure.php on line 41 – carlgoodtoseeyou Apr 24 '14 at 04:04
  • @nstCactus you it have exactly as I do? Would you mind posting to www.diffchecker.com ? – carlgoodtoseeyou Apr 24 '14 at 04:20
  • Seems lie the include path could be resolved. Try to use `require` intead of `include`. It gives a fatal error and aborts script execution if the required file can't be found. – nstCactus Apr 24 '14 at 04:20
  • @nstCactus after changing it to require there is no error after I submit, rather just a blank page shows up. – carlgoodtoseeyou Apr 24 '14 at 04:24
  • @nstCactus sorry, that last comment was false, if i change it to require I don't even get the form, I get a blank page. – carlgoodtoseeyou Apr 24 '14 at 04:34
  • Try to add this `var_dump(ini_get('display_errors'));` at the very beginning of your script and tell me what is the output. – nstCactus Apr 24 '14 at 04:45
  • @nstCactus string(0) "" Warning: include(/home/carlton/public_html/PHPproject/includes/PasswordHash.php): failed to open stream: Permission denied in /home/carlton/public_html/PHPproject/forms/registersecure.php on line 4 Warning: include(): Failed opening '/home/carlton/public_html/PHPproject/includes/PasswordHash.php' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /home/carlton/public_html/PHPproject/forms/registersecure.php on line 4 Fatal error: Class 'PasswordHash' not found in /home/carlton/public_html/PHPproject/forms/registersecure.php on line 5 – carlgoodtoseeyou Apr 24 '14 at 04:56
  • @nstCactus I get that ^ when I add that line to my original code, where $hash_obj is under the include. – carlgoodtoseeyou Apr 24 '14 at 04:57
  • @nstCactus so apparently the problem was that php didn't have the permissions to read the PasswordHash.php file along with one extra colon. Thanks so much for sticking with me. – carlgoodtoseeyou Apr 24 '14 at 05:15

1 Answers1

0

Your problem here is that PHP cannot find (or is not allowed to read) the PasswordHash.php file. Make sure that your filename is exactly as the one in the require instruction (usually paths are case sensitive which means PasswordHash.php is different from passwordHash.php).

You got a blank page for two reasons:

  1. As the include (or require) instruction couldn't find the PasswordHash.php file, the class PasswordHash (defined in PasswordHash.php) was unknown to PHP. We you tried to instantiate that class PHP gave a fatal error and stop the execution of the script.
  2. Your PHP seems to be configured not to display errors (which is great for production but not so when you are developing). These instructions error_reporting(E_ALL); ini_set('display_errors', 1); change that but you included these lines right after the instruction that gave the fatal error ($hash_obj = new PasswordHash(8, false);) hence they were not executed and the script died silently.

What you should do instead of changing the display_error & error_reporting properties at runtime is to change the values in your php.ini file.

And finally, just an advice, but for maximum portability, you should use relative paths in your include or require instructions. (require('../includes/PasswordHash.php') for example).

nstCactus
  • 5,141
  • 2
  • 30
  • 41