2012-10-25 1 views
1

보안에 대한 이론 및 기술적 인 논쟁으로 인해 새로운 응용 프로그램에 phpass을 구현하려고합니다. 그것은 물론 유일한 보안은 아니지만 암호 해싱을 위해 이것은 나에게 최상으로 보이는 것 같습니다.phpass의 최신 버전/개선 된 버전/대체 버전이 있습니까?

그러나 소스 코드를 보면 정말 만족스럽지 않습니다. 가장 기본적인 코드 규칙에 위배됩니다. 보안과 관련하여 특히 그렇습니다.

return $hash == $stored_hash; 

나는 항상 보안 주위 코드에 동일한 연산자 (===)를 사용합니다.

if ($hash[0] == '*') 
    $hash = crypt($password, $stored_hash); 

나는 항상하다면 중괄호에게 {를 사용합니다. 가능한 많은 사람들이 같은 줄에 있는지는 중요하지 않지만 가능한 경우에도 그 줄을 생략하면 나에게 좋은 느낌이 들지 않습니다.

또한 CRYPT_BLOWFISH 메서드를 강제 적용 할 방법이 없습니다. 지금 당장 필자는 자신의 래퍼에서 길이가 정확히 60 자로되어 있는지 확인하여이를 수행합니다.

누군가 내가 업데이트 된 버전을 알고 있는지 궁금한가요? 더 큰 공동체가 유지하고 점검 한 개선 된 것? 예를 들어 버퍼링되지 않은 읽기 PHP5의 모든 가능한 사용을 만드는 최신 버전 as the author himself already suggests?

아니면 그냥 진짜 이유없이 편집증에 걸렸을 수도 있습니다.

답변

2

새로운 응용 프로그램 인 경우 암호 해시에 https://github.com/ircmaxell/password_compat을 사용하십시오. PHP> = 5.3.7에서 작동하며 PHP 5.5의 향후 암호 기능을 다시 구현합니다.

암호 해시 보안에 대한 대안은 없습니다.

+0

가 동의했다. @ ircmaxell의 lib는 갈 길입니다. – rdlowrey

1

내 게시물은 이전 버전과의 호환성을 위해 phpass를 선호하지만 개선 된 버전을 사용하고 싶습니다.

나는 phpass에 다음과 같은 향상된 기능을 사용

  • 는 constructor에에 $의 random_state에 더 많은 데이터를 소개합니다.
  • 더 많은 메소드를 추가하고 더 많은 데이터를 엔트로피 풀에 추가하여 get_random_bytes()를 개선하십시오.
    일부 코드는 SecurityMultiTool by padraic이고 ircmaxell의 RandomLib 및 password_compat입니다.
  • PHP_VERSION> = 5.3.7 인 경우 gensalt_blowfish()에서 고정 된 새로운 BLOWFISH salt id를 사용하십시오.

전체 코드는 업데이트 주석의 버전이 변경 추가 :

<?php 
# 
# Portable PHP password hashing framework. 
# 
# Version 0.3/okaresz.v1 
# 
# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in 
# the public domain. Revised in subsequent years, still public domain. 
# 
# There's absolutely no warranty. 
# 
# The homepage URL for this framework is: 
# 
# http://www.openwall.com/phpass/ 
# 
# Please be sure to update the Version line if you edit this file in any way. 
# It is suggested that you leave the main version number intact, but indicate 
# your project name (after the slash) and add your own revision information. 
# 
# Please do not change the "private" password hashing method implemented in 
# here, thereby making your hashes incompatible. However, if you must, please 
# change the hash type identifier (the "$P$") to something different. 
# 
# Obviously, since this code is in the public domain, the above are not 
# requirements (there can be none), but merely suggestions. 
# 
# okaresz.v1: 
# 
# * Introducing more data to $random_state in the constuctor. 
# * Improve get_random_bytes() by adding more methods and by introducing more data to the entropy pool. 
# Some fo the code is from the SecurityMultiTool by padraic: https://github.com/padraic/SecurityMultiTool 
# and from ircmaxell's RandomLib: https://github.com/ircmaxell/RandomLib 
# and password_compat https://github.com/ircmaxell/password_compat 
# * In gensalt_blowfish() if PHP_VERSION >= 5.3.7, use the new, fixed BLOWFISH salt id. 
# 
class PasswordHash { 
    var $itoa64; 
    var $iteration_count_log2; 
    var $portable_hashes; 
    var $random_state; 

    function PasswordHash($iteration_count_log2, $portable_hashes) 
    { 
     $this->itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; 

     if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) 
      $iteration_count_log2 = 8; 
     $this->iteration_count_log2 = $iteration_count_log2; 

     $this->portable_hashes = $portable_hashes; 

     $this->random_state = microtime(); 
     if (function_exists('getmypid')) 
      $this->random_state .= getmypid(); 
     $this->random_state .= md5(serialize($_SERVER)); 
    } 

    function get_random_bytes($count) 
    { 
     $output = ''; 

     if(function_exists('openssl_random_pseudo_bytes')) 
     { 
      $output = openssl_random_pseudo_bytes($count, $usable); 
      if (true === $usable) { 
       return $output; 
      } 
     } 
     if(function_exists('mcrypt_create_iv') 
      && (version_compare(PHP_VERSION, '5.3.0') >= 0) 
      || (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') 
      && !defined('PHALANGER') 
     ) { 
      $output = mcrypt_create_iv($count, MCRYPT_DEV_URANDOM); 
      if ($output !== false && strlen($output) === $count) { 
       return $output; 
      } 
     } 

     if (is_readable('/dev/urandom') && 
      ($fh = @fopen('/dev/urandom', 'rb'))) { 
      $output = fread($fh, $count); 
      fclose($fh); 
     } 

     if (strlen($output) < $count) { 
      $output = ''; 
      $seed = microtime() . memory_get_usage(); 
      if(function_exists('gc_collect_cycles')) 
       { gc_collect_cycles(); } 
      else 
      { 
       $i = 0; 
       while($i < 32) 
        { $i += 1+(int)round(lcg_value()); } 
      } 

      $this->random_state .= $seed . microtime(); 
      for ($i = 0; $i < $count; $i += 16) 
      { 

       $this->random_state = md5(microtime() . $this->random_state); 
       $output .= pack('H*', md5(substr($this->random_state,0,16))); 
      } 
      $output = substr($output, 0, $count); 
     } 

     return $output; 
    } 

    function encode64($input, $count) 
    { 
     $output = ''; 
     $i = 0; 
     do { 
      $value = ord($input[$i++]); 
      $output .= $this->itoa64[$value & 0x3f]; 
      if ($i < $count) 
       $value |= ord($input[$i]) << 8; 
      $output .= $this->itoa64[($value >> 6) & 0x3f]; 
      if ($i++ >= $count) 
       break; 
      if ($i < $count) 
       $value |= ord($input[$i]) << 16; 
      $output .= $this->itoa64[($value >> 12) & 0x3f]; 
      if ($i++ >= $count) 
       break; 
      $output .= $this->itoa64[($value >> 18) & 0x3f]; 
     } while ($i < $count); 

     return $output; 
    } 

    function gensalt_private($input) 
    { 
     $output = '$P$'; 
     $output .= $this->itoa64[min($this->iteration_count_log2 + 
      ((PHP_VERSION >= '5') ? 5 : 3), 30)]; 
     $output .= $this->encode64($input, 6); 

     return $output; 
    } 

    function crypt_private($password, $setting) 
    { 
     $output = '*0'; 
     if (substr($setting, 0, 2) == $output) 
      $output = '*1'; 

     $id = substr($setting, 0, 3); 
     # We use "$P$", phpBB3 uses "$H$" for the same thing 
     if ($id != '$P$' && $id != '$H$') 
      return $output; 

     $count_log2 = strpos($this->itoa64, $setting[3]); 
     if ($count_log2 < 7 || $count_log2 > 30) 
      return $output; 

     $count = 1 << $count_log2; 

     $salt = substr($setting, 4, 8); 
     if (strlen($salt) != 8) 
      return $output; 

     # We're kind of forced to use MD5 here since it's the only 
     # cryptographic primitive available in all versions of PHP 
     # currently in use. To implement our own low-level crypto 
     # in PHP would result in much worse performance and 
     # consequently in lower iteration counts and hashes that are 
     # quicker to crack (by non-PHP code). 
     if (PHP_VERSION >= '5') { 
      $hash = md5($salt . $password, TRUE); 
      do { 
       $hash = md5($hash . $password, TRUE); 
      } while (--$count); 
     } else { 
      $hash = pack('H*', md5($salt . $password)); 
      do { 
       $hash = pack('H*', md5($hash . $password)); 
      } while (--$count); 
     } 

     $output = substr($setting, 0, 12); 
     $output .= $this->encode64($hash, 16); 

     return $output; 
    } 

    function gensalt_extended($input) 
    { 
     $count_log2 = min($this->iteration_count_log2 + 8, 24); 
     # This should be odd to not reveal weak DES keys, and the 
     # maximum valid value is (2**24 - 1) which is odd anyway. 
     $count = (1 << $count_log2) - 1; 

     $output = '_'; 
     $output .= $this->itoa64[$count & 0x3f]; 
     $output .= $this->itoa64[($count >> 6) & 0x3f]; 
     $output .= $this->itoa64[($count >> 12) & 0x3f]; 
     $output .= $this->itoa64[($count >> 18) & 0x3f]; 

     $output .= $this->encode64($input, 3); 

     return $output; 
    } 

    function gensalt_blowfish($input) 
    { 
     # This one needs to use a different order of characters and a 
     # different encoding scheme from the one in encode64() above. 
     # We care because the last character in our encoded string will 
     # only represent 2 bits. While two known implementations of 
     # bcrypt will happily accept and correct a salt string which 
     # has the 4 unused bits set to non-zero, we do not want to take 
     # chances and we also do not want to waste an additional byte 
     # of entropy. 
     $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; 

     $output = (version_compare(PHP_VERSION, '5.3.7') >= 0)? '$2y$' : '$2a$'; 
     $output .= chr(ord('0') + $this->iteration_count_log2/10); 
     $output .= chr(ord('0') + $this->iteration_count_log2 % 10); 
     $output .= '$'; 

     $i = 0; 
     do { 
      $c1 = ord($input[$i++]); 
      $output .= $itoa64[$c1 >> 2]; 
      $c1 = ($c1 & 0x03) << 4; 
      if ($i >= 16) { 
       $output .= $itoa64[$c1]; 
       break; 
      } 

      $c2 = ord($input[$i++]); 
      $c1 |= $c2 >> 4; 
      $output .= $itoa64[$c1]; 
      $c1 = ($c2 & 0x0f) << 2; 

      $c2 = ord($input[$i++]); 
      $c1 |= $c2 >> 6; 
      $output .= $itoa64[$c1]; 
      $output .= $itoa64[$c2 & 0x3f]; 
     } while (1); 

     return $output; 
    } 

    function HashPassword($password) 
    { 
     $random = ''; 

     if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { 
      $random = $this->get_random_bytes(16); 
      $hash = 
       crypt($password, $this->gensalt_blowfish($random)); 
      if (strlen($hash) == 60) 
       return $hash; 
     } 

     if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { 
      if (strlen($random) < 3) 
       $random = $this->get_random_bytes(3); 
      $hash = 
       crypt($password, $this->gensalt_extended($random)); 
      if (strlen($hash) == 20) 
       return $hash; 
     } 

     if (strlen($random) < 6) 
      $random = $this->get_random_bytes(6); 
     $hash = 
      $this->crypt_private($password, 
      $this->gensalt_private($random)); 
     if (strlen($hash) == 34) 
      return $hash; 

     # Returning '*' on error is safe here, but would _not_ be safe 
     # in a crypt(3)-like function used _both_ for generating new 
     # hashes and for validating passwords against existing hashes. 
     return '*'; 
    } 

    function CheckPassword($password, $stored_hash) 
    { 
     $hash = $this->crypt_private($password, $stored_hash); 
     if ($hash[0] == '*') 
      $hash = crypt($password, $stored_hash); 

     return $hash == $stored_hash; 
    } 
} 

?>