Jump to content

PHP Hashing - improve


Richard_Grant
Go to solution Solved by Jacques1,

Recommended Posts

Part of my class: using PHP5 ( http://php.net/manual/en/function.password-hash.php) If you know of anything new in PHP5 related to please do share :)

protected function create_hash($string){
$password = "#" . strrev($password);
    $grs =  $this->grs("|WordToTheWise",rand(22, 50));
    $hash = password_hash("_" . strrev($string), PASSWORD_BCRYPT, array('cost'=>rand(4,14),'salt'=>$grs));
    return strrev($hash);
}
public function verifyhash($string, $hash_string){//verifies that the hash is equal to the password
    return (password_verify("_" . strrev($string), strrev($hash_string)) ? true : false);
  }
 
private function grs($string_append = "", $length = 22) {
   $length = $length - strlen($string_append);
   $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&()_*,./;[]|';
   $randomString = '';
   for ($i = 0; $i < $length; $i++) {
       $randomString .= $characters[rand(0, strlen($characters) - 1)];
   }
   return $randomString . $string_append;
}

Okay so u use strrev on my string and hash just to make everything a bit more CONFUSING and i append the string with a "]" just to make the password harder to brute

the strrev and append string is not meant to make the hash any more secure.

 

I store the reversed hash in my DB as a varchar

 

The point of the reverse hash is only to make the hash a little more unrecognizable to the human eye.

 

 

The Const is randomly chosen 4 - 14, and the salt is randomly generated with a special string appended.

 

 

How would you improve the hashing?

Edited by Richard_Grant
Link to comment
Share on other sites

I would remove all of your code and use password_hash as it was intended.

 

I am using it exactly how it was intended.

http://php.net/manual/en/function.password-hash.php

 

PASSWORD_DEFAULT - Use the bcrypt algorithm (default as of PHP 5.5.0). Note that this constant is designed to change over time as new and stronger algorithms are added to PHP. For that reason, the length of the result from using this identifier can change over time. Therefore, it is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice).

PASSWORD_BCRYPT - Use the CRYPT_BLOWFISH algorithm to create the hash. This will produce a standard crypt() compatible hash using the "$2y$" identifier. The result will always be a 60 character string, or FALSE on failure.
Supported Options:
salt - to manually provide a salt to use when hashing the password. Note that this will override and prevent a salt from being automatically generated.
If omitted, a random salt will be generated by password_hash() for each password hashed. This is the intended mode of operation.
cost - which denotes the algorithmic cost that should be used. Examples of these values can be found on the crypt() page.
If omitted, a default value of 10 will be used. This is a good baseline cost, but you may want to consider increasing it depending on your hardware.

 

 

My password hashing works exactly the same way as intended only i wrote the random generating string myself (for a reason).

 

I chose to randomize the cost so that every user has a different cost to make it harder for someone to brute force my database. (cost can be found out by password_get_info)

 

 

Reversing the string and appending special character does NOT change the hashing functionality.

 

 

I was considering removing the Information part in the hash all together and ENCRYPTING it with a key then storing it in the DB separately. Example:

$2y$14$N1VbITdfWi9XfFdvcmRUbuAPbjVD3bQzrsObeoajoNHd2Hq5we7xm

 

The First part gives information about the hash,

$2y says its method is BCRYPT, while $14$ says its cost.

So if someone wanted to BRUTE my hash they would have information they would need.

 

So symmetric encrypting this part of the hash is reasonable.

 

Symmetric encrypting the hash information then turns the entire hash into a

Asymmetric encryption because now there are 2 keys to check if the password is correct, The blue and orange part of the hash are 2 separate keys.

Once the 2 keys are joined its now a hash and the password can be compared to the hash but the password can never be retrieved from the hash.

 

 

Hash - one way.

Encryption - Fancy way of hiding information you want to be able to read.

Link to comment
Share on other sites

  • Solution

Richard, the problem is that you don't understand how bcrypt works, yet at the same time you think you can “improve” it and outsmart everybody else. This does not work. The only person you've outsmarted is yourself.

 

By messing with the inner workings of bcrypt you've in fact massively weakened it:

 

The cryptographic salt must be a purely random 128-bit string from a high-entropy source encoded with a special Base64 alphabet. Your “salt” isn't any of this. It's not purely random, it's not from an appropriate source, and it's not encoded at all. What happens is this: Let's say you end up with a “salt” length of 22 characters. From those 22 characters, the last 14 belong to your strange “|WordToTheWise” suffix and don't have any effect at all (you don't seem to understand the purpose of a salt). So only 8 characters are left. Even worse, since “|” is not a valid Base64 digit, the password_hash() function thinks your salt consists of raw bytes. But of course that's not the case, each character only carries ~6 bits.

 

Long story short: You've reduced the salt from 128 random bits to roughly 51 halfway-random bits. That's a disaster.

 

Playing around with the password, reverting it and adding characters is also a very bad idea in the case of bcrypt. Since bcrypt has a maximum input length of 56 bytes, you may experience truncation. It's somewhat logical to take the first 56 bytes of the password and throw away the rest (if you tell your users about it). But what you do instead is take the last 56 bytes and throw away the rest. No user would expect that, so this is the recipe for disaster. Imagine a password which is padded with whitespace: In the worst case, you take the whitespace and throw away the actual password.

 

Apart from the severe technical mistakes, the entire approach is nonsense. This is a classical example of “security through obscurity”: You think you can improve security with confusing or even “secret” techniques.

 

This does not work. First of all, the confusion mostly hurts you, the programmer. The more obscure your code, the bigger the risk of making a mistake. And indeed that's exactly what happened. Secondly, attackers are not stupid. They know all your super-creative obfuscation techniques. Do you honestly believe that nobody before you has thought of reverting a string?

 

In security, we do not want obscurity. Quite the opposite: We want the code to be as clear and straightforward as possible to avoid bugs. If you spend your time coming up with obscure code, you're doing it wrong.

  • Like 1
Link to comment
Share on other sites

I would consider that reverse engineering in what you are doing, but that is just my opinion. If someone is going to brute force password_hash then they surely will have no problem brute-forcing your little add-on, again my opinion.  

 

There will always be the risk of BRUTE forcing to any type of security.

The point would be to hide the CRUCIAL information for brute forcing.

 

The brute forcer would need the $2y$14$ to brute force the hash.

The point here is to make it harder for them.

Link to comment
Share on other sites

Richard, the problem is that you don't understand how bcrypt works, yet at the same time you think you can “improve” it and outsmart everybody else. This does not work. The only person you've outsmarted is yourself.

 

By messing with the inner workings of bcrypt you've in fact massively weakened it:

 

The cryptographic salt must be a purely random 128-bit string from a high-entropy source encoded with a special Base64 alphabet. Your “salt” isn't any of this. It's not purely random, it's not from an appropriate source, and it's not encoded at all. What happens is this: Let's say you end up with a “salt” length of 22 characters. From those 22 characters, the last 14 belong to your strange “|WordToTheWise” suffix and don't have any effect at all (you don't seem to understand the purpose of a salt). So only 8 characters are left. Even worse, since “|” is not a valid Base64 digit, the password_hash() function thinks your salt consists of raw bytes. But of course that's not the case, each character only carries ~6 bits.

 

Long story short: You've reduced the salt from 128 random bits to roughly 51 halfway-random bits. That's a disaster.

 

Playing around with the password, reverting it and adding characters is also a very bad idea in the case of bcrypt. Since bcrypt has a maximum input length of 56 bytes, you may experience truncation. It's somewhat logical to take the first 56 bytes of the password and throw away the rest (if you tell your users about it). But what you do instead is take the last 56 bytes and throw away the rest. No user would expect that, so this is the recipe for disaster. Imagine a password which is padded with whitespace: In the worst case, you take the whitespace and throw away the actual password.

 

Apart from the severe technical mistakes, the entire approach is nonsense. This is a classical example of “security through obscurity”: You think you can improve security with confusing or even “secret” techniques.

 

This does not work. First of all, the confusion mostly hurts you, the programmer. The more obscure your code, the bigger the risk of making a mistake. And indeed that's exactly what happened. Secondly, attackers are not stupid. They know all your super-creative obfuscation techniques. Do you honestly believe that nobody before you has thought of reverting a string?

 

In security, we do not want obscurity. Quite the opposite: We want the code to be as clear and straightforward as possible to avoid bugs. If you spend your time coming up with obscure code, you're doing it wrong.

 

 

Im not trying to enhance the security at ALL,

A salt does NOT have to be a 128 bit purely random string.

It is recommended that a salt is ATLEAST 80bits and anything less is considered obsolete (the 22 digit string i assigned by a randomized definitely needs to be longer i agree)

I thank you for pointing out the BASE_64 issue.

 

So now i will take off the appending string and generate an entirely random salt (and there is no point of me using a random generate string)

So i am left with:

protected function create_hash($string){
    $hash = password_hash(strrev($string), PASSWORD_BCRYPT, array( 'cost'=>rand(14,18) )); //changed cost range to 14 - 18
    return strrev($hash);
}
public function verifyhash($string, $hash_string){//verifies that the hash is equal to the password
    return (password_verify(strrev($string), strrev($hash_string)) ? true : false);
}
  

And i am no longer considering encrypting the beginning of the hash.

 

 

 

I definitely agree i am over thinking a lot of things, that's why i made a post :) Even though your post was brutal THANK YOU :) keep it coming

 

 

 

 

I would remove all of your code and use password_hash as it was intended. 

I should have though harder about what you meant exactly.

Edited by Richard_Grant
Link to comment
Share on other sites

If you remove the strrev() as well, I think everybody is happy. ;)

 

Like I said above, it's dangerous to mess with the password. How do you know you can safely take the last 56 bytes of it and throw everything else away? When I enter a password, I expect it to be processed from left to right. And you have to admit it's a bit silly. If an attacker sees “...$y2$”, don't you think they can figure out that it's actually “$2y$...”?

 

Randomizing the cost factor also doesn't make a terrible lot of sense. The factor needs to be carefully chosen by you, the programmer. If you use a random number instead, that means a lot of hashes will be either weaker than necessary or too strong for your hardware. And there's absolutely no security benefit either. In the attack scenario bcrypt tries to protect against, the attacker knows all hash parameters.

 

Instead, make the cost factor configurable so that it can be changed if the application is moved to different hardware. For example, use a constant in a configuration script:

/*
 * The cost factor for the hashes of standard user accounts.
 *
 * Adjust this according to your hardware, the value of the user accounts and
 * the patience of your users. A high factor makes the hashes more robust
 * against brute force attacks, but it also stresses the CPU, slows down the
 * application and increases the risk of denial-of-service attacks.
 */
define('PASSWORD_STANDARD_COST', 12);

You can then use this constant instead of hard-coding some specific value into your code:

$password_hash = password_hash($password, PASSWORD_BCRYPT, array('cost' => PASSWORD_STANDARD_COST));
Link to comment
Share on other sites

 

The brute forcer would need the $2y$14$ to brute force the hash.

 

To brute-force by hash? What do you mean?

 

Using a hashing password would not prevent you from brute-forcing. Below is an example of brute-forcing over an html-form-based authentication to my account using php password_hash and bcrypt algorithm. More information and examples about the brute-force attack can be found here - https://www.owasp.org/index.php/Testing_for_Brute_Force_%28OWASP-AT-004%29

 

 

Hydra (http://www.thc.org/thc-hydra) starting at 2014-09-10 21:28:32

[WARNING] Restorefile (./hydra.restore) from a previous session found, to prevent overwriting, you have 10 seconds to abort...

[DATA] max 4 tasks per 1 server, overall 4 tasks, 479831 login tries (l:1/p:479831), ~29989 tries per task

[DATA] attacking service http-post-form on port 80

[VERBOSE] Resolving addresses ... done

[sTATUS] 696.00 tries/min, 696 tries in 00:01h, 479135 todo in 11:29h, 4 active

[sTATUS] 587.00 tries/min, 1761 tries in 00:03h, 478070 todo in 13:35h, 4 active

[sTATUS] 657.57 tries/min, 4603 tries in 00:07h, 475228 todo in 12:03h, 4 active

[80][www-form] host: 192.168.122.2   login: jazzman   password: phpfreaks

[sTATUS] attack finished for 192.168.122.2 (waiting for children to complete tests)

1 of 1 target successfully completed, 1 valid password found

Hydra (http://www.thc.org/thc-hydra) finished at 2014-09-10 21:38:09

Link to comment
Share on other sites

I find this comment rather misleading.

 

Of course bcrypt does not magically block brute-force attacks or turn bad passwords into good ones. Nobody made this claim. The point is that bcrypt massively increases the resources needed for an attack. If we use MD5, then an attacker can try billions of hashes per second on stock hardware. If we use bcrypt, they're down to maybe one password per second (depending on the cost factor).

 

This means if you choose a decent(!) password, then bcrypt makes a brute-force attack infeasible. Yes, you do need a good password. If you use “phpfreaks”, then no algorithm on this planet can help you.

 

Secondly, the goal of hash algorithms is to protect the passwords against offline attacks where the attacker has obtained the raw data and can attack it directly. Your example shows an online attack against the log-in form of the application. That's not what bcrypt is about. To protect yourself against online attacks, you don't need hashes at all. It's enough to artificially slow down or limit the log-in attempts.

Link to comment
Share on other sites

I find this comment rather misleading.

 

Of course bcrypt does not magically block brute-force attacks or turn bad passwords into good ones. Nobody made this claim. The point is that bcrypt massively increases the resources needed for an attack. If we use MD5, then an attacker can try billions of hashes per second on stock hardware. If we use bcrypt, they're down to maybe one password per second (depending on the cost factor).

 

This means if you choose a decent(!) password, then bcrypt makes a brute-force attack infeasible. Yes, you do need a good password. If you use “phpfreaks”, then no algorithm on this planet can help you.

 

Secondly, the goal of hash algorithms is to protect the passwords against offline attacks where the attacker has obtained the raw data and can attack it directly. Your example shows an online attack against the log-in form of the application. That's not what bcrypt is about. To protect yourself against online attacks, you don't need hashes at all. It's enough to artificially slow down or limit the log-in attempts.

 

 

You always have the best answers.

Link to comment
Share on other sites

I didn't say nothing against of using the bcrypt algorithm. I just didn't understand what OP meant by saying that? Jacques, I find you pretty freaky and very sensitive when speaking for security....just relax ... mate...relax ;)

 

 

You always have the best answers.

 

Yeah....agreed...he is very good.

Edited by jazzman1
Link to comment
Share on other sites

I didn't say nothing against of using the bcrypt algorithm. I just didn't understand what OP meant by saying that?

 

OK. But you have to admit that a demonstration which breaks a bcrypt hash in 7 seconds is slightly misleading. If an uninformed user reads this, they may very well conclude that bcrypt is useless and not worth the trouble.

 

You do bring up a valid point, though: Password strength is important, no matter what algorithm you use.

 

So it was just a clarification. Don't forget that the Internet is full of wrong information, and many programmers don't know anything about hashing. If they read about it on this forum, I think it's important that they don't get any wrong impressions. They shouldn't leave with the thought: “A guru at PHP Freaks has broken bcrypt in 7 seconds, so I think I'll stick to MD5 for now.” ;)

Link to comment
Share on other sites

I am sorry for any misunderstanding. This testing brute-force was against my account located in my local server that's why the speed of the attack was very high. In the reality it's slightly different ;) So, make sure you have a strong password and algorithm for it.

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.