我遇到了我正在建设的门户网站的问题,并决定通过小型压力测试进一步调查。该测试为一个指定密码生成4,000 / 10,000 / 50,000个不同的盐。代码如下:
$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';
$Auth->Hash_Password
为:
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;
}
现在,在显示代码之后。我的输出如下(多次刷新)
4000次检查22次虚假退货&amp; 3978回报
4000次检查15次虚假退货&amp; 3985回报
4000次检查15次虚假退货&amp; 3985回报
4000次检查10次虚假退货&amp; 3990回报
4000次检查6次虚假退货&amp; 3994回报
10000次检查40次虚假退货&amp; 9960真实回报
10000次检查43次虚假退货&amp; 9957真实的回报
- 醇>
50000次检查196次虚假退货&amp; 49804真实的回报
尽管失败率很低。问题仍然存在。关于密码加密,对于所有密码比较,这不应该是100%吗?
所以,总体问题是:什么可能导致这种影响?它可能是我的编码吗?或完全无瑕的PHP中的缺陷?
答案 0 :(得分:9)
似乎地穴受到&#34;空字节中毒&#34;的影响。所有测试都将通过,如果您将SetSalt方法更改为:
<?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;
}
之后所有测试都通过:
$ php crypt_test.php
Of 4000 Checks 0 False Returns & 4000 True Returns
如果您想了解有关空字节的更多信息,可以从这里开始:http://www.madirish.net/401
为了更好地说明,这是失败测试的示例输出:
string(34) "$1$iyJhOmt2$23uOXEcjWr2GcjSMqKpHk0"
array(2) {
'Salt' =>
string(16) "\000g-Ŕ=(
��A��n0"
'Password' =>
string(34) "$1$QCbFiEDR$g3RDS7LK3m88K7XPqjF5O."
}
您可以在此处看到空字节:\0
。
对于所有懒人,这里是我使用的完整(固定)测试脚本;)
<?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";
快乐编码