什么可能导致crypt产生错误的验证?

时间:2015-08-22 22:38:09

标签: php

我遇到了我正在建设的门户网站的问题,并决定通过小型压力测试进一步调查。该测试为一个指定密码生成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;
}

现在,在显示代码之后。我的输出如下(多次刷新)

  
      
  1. 4000次检查22次虚假退货&amp; 3978回报

  2.   
  3. 4000次检查15次虚假退货&amp; 3985回报

  4.   
  5. 4000次检查15次虚假退货&amp; 3985回报

  6.   
  7. 4000次检查10次虚假退货&amp; 3990回报

  8.   
  9. 4000次检查6次虚假退货&amp; 3994回报

  10.   
  11. 10000次检查40次虚假退货&amp; 9960真实回报

  12.   
  13. 10000次检查43次虚假退货&amp; 9957真实的回报

  14.   
  15. 50000次检查196次虚假退货&amp; 49804真实的回报

  16.   

尽管失败率很低。问题仍然存在。关于密码加密,对于所有密码比较,这不应该是100%吗?

所以,总体问题是:什么可能导致这种影响?它可能是我的编码吗?或完全无瑕的PHP中的缺陷?

1 个答案:

答案 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";

快乐编码