Jump to content

PHP password security


Richard_Grant
Go to solution Solved by kicken,

Recommended Posts

I have been out of the game for a while and i need a bit of guidance on this.

 

Password: HelloWorld!

Salt: mySaltForMyReallyCoolPasswordThatiMadeForPHPFREAKS

 

MD5 . SALT = 072ce1d7fd7e6f14ba12053a9e057b26

 

SHA1 . SALT  = d580f4880e29ed757d942623f4d96dab1976d929

 

Crypt . SALT = my7LFLALq6s3c

 

password_hash = $2y$13$mySaltForMyReallyCoolO9t3RUqt1WbzVeqqQGxDHqOF/nu2Zhs2

 

 

Which security protocol is most prefered.

 

SALT . MD5 . SALT http://php.net/manual/en/function.md5.php

 

SHA1 . SALT http://php.net/manual/en/function.sha1.php

 

Crypt http://php.net/manual/en/function.crypt.php

 

password_hash http://php.net/manual/en/function.password-hash.php (new)

 

 

 

 

function:

<?PHP
$password = "HelloWorld!";
$salt = "mySaltForMyReallyCoolPasswordThatiMadeForPHPFREAKS";

$md5 = md5($password . $salt);

$sha1 = $sha1($password . $salt);

$crypt = crypt($password, $salt);


$o = [
    'cost' => 13,
    'salt' => $salt,
];
$password_hash = password_hash($p, PASSWORD_DEFAULT, $o);

?>

After i get the encrypted password, i will convert it to binary and then store it in the database as a binary.

 

 

Which of these methods do you prefer and why? (ps. i might have used password_hash incorrectly).

 

i don't need any source code, just fill me in :)

Edited by Richard_Grant
Link to comment
Share on other sites

  • Solution

Use the password_hash function. Specify PASSWORD_BCRYPT rather than the default and do NOT specify a salt, let PHP generate it for you. You should specify a cost however.

 

password_hash($password, PASSWORD_BCRYPT, array('cost' => 14));
You'll want to play with the cost value until the function takes about a second to complete. You can measure the time using microtime, such as:

<?php

$s = microtime(true);
password_hash('abcdefg', PASSWORD_BCRYPT, array('cost'=>14));
$e = microtime(true);

var_dump($e-$s);
When storing the hash to the database there is no reason to convert it into some kind of binary representation, whatever you mean by that. Just store the string into a VARCHAR column.

 

 

As for the why, MD5 has collision weaknesses, and both it and SHA are fast algorithms which is NOT a good thing for password hashing. For password hashing you want an algorithm that is slow and/or memory intensive so that someone who is trying to brute-force the hashes is forced to spend a lot of time on it and/or expend significant resources.

 

bcrypt is designed for password hashing and is slow and expensive. It does however have it's own limitations that you should be aware of.

  • Like 1
Link to comment
Share on other sites

First of all, this is hashing, not encryption. Those are two entirely different concepts, and it's very important to not confuse them.

 

General-purpose hash algorithms like MD5, SHA-1, SHA-2 etc. are inacceptable for password hashing. They're designed for an entirely different purpose (like data integrity), and they're supposed to be very fast. When dealing with passwords, this property makes them extremely vulnerable to brute force attacks. For example, an old gamer GPU can calculate billions(!) of MD5 or SHA hashes per second. If the attacker is willing to pay some extra money for specialized hardware or a cloud computing service, they can have trillions of hashes per second. It's easy to see that even strong passwords won't survive such an attack.

 

This home-made salting scheme also doesn't help at all. In fact, you should never invent your own security algorithms. Leave that to the experts.

 

The crypt() function can theoretically be used to hash passwords. However, there are two big problems:

  • Most algorithms it offers are long obsolete.
  • Most people have no idea how to use it correctly (and appearently don't bother to read the manual).

Unfortunately, this applies to you as well: When you just pass some gibberish to the second parameter, you get the extremely weak DES-based algorithm. Even worse, this algorithm only processes the first 8 bytes/characters of the password and ignores the rest.

 

So crypt() is out of question as well.

 

That leaves you with password_hash(), and this is indeed the best solution. It uses very strong algorithms (currently bcrypt), and it's specifically designed for non-experts.

Link to comment
Share on other sites

As for the why, MD5 has collision weaknesses

 

No, no, no. Please stop spreading this nonsense. Collision resistance has absolutely nothing to do with password hashing, and I have no idea why this myth keeps popping up in discussions.

 

A collision attack means finding two arbitrary preimages which map to the same hash. That doesn't help us find passwords at all. What we need is a preimage which maps to a particular hash. And that's an antirely different scenario aptly called preimage attack.

 

This may seem like an unimportant detail, but many people actually believe this collision stuff and choose the algorithm based on collision resistance (e. g. SHA-2). That's a huge mistake, because SHA-2 is just as unsuitable for password hashing as MD5.

 

The problem of MD5 is its efficiency, not its issues with collisions. In fact, you may very well use MD5 for high-entropy input like random tokens.

Link to comment
Share on other sites

A collision attack means finding two arbitrary preimages which map to the same hash. That doesn't help us find passwords at all. What we need is a preimage which maps to a particular hash. And that's an antirely different scenario aptly called preimage attack.

 

To elaborate: MD5 hashes are commonly used as a checksum on files so you can verify the file you just downloaded has not been tampered with from what the original provider intended (e.g. someone has inserted a virus). It is in this context that collisions become an issue. With a large source it is possible to create another file with the same size which would produce the same hash - defeating the purpose of a checksum. With the "relatively" limited number of possible password combinations that could be produced with 8-40ish of the available characters, I would guess it is a safe bet that no collisions would exist in that range.

Link to comment
Share on other sites

I think you're still confusing collision attacks and preimage attacks.

 

To find a password, you need a preimage for a particular hash. It doesn't help you that somewhere there are two preimages which both map to some other hash. You need this hash. And that's is a preimage attack, not a collision attack.

 

Or maybe you think that collisions are so common that an attacker can choose from a large pool of different strings which are all accepted as a valid password? No. Collisions don't just happen by accident, you need to actually find them.

 

So again: Collision resistance is not relevant for password hashing. This claim is just wrong and leads to the wrong conclusions. You do need collisions resistance for things like digital signatures, but that's an entirely different topic. Right now, we're talking about passwords.

Link to comment
Share on other sites

Use the password_hash function. Specify PASSWORD_BCRYPT rather than the default and do NOT specify a salt, let PHP generate it for you. You should specify a cost however.

 

password_hash($password, PASSWORD_BCRYPT, array('cost' => 14));
You'll want to play with the cost value until the function takes about a second to complete. You can measure the time using microtime, such as:

<?php

$s = microtime(true);
password_hash('abcdefg', PASSWORD_BCRYPT, array('cost'=>14));
$e = microtime(true);

var_dump($e-$s);
When storing the hash to the database there is no reason to convert it into some kind of binary representation, whatever you mean by that. Just store the string into a VARCHAR column.

 

 

As for the why, MD5 has collision weaknesses, and both it and SHA are fast algorithms which is NOT a good thing for password hashing. For password hashing you want an algorithm that is slow and/or memory intensive so that someone who is trying to brute-force the hashes is forced to spend a lot of time on it and/or expend significant resources.

 

bcrypt is designed for password hashing and is slow and expensive. It does however have it's own limitations that you should be aware of.

 

 

 

Okay so i just learned how to use passowrd correctly:

$password = "HelloWorld!";
$options = [
    'cost' => 14
];
$hash = password_hash($password, PASSWORD_BCRYPT, $options);
echo $hash . "<br>";
if(password_verify($password, $hash)){
  echo "Match";
}

I was unaware of the password_verify function, which is why i chose to use PASSWORD_DEFAULT but i opened up the manual :).

 

Which brings me to my next point...

If password_verify checks if $password & $hash are equal, doesn't that mean that $hash is being (de-hashed)?

Edited by Richard_Grant
Link to comment
Share on other sites

No it doesn't.  In fact, this is explained in the manual.

 


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. This allows the password_verify() function to verify the hash without needing separate storage for the salt or algorithm information.

  • Like 1
Link to comment
Share on other sites

 

If password_verify checks if $password & $hash are equal, doesn't that mean that $hash is being (de-hashed)?

 

No. There's no such thing as “de-hashing”. The whole point of hash algorithms is that they cannot be reverted.

 

The result of password_hash() contains all parameters of the original hash calculation: the algorithm (“2y” means bcrypt), the cost factor (in my case 10) and the salt (128 bits), followed by actual hash (184 bits).

 

--------------------------------------------------------------------------------------------------------

$2y$10$4DD6Ts9Lw4gZOKoYCd3iferiRKfNGHuMIMgbBkRSHQyal0NqHvBKK

--------------------------------------------------------------------------------------------------------

 

To verify a password, the function hashes it with the exact same parameters and then compares the result with the given hash. If the hashes match, then the password is correct, otherwise it's wrong.

 

It's the same thing you did with MD5: To verify a password, you hash it and then compare the result with the MD5 hash in your database. If they match, then the password is correct.

Edited by Jacques1
Link to comment
Share on other sites

No it doesn't.  In fact, this is explained in the manual.

 

Thank you @jcbones :) that was EXTREMELY helpful.

 

So here is the function i will be using for password hashing: (tell me what you would do differently if anything)

<?php

class password_handle{
  public static function hashbinary($password){//converts string to binary
    $hash = password_hash($password, PASSWORD_BCRYPT, array('cost'=>14));
    $bin_layer = "";
    $hash_split = str_split($hash);
    for($i = 0; $i < count($hash_split);$i++){
      $bin = decbin(ord($hash_split[$i]));
      $bin_l = strlen($bin);
      if($bin_l < 7){
        $pad ="";
        for($j =0; $j < 7 - $bin_l;$j++){
          $pad .= "0";
        }
        $bin = $pad . $bin;
      }
      $char = chr(bindec($bin));
      $bin_layer .= $bin;
    }
    return $bin_layer;
  }
  public static function verifyhash($password, $bin_password){//verifies that the hash is equal to the password
    return (password_verify($password, self::binarystring($bin_password))) ? true : false;
  }
  private function binarystring($binary){//converts binary to string
    $char_layer = "";
    $bin_split = str_split($binary, 7);
    for($i = 0; $i < count($bin_split); $i++){
      $char_layer .= chr(bindec($bin_split[$i]));
    }
    return $char_layer;
  }
}

$password = "HelloWorld!";
$p_h = new password_handle();
$hashbin =  $p_h::hashbinary($password); //Store this in database
/*
01001000110010111100101001000110001011010001001000110111111011101101001101000011100011001011000010
11110001011001011100001011101000101101010110110101010000111011011001101001011111101011101101110110
10011111101010100110110010111101101101100101011101001101111101010101011101001101000011001101011010
11100010110001110011011010101010001011001010101111000010111010011110101001111100001010000101110000
110011010010111001010010111
*/
if($p_h::verifyhash($password,$hashbin)){//if password is the same as the hash
  echo "true";
}else{
  echo "false";
}

?> 

What is happening there is:

  1. i password_hash the string with PASSWORD_BCRYPT allowing they method to determine its own salt
  2. I convert the hash to a binary string for storing in my mysql database
  3. I check if the binary hash is equal to the password string.
Edited by Richard_Grant
Link to comment
Share on other sites

 

  1. i password_hash the string with PASSWORD_BCRYPT allowing they method to determine its own salt
  2. I convert the hash to a binary string for storing in my mysql database
  3. I check if the binary hash is equal to the password string.

 

And the question is why? This is a horrible and sadistic thing against you computer and all services on it :)

Link to comment
Share on other sites

 

To elaborate: MD5 hashes are commonly used as a checksum on files so you can verify the file.....

 

Psycho, have you read the bugs message from authors of this program?

 

 

       The MD5 algorithm should not be used any more for security related pur‐

       poses.  Instead, better use an SHA-2 algorithm, implemented in the pro‐

       grams sha224sum(1), sha256sum(1), sha384sum(1), sha512sum(1)

Link to comment
Share on other sites

Note that this is not a “binary string”. It's literally a sequence of the characters “0” and “1”, which means this takes 8 times as much space. It's entirely unclear why you would do this. There also seems to be some confusion regarding the number of bits in a byte. One byte is 8 bits (not 7).

 

Just store the hash in a CHAR(60) column. It's generally a good idea to get the basic code done before you start implementing strange ideas.

Link to comment
Share on other sites

Note that this is not a “binary string”. It's literally a sequence of the characters “0” and “1”, which means this takes 8 times as much space. It's entirely unclear why you would do this. There also seems to be some confusion regarding the number of bits in a byte. One byte is 8 bits (not 7).

 

Just store the hash in a CHAR(60) column. It's generally a good idea to get the basic code done before you start implementing strange ideas.

 

I'm not going to lie, i wasn't thinking about bytes when i wrote that. (i will update that now).

In my case speed is not an issue, i am sacrificing speed for security.

 

TBH i haven't decided if i want to store the binary string as a BINARY in the database or convert the binary string to byte array and add it to the database as a BLOB,

Link to comment
Share on other sites

I think you're still confusing collision attacks and preimage attacks.

 

To find a password, you need a preimage for a particular hash. It doesn't help you that somewhere there are two preimages which both map to some other hash. You need this hash. And that's is a preimage attack, not a collision attack.

 

Or maybe you think that collisions are so common that an attacker can choose from a large pool of different strings which are all accepted as a valid password? No. Collisions don't just happen by accident, you need to actually find them.

 

So again: Collision resistance is not relevant for password hashing. This claim is just wrong and leads to the wrong conclusions. You do need collisions resistance for things like digital signatures, but that's an entirely different topic. Right now, we're talking about passwords.

 

Was that in response to my post? I was agreeing with you. Perhaps it is you that is confused. I specifically stated that collisions were a non-issue with respect to passwords. The collisions are only an issue when using a hash for something such as a checksum where an alternative file could be created to appear to be the target file with potential malicious modifications, and yet produce the same hash. It is not an easy thing to do, but it is possible. Again, I was just elaborating on your previous comment.

Link to comment
Share on other sites

Was that in response to my post? I was agreeing with you. Perhaps it is you that is confused. I specifically stated that collisions were a non-issue with respect to passwords. The collisions are only an issue when using a hash for something such as a checksum where an alternative file could be created to appear to be the target file with potential malicious modifications, and yet produce the same hash. It is not an easy thing to do, but it is possible. Again, I was just elaborating on your previous comment.

 

Psycho, whats your opinion about storing a hashed password as a binary in a mysql database?

Link to comment
Share on other sites

In my case speed is not an issue, i am sacrificing speed for security.

 

What? How are those weird digit sequences supposed to increase security? It's the exact opposite: I wouldn't be surprised if there's a bug somewhere.

 

 

 

TBH i haven't decided if i want to store the binary string as a BINARY in the database or convert the binary string to byte array and add it to the database as a BLOB,

 

Please don't. I think you've put a lot of strange ideas into your head, and now it's time to get back to reality.

 

Store the result of password_hash() in a CHAR(60) column like everybody else on this planet. I understand that you're into math and like to play around with different bases. But user passwords aren't the right thing to play with. Make this a separate project.

 

 

 

Was that in response to my post? I was agreeing with you. Perhaps it is you that is confused.

 

I am referring to your last sentence where you stated that there are probably no collision in the space of typical passwords.

 

Now, maybe you just meant that as a kind of “fun fact” with no relation to the previous text. But I read it as: The reason why collision attacks are not a problem for passwords is because there are no collision in that space. And that would be plain wrong.

Link to comment
Share on other sites

I am referring to your last sentence where you stated that there are probably no collision in the space of typical passwords.

 

Now, maybe you just meant that as a kind of “fun fact” with no relation to the previous text. But I read it as: The reason why collision attacks are not a problem for passwords is because there are no collision in that space. And that would be plain wrong.

 

I see what you mean. I could have been more clear. What I was trying to state was that the possibility of collisions within that space of permutations is so infinitesimally remote that the discussion of whether they are an issue or not for passwords is not even worth discussing. And, trying to even use collisions for the purpose of cracking a password would be a foolish endeavor.

 

 

Psycho, whats your opinion about storing a hashed password as a binary in a mysql database?

 

I don't see the point. Are you thinking that storing it in this way will trip up someone trying to brute force the values? I guess that is a possibility, but think about "who" you are trying to protect the data from. This would be people with decent technical skills. Assuming you are already using a good hashing method, these would be people that have enough knowledge to reverse engineer the hashing method to create the logic to run a brute force attack. Do really think this type of person would not recognize that the hash is stored in binary and just add that to the brute-force process? I would run off the assumption that a person who has infiltrated my DB has likely accessed the source files as well. And, in that case they can "see" that the values are stored in binary. All this does is add a small level of obfuscation. And, obfuscation does not equal increased security in my opinion.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.