2010-04-07 6 views
11

phpass è un "framework" di hashing ampiamente utilizzato.
E 'buona norma sale la password pianura prima di darlo ai PasswordHash (v0.2), in questo modo ?:Il sale è contenuto in un hash phpass o è necessario salarne l'input?

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

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

Per riferimento alla classe 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; 
    } 
} 

risposta

32

Questa è una risposta da parte dell'autore originale se stesso:

Oltre l'hashing reale, phpass genera in modo trasparente sali casuali quando una nuova password o passphrase viene assegnata, e codifica il tipo di hash , il sale e il conteggio delle iterazioni della password nella stringa di codifica "hash" restituita. Quando phpass autentica una password o una passphrase con un hash memorizzato, allo stesso modo è trasparente negli estratti e utilizza l'identificatore del tipo di hash, il salt e il conteggio di iterazione dalla "stringa di codifica hash". Pertanto, non è necessario disturbare con salatura e stretching da soli - phpass si occupa di questi per tu.

Bottom line: non ha senso inserire la password prima di "phpassing".

1

È non hanno davvero bisogno di due sali (cioè il sale statico è ridondante, il sale dinamico è abbondante) - lo scopo principale di un sale è prevenire attacchi da tavolo arcobaleno se gli hash vengono mai acquisiti da una parte malintenzionata, e il motivo per i sali dinamici servono a prevenire ulteriormente il rainbo del caso speciale w generazione di tabelle rompendo tutte le password simultaneamente.

A prescindere da ciò, non può fare male salare indipendentemente dal fatto che la libreria abbia salato o meno (anche se a meno che non lo si passi più informazioni rispetto al solo oggetto da sottoporre a hash, in realtà non lo fa avere qualcosa da usare come un sale dinamico, quindi è probabile che sia non è sale se non è già evidente che lo fa).

+0

'(anche se a meno che non lo si passi più informazioni che solo l'elemento da sottoporre a hash, in realtà non ha nulla da usare come sale dinamico, quindi è probabile che non sale per te se non lo è già ovvio che lo fa). Sto ancora cercando di analizzare questa frase ... :) Potresti spiegare cosa intendi? –

+0

In alcuni casi un doppio sale può essere legittimo. Per esempio se sei preoccupato che uno dei sali sia stato ottenuto da un aggressore. Ad esempio se 1 sale è memorizzato nel database allora potrebbe essere ottenuto con SQL injection, un altro sale potrebbe essere memorizzato in un file flat che è più difficile da ottenere. Un hash della password non può essere interrotto fino a quando non si ottengono tutti i sali, una volta che i sali sono stati ottenuti, è banale da rompere usando un attacco del dizionario come John the Ripper. – rook

+0

@Exception: partendo dal presupposto che si desideri il vantaggio di un sale dinamico, è necessario qualche informazione non correlata all'elemento attualmente sottoposto a hash, ma che viene archiviato per il momento in cui si desidera ricostituire l'hash. @ TheRook, i sali non sono pensati per essere oggetti nascosti. Se qualcuno riesce a ottenere l'hash, in primo luogo, provare davvero a nascondere il sale non ti comprerà molto; avresti dovuto nascondere l'hash meglio. Tieni presente che se qualcuno può leggere il tuo codice per sapere in che modo dovrebbero incorporare il sale, in primo luogo, può leggere qualsiasi file piatto. – Amber