2010-04-07 36 views
11

phpass là một khuôn khổ băm được sử dụng rộng rãi.
Có thực hành tốt để muối mật khẩu đồng bằng trước khi đưa nó vào PasswordHash (v0.2), như vậy ?:Là muối chứa trong một băm phpass hoặc bạn cần phải muối đầu vào của nó?

$dynamicSalt = $record['salt']; 
$staticSalt = 'i5ininsfj5lt4hbfduk54fjbhoxc80sdf'; 
$plainPassword = $_POST['password']; 
$password  = $plainPassword . $dynamicSalt . $staticSalt; 

$passwordHash = new PasswordHash(8, false); 
$storedPassword = $passwordHash->HashPassword($password); 

Để tham khảo lớp phpsalt:

# Portable PHP password hashing framework. 
# 
# Version 0.2/genuine. 
# 
# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in 
# the public domain. 
# 
# 
# 
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() . getmypid(); 
    } 

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

     if (strlen($output) < $count) { 
      $output = ''; 
      for ($i = 0; $i < $count; $i += 16) { 
       $this->random_state = 
        md5(microtime() . $this->random_state); 
       $output .= 
        pack('H*', md5($this->random_state)); 
      } 
      $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'; 

     if (substr($setting, 0, 3) != '$P$') 
      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 = '$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; 
    } 
} 

Trả lời

32

Đây là một câu trả lời từ tác giả ban đầu mình:

Bên cạnh băm thực tế, phpass minh bạch tạo ra muối ngẫu nhiên khi một mật khẩu mới hoặc cụm từ mật khẩu được băm, và nó mã hóa kiểu băm , muối và số lần lặp lại mật khẩu kéo dài vào chuỗi mã hóa băm "mà nó trả về. Khi phpass xác thực mật khẩu hoặc cụm mật khẩu đối với băm được lưu trữ, nó tương tự minh bạch chiết xuất và sử dụng mã định danh loại băm, muối và phép lặp tính từ "chuỗi mã hóa băm". Do đó, bạn không cần phải bận tâm bằng cách ướp muối và kéo dài theo cách riêng của bạn - phpass sẽ chăm sóc những điều này cho bạn.

Điểm mấu chốt: không có ý nghĩa khi nhập mật khẩu của bạn trước khi "bỏ qua".

1

Bạn không thực sự cần hai muối (tức là muối tĩnh là thừa, muối động là rất nhiều) - mục đích chính của muối là ngăn chặn các cuộc tấn công của bảng cầu vồng nếu băm được mua lại bởi một bên độc hại, và lý do các muối động là để ngăn chặn hơn nữa trường hợp đặc biệt rainbo w bảng thế hệ từ phá vỡ tất cả các mật khẩu cùng một lúc.

Ngoài ra, nó không thể gây hại cho muối bất kể thư viện có được tích hợp muối hay không (mặc dù trừ khi bạn chuyển thông tin đó nhiều hơn chỉ mục được băm, nó không thực sự có bất cứ điều gì để sử dụng như một muối động, vì vậy rất có thể là nó không muối cho bạn nếu nó không phải là đã rõ ràng rằng nó không).

+0

'(mặc dù trừ khi bạn chuyển thông tin nhiều hơn chỉ mục được băm, nó không thực sự có bất kỳ thứ gì để sử dụng làm muối động, vì vậy rất có thể nó không có muối cho bạn nếu nó chưa rõ ràng là nó có) .' Tôi vẫn đang cố gắng phân tích câu này ... :) Bạn có thể giải thích ý của bạn không? –

+0

Một muối đôi có thể hợp pháp trong một số trường hợp. Ví dụ, nếu bạn đang lo lắng rằng một trong những muối có thể thu được bởi một kẻ tấn công. Ví dụ nếu 1 muối được lưu trữ trong cơ sở dữ liệu của anh ta thì nó có thể thu được với tiêm sql, một muối khác có thể được lưu trữ trong một tệp phẳng khó khăn hơn để có được. Một mật khẩu băm không thể bị phá vỡ cho đến khi tất cả các muối thu được, một khi các muối thu được tầm thường của nó để phá vỡ bằng cách sử dụng một cuộc tấn công từ điển như John the Ripper. – rook

+0

@Exception: giả sử bạn muốn lợi ích của một muối động, bạn cần một số thông tin không liên quan đến mục thực sự được băm, nhưng được lưu trữ khi bạn muốn băm lại để so sánh. @ TheRook, muối không có nghĩa là để được ẩn mục. Nếu ai đó có thể nhận được băm ở nơi đầu tiên, sau đó thực sự cố gắng để ẩn muối sẽ không mua cho bạn nhiều; bạn nên chỉ cần giấu băm tốt hơn. Hãy nhớ rằng nếu ai đó có thể đọc mã của bạn để biết cách họ nên kết hợp muối ngay từ đầu, họ có thể đọc bất kỳ tệp phẳng nào. – Amber

Các vấn đề liên quan