I encountered a problem with a portal I am building, and decided to investigate further with a small stress test. This test, produced 4,000/10,000/50,000 different salts for one specified password. The code follows:
$Incline = 0;
$Max = 4000;
$Auth = new Authentication();
$FalseCounter = 0;
$TrueCounter = 0;
while ($Incline < $Max){
$PasswordString = "1";
$Encrypted_Pass = $Auth->Hash_Password($PasswordString);
$Check = crypt($PasswordString,$Encrypted_Pass['Salt']);
if ($Check === $Encrypted_Pass['Password']){
$TrueCounter++;
}else{
$FalseCounter++;
}
if ($Incline === $Max){
break;
}
$Incline++;
}
echo 'Of '.$Max.' Checks '.$FalseCounter.' False Returns & '.$TrueCounter.' True Returns';
With $Auth->Hash_Password
being:
public $Salt = null;
public function SetSalt(){
$ByteSize = mcrypt_get_iv_size(MCRYPT_CAST_256, MCRYPT_MODE_CFB);
$Salt = mcrypt_create_iv($ByteSize, MCRYPT_DEV_RANDOM);
$this->Salt = $Salt;
if (!is_null($this->Salt)){
return true;
}
return false;
}
public function Hash_Password($Password){
$this->SetSalt();
$Return_Array = array();
$Return_Array['Salt'] = $this->Salt;
$Return_Array['Password'] = crypt($Password,$this->Salt);
return $Return_Array;
}
Now, after showing the code. My output is as follows (on multiple refreshes)
Of 4000 Checks 22 False Returns & 3978 True Returns
Of 4000 Checks 15 False Returns & 3985 True Returns
Of 4000 Checks 15 False Returns & 3985 True Returns
Of 4000 Checks 10 False Returns & 3990 True Returns
Of 4000 Checks 6 False Returns & 3994 True Returns
Of 10000 Checks 40 False Returns & 9960 True Returns
Of 10000 Checks 43 False Returns & 9957 True Returns
Of 50000 Checks 196 False Returns & 49804 True Returns
Despite being a minute fail rate. The problem is still there. Regarding password encryption, Should this not be 100% for all password comparisons?
So, overall question is: What could cause this type of affect? Could it possibly be my coding? Or a flaw within the completely flawless PHP?
For a public blockchain, the decision to add a transaction to the chain is made by consensus. This means that the majority of “nodes” (or computers in the network) must agree that the transaction is valid. The people who own the computers in the network are incentivised to verify transactions through rewards.
Mining transactions are validated digitally on the bitcoin network you use and add to the blockchain ledger. It is done by solving complex cryptographic hash puzzles to verify blocks of transactions updated on the decentralized blockchain ledger.
What makes this system theoretically tamperproof is two things: a cryptographic fingerprint unique to each block, and a “consensus protocol,” the process by which the nodes in the network agree on a shared history. The fingerprint, called a hash, takes a lot of computing time and energy to generate initially.
Under PoS, block creators are called validators. A validator checks transactions, verifies activity, votes on outcomes, and maintains records. Under PoW, block creators are called miners. Miners work to solve for the hash, a cryptographic number, to verify transactions.
It seems crypt is affected by "null byte poisoning". All tests will pass, If you change your SetSalt method to this:
<?php
public function SetSalt()
{
$ByteSize = mcrypt_get_iv_size(MCRYPT_CAST_256, MCRYPT_MODE_CFB);
do {
$Salt = mcrypt_create_iv($ByteSize, MCRYPT_DEV_RANDOM);
// Remove null byte from salt
$this->Salt = str_replace(chr(0), '', $Salt);
} while ($this->Salt !== $Salt); // Retry until salt without null byte is generated
if (!is_null($this->Salt)) {
return true;
}
return false;
}
After that all tests pass:
$ php crypt_test.php
Of 4000 Checks 0 False Returns & 4000 True Returns
If you want to read more about null bytes you can start here: http://www.madirish.net/401
For better illustration, here is an example output of a failing test:
string(34) "$1$iyJhOmt2$23uOXEcjWr2GcjSMqKpHk0"
array(2) {
'Salt' =>
string(16) "\000g-Ŕ=(
��A��n0"
'Password' =>
string(34) "$1$QCbFiEDR$g3RDS7LK3m88K7XPqjF5O."
}
You can see the null byte here: \0
.
And for all lazy people out there, here is the complete (fixed) test script I used ;)
<?php
class Authentication
{
public $Salt = null;
public function SetSalt()
{
$ByteSize = mcrypt_get_iv_size(MCRYPT_CAST_256, MCRYPT_MODE_CFB);
do {
$Salt = mcrypt_create_iv($ByteSize, MCRYPT_DEV_RANDOM);
// Remove null byte from salt
$this->Salt = str_replace(chr(0), '', $Salt);
} while ($this->Salt !== $Salt);
if (!is_null($this->Salt)) {
return true;
}
return false;
}
public function Hash_Password($Password)
{
$this->SetSalt();
$Return_Array = array();
$Return_Array['Salt'] = $this->Salt;
$Return_Array['Password'] = crypt($Password, $this->Salt);
return $Return_Array;
}
}
$Incline = 0;
$Max = 4000;
$Auth = new Authentication();
$FalseCounter = 0;
$TrueCounter = 0;
while ($Incline < $Max) {
$PasswordString = "1";
$Encrypted_Pass = $Auth->Hash_Password($PasswordString);
$Check = crypt($PasswordString, $Encrypted_Pass['Salt']);
if ($Check === $Encrypted_Pass['Password']) {
$TrueCounter++;
} else {
var_dump($Encrypted_Pass, $Check);
$FalseCounter++;
}
if ($Incline === $Max) {
break;
}
$Incline++;
}
echo 'Of ' . $Max . ' Checks ' . $FalseCounter . ' False Returns & ' . $TrueCounter . ' True Returns' . "\n";
Happy coding
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With