Jörg Reinholz: Kleine Bibliothek für Passwort-Hash-Erzeugug und Verifizierung

Beitrag lesen

Bei mt_rand() soll nur die Verteilung und die Geschwindigkeit besser sein als beim gewöhnlichen rand(). Es erfüllt trotzdem nicht die Ansprüche eines kryptografischen Zufalls, diese Eigenschaften enthält man in der Regel nur dann, wenn die Zufallsdaten von einer nicht deterministischen Entropiequelle gespeist werden.

Das ist hier aber nicht notwendig. Es ist nur notwendig, möglichst viele _verschiedene_ Salts zu haben. Es gilt auch nicht als Problem, dass mit dem Hash auch der Salt bekannt wird. Demnach kann es auch nicht ein Problem sein, wenn man - mit immer noch ziemlich geringer Wahrscheinlichkeit des Treffers - spekulieren kann, welcher Salt wohl zu jeden vollen 10, 20, 30, 40 oder 50 Sekunden nach Rechnerstart generiert wird.

Wenn ein Angreifer jetzt hingänge und "abermilliarden" gehaschte Passwörter von abertausenden Quellen zusammentragen würde, dann könnte für diesen womöglich ein Vorteil entstehen. Der zerfällt aber gleich wieder, weil diese vielen verschiedenen Quellen natürlich andere Ausgangsdaten für deterministischen Entropiequellen haben. Auf deutsch gesagt: Es nützt dem Angreifer nichts. Jedenfalls nicht so lange rand() oder mt_rand RICHTIG schlecht wären so dass Salts gehäuft identisch sind. Das findet aber nicht statt.

Ich habe das mal getestet:

<?php  
$bytes=1024 * 1024 * 1024;  
print "Generiere $bytes Bytes große Dateien.\n";  
  
file_put_contents('/tmp/open_ssl_test.bin', openssl_random_pseudo_bytes($bytes));  
  
$bin=''; $i=0;  
while ($i < $bytes) {  
    $bin .= chr(rand(0,255));  
    $i++;  
}  
file_put_contents('/tmp/test.bin',$bin);  
  
$bin=''; $i=0;  
while ($i < $bytes) {  
    $bin .= chr(mt_rand(0,255));  
    $i++;  
}  
file_put_contents('/tmp/mt_test.bin',$bin);

Mit diesem Skript habe ich die Dateien erzeugt. Die sind jeweils 1 GigaByte groß:

fastix@trainer:/tmp$ php test.php
Generiere 1073741824 Bytes große Dateien.
fastix@trainer:/tmp$ ll *test.bin*
-rw-rw-r-- 1 fastix fastix 1073741824 Dez 23 17:58 mt_test.bin
-rw-rw-r-- 1 fastix fastix 1073741824 Dez 23 17:42 open_ssl_test.bin
-rw-rw-r-- 1 fastix fastix 1073741824 Dez 23 17:50 test.bin
fastix@trainer:/tmp$ gzip *test.bin*
fastix@trainer:/tmp$ ll *test.bin*
-rw-rw-r-- 1 fastix fastix 1073915362 Dez 23 17:58 mt_test.bin.gz
-rw-rw-r-- 1 fastix fastix 1073915392 Dez 23 17:42 open_ssl_test.bin.gz
-rw-rw-r-- 1 fastix fastix 1073915637 Dez 23 17:50 test.bin.gz

In allen drei Varianten war die Datei nach dem Packen größer. Da beim Packen nach irgendwelchen Regelmäßigkeiten bzw. Wiederholungen gesucht wird, die dann mehr oder weniger in eine mathematische Formel gepackt werden um die Datei zu verkleinern ist gzip bei solchen Zufallssachen ein ganz guter Test. So, wie das hier aussieht, sind weder mt_rand() noch rand() signifikant schlechter als openssl.

Unstreitig hat die Erzeugung mit openssl_random_pseudo_bytes() am wenigsten Zeit gebraucht, mit rand() an längsten gedauert.

Das sähe anders aus, wenn man Datenverkehr verschlüsselt und dafür Zufallszahlen braucht (echtes Verschlüsseln). Dann sind erstens die Datenmengen größer und zum zeitens lässt sich genauer bestimmen, was wann und in welcher Reihenfolge verschlüsselt war.

Wie gesagt, beim Hashen (das ja auch noch nach Anfall, z.B. beim Eintragen oder Ändern eines Passwortes stattfindet, wobei sich die deterministischen Entropiequellen unvorhersehbar bzw. nicht nachvollziehbar entwickelt haben) kommt es nur darauf an, den Angreifer mit einer Vielzahl von verschiedenen Salts zu konfrontieren um ihn zu zwingen, die Rainbowtables für jeden genutzten Salt zu erzeugen, was einiges an Rechenleistung und damit Zeit und Speicherplatz erfordert.

Beim Angriff auf Hashes nützt es dem Angreifer absolut nichts, wenn er die nächste Zufallszahl (mit einer, übrigens dem reinen Zufall gegenüber nur leicht erhöhten Wahrscheinlichkeit) richtig voraussagen kann. Er hat den Hash und damit den Salt ja so oder so in den Händen. Beim echten Verschlüsseln sieht das ganz anders aus.

Jörg Reinholz