Wie verwendet man bcrypt zum Hashing von Passwörtern in PHP?


1255

Hin und wieder höre ich den Rat "Verwenden Sie bcrypt zum Speichern von Passwörtern in PHP, bcrypt Regeln".

Aber was ist das bcrypt? PHP bietet keine solchen Funktionen, Wikipedia plappert über ein Dienstprogramm zur Dateiverschlüsselung und Websuchen enthüllen nur einige Implementierungen von Blowfish in verschiedenen Sprachen. Jetzt ist Blowfish auch in PHP über verfügbar mcrypt, aber wie hilft das beim Speichern von Passwörtern? Blowfish ist eine Allzweck-Chiffre, die auf zwei Arten funktioniert. Wenn es verschlüsselt werden könnte, kann es entschlüsselt werden. Passwörter benötigen eine Einweg-Hashing-Funktion.

Was ist die Erklärung?


13
Diese Frage wurde bereits zuvor beantwortet , und ihr Vorschlag, eine Standardbibliothek zu verwenden, ist ausgezeichnet. Sicherheit ist eine komplizierte Angelegenheit, und wenn Sie ein Paket verwenden, das von jemandem entwickelt wurde, der weiß, was zum Teufel er tut, helfen Sie sich nur selbst.
Eykanal

59
@eykanal - diese Seite erwähnt nicht einmal bcrypt, geschweige denn erklärt, was es ist .
Vilx

8
@eykanal - Ich frage nicht nach einer Erklärung, wie es funktioniert. Ich möchte nur wissen, was es ist. Denn was auch immer ich unter dem Schlüsselwort "bcrypt" im Internet ausgraben kann, kann in keiner Weise zum Hashing von Passwörtern verwendet werden. Jedenfalls nicht direkt und nicht in PHP. OK, jetzt verstehe ich, dass es wirklich das "phpass" -Paket ist, das Blowfish verwendet, um Ihr Passwort mit einem Schlüssel zu verschlüsseln, der von Ihrem Passwort abgeleitet ist (im Wesentlichen das Passwort mit sich selbst verschlüsseln). Aber es als "bcrypt" zu bezeichnen, ist sehr irreführend, und das wollte ich in dieser Frage klarstellen.
Vilx

3
@Vilx: Ich habe in meiner Antwort weitere Informationen hinzugefügt, warum bcryptein Einweg-Hashing-Algorithmus im Vergleich zu einem Verschlüsselungsschema verwendet wird . Es gibt dieses ganze Missverständnis, das nur Blowfish ist, obwohl es tatsächlich einen völlig anderen Schlüsselplan gibt, der sicherstellt, dass einfacher Text nicht aus dem Chiffretext wiederhergestellt werden kann, ohne den Anfangszustand der Chiffre (Salz, Runden, Schlüssel) zu kennen. bcrypt
Andrew Moore

1
Siehe auch Openwalls Portable PHP Password Hashing Framework (PHPass). Es ist gegen eine Reihe von häufigen Angriffen auf Benutzerkennwörter gehärtet.
JWW

Antworten:


1065

bcryptist ein Hashing-Algorithmus, der mit Hardware skalierbar ist (über eine konfigurierbare Anzahl von Runden). Die Langsamkeit und die mehreren Runden stellen sicher, dass ein Angreifer massive Mittel und Hardware einsetzen muss, um Ihre Passwörter knacken zu können. Dazu kommen pro-Passwort Salze ( bcryptBRAUCHT Salze) , und Sie können sicher sein , dass ein Angriff ohne entweder lächerliche Menge von Geldern oder Hardware praktisch nicht durchführbar ist.

bcryptverwendet den Eksblowfish- Algorithmus zum Hashing von Passwörtern. Während die Verschlüsselungsphase von Eksblowfish und Blowfish genau gleich ist, stellt die Schlüsselzeitplanphase von Eksblowfish sicher, dass jeder nachfolgende Status sowohl von Salt als auch vom Schlüssel (Benutzerkennwort) abhängt und kein Status ohne Wissen von beiden vorberechnet werden kann. Aufgrund dieses Hauptunterschieds bcrypthandelt es sich um einen Einweg-Hashing-Algorithmus. Sie können das Nur-Text-Passwort nicht abrufen, ohne das Salz, die Runden und den Schlüssel (Passwort) bereits zu kennen . [ Quelle ]

So verwenden Sie bcrypt:

Mit PHP> = 5.5-DEV

Passwort-Hashing-Funktionen wurden jetzt direkt in PHP> = 5.5 integriert . Sie können jetzt password_hash()einen bcryptHash eines beliebigen Passworts erstellen :

<?php
// Usage 1:
echo password_hash('rasmuslerdorf', PASSWORD_DEFAULT)."\n";
// $2y$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// For example:
// $2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a

// Usage 2:
$options = [
  'cost' => 11
];
echo password_hash('rasmuslerdorf', PASSWORD_BCRYPT, $options)."\n";
// $2y$11$6DP.V0nO7YI3iSki4qog6OQI5eiO6Jnjsqg7vdnb.JgGIsxniOn4C

Um ein vom Benutzer angegebenes Kennwort anhand eines vorhandenen Hashs zu überprüfen, können Sie Folgendes verwenden password_verify():

<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

if (password_verify('rasmuslerdorf', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}

Mit PHP> = 5.3.7, <5.5-DEV (auch RedHat PHP> = 5.3.3)

Es gibt eine Kompatibilitätsbibliothek auf GitHub erstellt auf den Quellcode der oben genannten Funktionen basieren ursprünglich in C geschrieben, die die gleiche Funktionalität zur Verfügung stellt. Sobald die Kompatibilitätsbibliothek installiert ist, ist die Verwendung dieselbe wie oben (abzüglich der Kurzarray-Notation, wenn Sie sich noch im Zweig 5.3.x befinden).

Verwenden von PHP <5.3.7 (DEPRECATED)

Sie können die crypt()Funktion verwenden, um bcrypt-Hashes von Eingabezeichenfolgen zu generieren. Diese Klasse kann automatisch Salze erzeugen und vorhandene Hashes anhand einer Eingabe überprüfen. Wenn Sie eine Version von PHP verwenden, die höher oder gleich 5.3.7 ist, wird dringend empfohlen, die integrierte Funktion oder die kompatible Bibliothek zu verwenden . Diese Alternative wird nur für historische Zwecke bereitgestellt.

class Bcrypt{
  private $rounds;

  public function __construct($rounds = 12) {
    if (CRYPT_BLOWFISH != 1) {
      throw new Exception("bcrypt not supported in this installation. See http://php.net/crypt");
    }

    $this->rounds = $rounds;
  }

  public function hash($input){
    $hash = crypt($input, $this->getSalt());

    if (strlen($hash) > 13)
      return $hash;

    return false;
  }

  public function verify($input, $existingHash){
    $hash = crypt($input, $existingHash);

    return $hash === $existingHash;
  }

  private function getSalt(){
    $salt = sprintf('$2a$%02d$', $this->rounds);

    $bytes = $this->getRandomBytes(16);

    $salt .= $this->encodeBytes($bytes);

    return $salt;
  }

  private $randomState;
  private function getRandomBytes($count){
    $bytes = '';

    if (function_exists('openssl_random_pseudo_bytes') &&
        (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { // OpenSSL is slow on Windows
      $bytes = openssl_random_pseudo_bytes($count);
    }

    if ($bytes === '' && is_readable('/dev/urandom') &&
       ($hRand = @fopen('/dev/urandom', 'rb')) !== FALSE) {
      $bytes = fread($hRand, $count);
      fclose($hRand);
    }

    if (strlen($bytes) < $count) {
      $bytes = '';

      if ($this->randomState === null) {
        $this->randomState = microtime();
        if (function_exists('getmypid')) {
          $this->randomState .= getmypid();
        }
      }

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

        if (PHP_VERSION >= '5') {
          $bytes .= md5($this->randomState, true);
        } else {
          $bytes .= pack('H*', md5($this->randomState));
        }
      }

      $bytes = substr($bytes, 0, $count);
    }

    return $bytes;
  }

  private function encodeBytes($input){
    // The following is code from the PHP Password Hashing Framework
    $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

    $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 (true);

    return $output;
  }
}

Sie können diesen Code folgendermaßen verwenden:

$bcrypt = new Bcrypt(15);

$hash = $bcrypt->hash('password');
$isGood = $bcrypt->verify('password', $hash);

Alternativ können Sie auch das Portable PHP Hashing Framework verwenden .


7
@ The Wicked Flea: Tut mir leid, dass ich Sie enttäuscht habe, aber es mt_rand()wird auch die aktuelle Zeit und die aktuelle Prozess-ID verwendet. Bitte sehen Sie GENERATE_SEED()in/ext/standard/php_rand.h .
Andrew Moore

53
@ Mike: Mach weiter, es ist genau aus diesem Grund da!
Andrew Moore

14
Für alle, die denken, dass sie den Anfang der $ salt-Zeichenfolge in der Funktion getSalt ändern müssen, ist dies nicht erforderlich. Das $ 2a $ __ ist Teil des CRYPT_BLOWFISH-Salzes. Aus den Dokumenten: "Blowfish Hashing mit einem Salz wie folgt:" $ 2a $ ", ein zweistelliger Kostenparameter," $ "und 22 Ziffern aus dem Alphabet".
Jwinn

18
@MichaelLang: Gut, dass crypt()dann Peer-Review und Verifikation durchgeführt werden. Der obige Code ruft PHPs auf crypt(), die die POSIX- crypt()Funktion aufrufen . Der obige Code bewirkt lediglich, dass vor dem Aufruf ein zufälliges Salt generiert wird (das nicht kryptografisch sicher sein muss, das Salt wird nicht als Geheimnis betrachtet) crypt(). Vielleicht sollten Sie selbst ein wenig recherchieren, bevor Sie Wolf anrufen.
Andrew Moore

31
Bitte beachten Sie, dass diese Antwort zwar gut ist, aber allmählich ihr Alter zeigt. Dieser Code (wie jede PHP-Implementierung, auf die Sie sich verlassen crypt()) unterliegt einer Sicherheitslücke vor 5.3.7 und ist nach 5.3.7 (geringfügig) ineffizient. Details zum relevanten Problem finden Sie hier . Bitte beachten Sie auch, dass die neue Passwort-Hashing-API ( abwärtskompatible Bibliothek ) jetzt die bevorzugte Methode zum Implementieren von bcrypt-Passwort-Hashing in Ihrer Anwendung ist.
Dave Random

295

Sie möchten also bcrypt verwenden? Genial! Wie in anderen Bereichen der Kryptographie sollten Sie dies jedoch nicht selbst tun. Wenn Sie sich Gedanken über die Verwaltung von Schlüsseln, das Speichern von Salzen oder das Generieren von Zufallszahlen machen müssen, machen Sie es falsch.

Der Grund ist einfach: Es ist so einfach, bcrypt zu vermasseln . Wenn Sie sich fast jeden Code auf dieser Seite ansehen, werden Sie feststellen, dass mindestens eines dieser häufigen Probleme verletzt wird.

Seien Sie ehrlich, Kryptographie ist schwer.

Überlassen Sie es den Experten. Überlassen Sie es den Personen, deren Aufgabe es ist, diese Bibliotheken zu pflegen. Wenn Sie eine Entscheidung treffen müssen, machen Sie es falsch.

Verwenden Sie stattdessen einfach eine Bibliothek. Je nach Ihren Anforderungen gibt es mehrere.

Bibliotheken

Hier finden Sie eine Aufschlüsselung einiger der gängigsten APIs.

PHP 5.5 API - (Verfügbar für 5.3.7+)

Ab PHP 5.5 wird eine neue API zum Hashing von Passwörtern eingeführt. Es gibt auch eine Shim-Kompatibilitätsbibliothek, die (von mir) für 5.3.7+ verwaltet wird. Dies hat den Vorteil, dass es sich um eine von Experten überprüfte und einfach zu verwendende Implementierung handelt.

function register($username, $password) {
    $hash = password_hash($password, PASSWORD_BCRYPT);
    save($username, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    if (password_verify($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Wirklich, es soll extrem einfach sein.

Ressourcen:

Zend \ Crypt \ Password \ Bcrypt (5.3.2+)

Dies ist eine weitere API, die der von PHP 5.5 ähnelt und einen ähnlichen Zweck erfüllt.

function register($username, $password) {
    $bcrypt = new Zend\Crypt\Password\Bcrypt();
    $hash = $bcrypt->create($password);
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $bcrypt = new Zend\Crypt\Password\Bcrypt();
    if ($bcrypt->verify($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Ressourcen:

PasswordLib

Dies ist ein etwas anderer Ansatz für das Passwort-Hashing. PasswordLib unterstützt nicht nur bcrypt, sondern auch eine Vielzahl von Hashing-Algorithmen. Dies ist vor allem in Kontexten nützlich, in denen Sie die Kompatibilität mit älteren und unterschiedlichen Systemen unterstützen müssen, die möglicherweise außerhalb Ihrer Kontrolle liegen. Es unterstützt eine große Anzahl von Hashing-Algorithmen. Und wird 5.3.2+ unterstützt

function register($username, $password) {
    $lib = new PasswordLib\PasswordLib();
    $hash = $lib->createPasswordHash($password, '$2y$', array('cost' => 12));
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $lib = new PasswordLib\PasswordLib();
    if ($lib->verifyPasswordHash($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Verweise:

  • Quellcode / Dokumentation: GitHub

PHPASS

Dies ist eine Ebene, die bcrypt unterstützt, aber auch einen ziemlich starken Algorithmus unterstützt, der nützlich ist, wenn Sie keinen Zugriff auf PHP> = 5.3.2 haben ... Sie unterstützt tatsächlich PHP 3.0+ (obwohl nicht mit bcrypt).

function register($username, $password) {
    $phpass = new PasswordHash(12, false);
    $hash = $phpass->HashPassword($password);
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $phpass = new PasswordHash(12, false);
    if ($phpass->CheckPassword($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Ressourcen

Hinweis: Verwenden Sie keine PHPASS-Alternativen, die nicht auf Openwall gehostet werden, sondern unterschiedliche Projekte !!!

Über BCrypt

Wenn Sie bemerken, gibt jede dieser Bibliotheken eine einzelne Zeichenfolge zurück. Das liegt daran, wie BCrypt intern funktioniert. Und dazu gibt es eine Menge Antworten. Hier ist eine Auswahl, die ich geschrieben habe und die ich hier nicht kopieren / einfügen werde, sondern auf Folgendes verweise:

Einpacken

Es gibt viele verschiedene Möglichkeiten. Welche Sie wählen, liegt bei Ihnen. Ich würde jedoch dringend empfehlen, dass Sie eine der oben genannten Bibliotheken verwenden, um dies für Sie zu erledigen .

Wenn Sie crypt()direkt verwenden, machen Sie wahrscheinlich etwas falsch. Wenn Ihr Code direkt hash()(oder md5()oder sha1()) verwendet, machen Sie mit ziemlicher Sicherheit etwas falsch.

Verwenden Sie einfach eine Bibliothek ...


7
Das Salz muss zufällig erzeugt werden, muss jedoch nicht aus einer sicheren zufälligen Quelle stammen. Das Salz ist kein Geheimnis . Das nächste Salz erraten zu können, hat keine wirklichen Auswirkungen auf die Sicherheit. Solange sie aus einem ausreichend großen Datenpool stammen, um für jedes verschlüsselte Passwort unterschiedliche Salze zu erzeugen, ist alles in Ordnung. Denken Sie daran, dass das Salz dazu dient, die Verwendung von Regenbogentischen zu verhindern, wenn Ihre Hashes in schlechte Hände geraten. Sie sind nicht geheim.
Andrew Moore

7
@ AndrewMoore absolut richtig! Das Salz muss jedoch eine ausreichende Entropie aufweisen, um statistisch eindeutig zu sein. Nicht nur in Ihrer Anwendung, sondern in allen Anwendungen. Hat mt_rand()also eine ausreichend hohe Periode, aber der Startwert beträgt nur 32 Bit. mt_rand()Wenn Sie also effektiv verwenden, sind Sie auf nur 32 Bit Entropie beschränkt. Dank des Geburtstagsproblems haben Sie eine 50% ige Kollisionswahrscheinlichkeit bei nur 7.000 erzeugten Salzen (weltweit). Da bcrypt128 Bit Salz akzeptiert werden, ist es besser, eine Quelle zu verwenden, die alle 128 Bit liefern kann ;-). (bei 128 Bit, 50% Kollisionswahrscheinlichkeit bei 2e19 Hashes) ...
ircmaxell

1
@ircmaxell: Hense den "ausreichend großen Datenpool". Ihre Quelle muss jedoch keine SEHR HOHE Entropiequelle sein, die gerade hoch genug für die 128 Bit ist. Wenn Sie jedoch alle verfügbaren Quellen ausgeschöpft haben (kein OpenSSL usw.) und Ihr einziger Fallback mt_rand () ist, ist es immer noch besser als die Alternative (rand ()).
Andrew Moore

4
@ AndrewMoore: absolut. Das nicht streiten. Nur das mt_randund uniqid(und damit lcg_valueund rand) sind nicht die erste Wahl ...
ircmaxell

1
ircmaxell, vielen Dank für die Bibliothek password_compat für 5.3.xx, die wir bisher noch nicht benötigt haben, aber jetzt auf einem PHP-Server 5.3.xx, und vielen Dank für Ihren klaren Rat, diese Logik nicht zu versuchen man selbst.
Lizardx

47

In Genug mit den Regenbogentabellen finden Sie viele Informationen : Was Sie über sichere Kennwortschemata oder das tragbare PHP-Kennwort-Hashing-Framework wissen müssen .

Das Ziel ist es, das Passwort mit etwas Langsamem zu hashen, damit jemand, der Ihre Passwortdatenbank erhält, beim Versuch, es brutal zu erzwingen, stirbt (eine Verzögerung von 10 ms, um ein Passwort zu überprüfen, ist nichts für Sie, viel für jemanden, der versucht, es brutal zu erzwingen). Bcrypt ist langsam und kann mit einem Parameter verwendet werden, um zu wählen, wie langsam es ist.


7
Erzwingen Sie, was Sie wollen, Benutzer können es vermasseln und dasselbe Passwort für mehrere Dinge verwenden. Sie müssen es also so weit wie möglich schützen oder etwas implementieren, mit dem Sie kein Passwort (SSO, openID usw.) speichern müssen.
Arkh

41
Nein. Passwort-Hashing wird zum Schutz vor einem Angriff verwendet: Jemand hat Ihre Datenbank gestohlen und möchte Klartext-Login + Passwörter erhalten.
Arkh

4
@Josh K. Ich empfehle Ihnen, zu versuchen, einige einfache Passwörter zu knacken, nachdem Sie sie über phpass eingestellt haben, sodass die Berechnung auf Ihrem Webserver zwischen 1 ms und 10 ms dauert.
Arkh

3
Einverstanden. Aber die Art von Benutzer, die qwerty als Passwort verwendet, ist auch die Art von Benutzer, die komplizierte Benutzer irgendwo notiert, wo er (und Angreifer) sie leicht lesen können. Mit bcrypt erreichen Sie, dass es schwieriger ist, Benutzer mit einem Kennwort wie ^ | $$ & ZL6- £ zu erreichen, wenn Ihre Datenbank gegen Ihren Willen an die Öffentlichkeit geht, als wenn Sie sha512 in einem Durchgang verwendet haben.
Arkh

4
@coreyward erwähnenswert, dass dies schädlicher ist, als überhaupt nicht zu blockieren; Dies wird leicht als "Denial-of-Service" -Vektor angesehen. Fangen Sie einfach an, schlechte Anmeldungen auf bekannte Konten zu spammen, und Sie können viele Benutzer sehr, sehr leicht stören. Es ist besser, den Angreifer zu tarpieren (zu verzögern), als den Zugriff direkt zu verweigern, insbesondere wenn es sich um einen zahlenden Kunden handelt.
Damianb

36

Sie können mit bcrypt einen Einweg-Hash erstellen, indem Sie die PHP- crypt()Funktion verwenden und ein geeignetes Blowfish-Salz eingeben. Das Wichtigste der gesamten Gleichung ist, dass A) der Algorithmus nicht kompromittiert wurde und B) Sie jedes Passwort richtig salzen . Verwenden Sie kein anwendungsweites Salz. Dadurch wird Ihre gesamte Anwendung für Angriffe von einem einzigen Satz von Rainbow-Tabellen aus geöffnet.

PHP - Kryptofunktion


4
Dies ist der richtige Ansatz - verwenden Sie die PHP- crypt()Funktion, die verschiedene Passwort-Hashing-Funktionen unterstützt. Stellen Sie sicher, dass Sie nicht verwenden CRYPT_STD_DESoder CRYPT_EXT_DES- - einer der anderen unterstützten Typen ist in Ordnung (und enthält bcrypt unter dem Namen CRYPT_BLOWFISH).
Café

4
SHA hat in der Tat auch einen Kostenparameter über die Option "Runden". Wenn ich das benutze, sehe ich auch keinen Grund, bcrypt zu bevorzugen.
Pieter Ennes

3
Tatsächlich ist ein einzelner SHA-1 (oder MD5) eines Passworts mit oder ohne Salz immer noch leicht Brute-Force-fähig (Salz hilft gegen Regenbogentabellen, nicht gegen Brute-Forcing). Verwenden Sie bcrypt.
Paŭlo Ebermann

Ich finde es beunruhigend, dass jeder "bcrypt" zu sagen scheint, wenn er die Krypta von php () meint.
Sliq

3
@Panique Warum? Der Algorithmus heißt bcrypt . cryptmacht mehrere Passwort-Hashes verfügbar, wobei bcrypt der CRYPT_BLOWFISHKonstante entspricht. Bcrypt ist derzeit der stärkste Algorithmus, der von unterstützt wird, cryptund einige andere, die es unterstützt, sind ziemlich schwach.
CodesInChaos

34

Edit: 2013.01.15 - Wenn Ihr Server dies unterstützt, verwenden Sie stattdessen die Lösung von martinstoeckli .


Jeder möchte das komplizierter machen als es ist. Die crypt () -Funktion erledigt den größten Teil der Arbeit.

function blowfishCrypt($password,$cost)
{
    $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $salt=sprintf('$2y$%02d$',$cost);
//For PHP < PHP 5.3.7 use this instead
//    $salt=sprintf('$2a$%02d$',$cost);
    //Create a 22 character salt -edit- 2013.01.15 - replaced rand with mt_rand
    mt_srand();
    for($i=0;$i<22;$i++) $salt.=$chars[mt_rand(0,63)];
    return crypt($password,$salt);
}

Beispiel:

$hash=blowfishCrypt('password',10); //This creates the hash
$hash=blowfishCrypt('password',12); //This creates a more secure hash
if(crypt('password',$hash)==$hash){ /*ok*/ } //This checks a password

Ich weiß, dass es offensichtlich sein sollte, aber bitte verwenden Sie nicht 'Passwort' als Ihr Passwort.


3
Die Erstellung des Salzes könnte verbessert werden (verwenden Sie die zufällige Quelle des Betriebssystems), sonst sieht es für mich gut aus. Für neuere PHP-Versionen ist es besser, 2yanstelle von zu verwenden 2a.
Martinstoeckli

Verwendung mcrypt_create_iv($size, MCRYPT_DEV_URANDOM)als Quelle für das Salz.
CodesInChaos

Ich werde mir mcrypt_create_iv () genauer ansehen, wenn ich einen Moment Zeit habe. Wenn nichts anderes, sollte dies die Leistung leicht verbessern.
Jon Hulka

2
Fügen Sie die Base64-Codierung hinzu und übersetzen Sie sie in das benutzerdefinierte Alphabet bcrypt. mcrypt_create_iv(17, MCRYPT_DEV_URANDOM), str_replace('+', '.', base64_encode($rawSalt)),$salt = substr($salt, 0, 22);
CodesInChaos

1
@ JonHulka - Schauen Sie sich das Kompatibilitätspaket von PHP an [Zeile 127], dies ist eine einfache Implementierung.
Martinstoeckli

29

Version 5.5 von PHP bietet integrierte Unterstützung für BCrypt, die Funktionen password_hash()und password_verify(). Eigentlich sind dies nur Wrapper um die Funktion crypt()und sollen die korrekte Verwendung erleichtern. Es sorgt für die Erzeugung eines sicheren zufälligen Salzes und liefert gute Standardwerte.

Der einfachste Weg, diese Funktionen zu nutzen, ist:

$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
$isPasswordCorrect = password_verify($password, $existingHashFromDb);

Dieser Code hasht das Passwort mit BCrypt (Algorithmus 2y), generiert ein zufälliges Salt aus der zufälligen Quelle des Betriebssystems und verwendet den Standardkostenparameter (im Moment ist dies 10). In der zweiten Zeile wird geprüft, ob das vom Benutzer eingegebene Passwort mit einem bereits gespeicherten Hashwert übereinstimmt.

Wenn Sie den Kostenparameter ändern möchten, können Sie dies folgendermaßen tun: Erhöhen Sie den Kostenparameter um 1 und verdoppeln Sie die für die Berechnung des Hashwerts erforderliche Zeit:

$hash = password_hash($password, PASSWORD_BCRYPT, array("cost" => 11));

Im Gegensatz zum "cost"Parameter ist es am besten, den "salt"Parameter wegzulassen , da die Funktion bereits ihr Bestes tut, um ein kryptografisch sicheres Salz zu erstellen.

Für PHP Version 5.3.7 und höher gibt es ein Kompatibilitätspaket desselben Autors, der die password_hash()Funktion erstellt hat. Für PHP - Versionen vor 5.3.7 gibt es keine Unterstützung für crypt()mit 2y, die Unicode sicher BCrypt Algorithmus. Man könnte es stattdessen durch ersetzen 2a, was die beste Alternative für frühere PHP-Versionen ist.


3
Nachdem ich dies gelesen hatte, war mein erster Gedanke: "Wie lagert man das erzeugte Salz?" Nach dem Durchsuchen der Dokumente generiert die Funktion password_hash () eine Zeichenfolge, in der die Verschlüsselungsmethode, das Salt und der generierte Hash gespeichert sind. Es speichert also einfach alles, was es braucht, in einer Zeichenfolge, damit die Funktion password_verify () funktioniert. Ich wollte dies nur erwähnen, da es anderen helfen kann, wenn sie dies sehen.
jzimmerman2011

@ jzimmerman2011 - Genau, in einer anderen Antwort habe ich versucht, dieses Speicherformat anhand eines Beispiels zu erklären.
Martinstoeckli

7

Aktuelles Denken: Hashes sollten am langsamsten verfügbar sein, nicht am schnellsten. Dies unterdrückt Regenbogentischangriffe .

Ebenfalls verwandt, aber vorsorglich: Ein Angreifer sollte niemals uneingeschränkten Zugriff auf Ihren Anmeldebildschirm haben. Um dies zu verhindern: Richten Sie eine IP-Adressverfolgungstabelle ein, die jeden Treffer zusammen mit dem URI aufzeichnet. Wenn in einem Zeitraum von fünf Minuten mehr als 5 Anmeldeversuche von derselben IP-Adresse ausgehen, blockieren Sie dies mit einer Erklärung. Ein sekundärer Ansatz besteht darin, ein zweistufiges Passwortschema zu haben, wie es Banken tun. Das Sperren von Fehlern im zweiten Durchgang erhöht die Sicherheit.

Zusammenfassung: Verlangsamen Sie den Angreifer mithilfe zeitaufwändiger Hash-Funktionen. Blockieren Sie außerdem zu viele Zugriffe auf Ihr Login und fügen Sie eine zweite Kennwortebene hinzu.


Ich denke, sie gehen davon aus, dass der Angreifer es bereits geschafft hat, meine Datenbank auf andere Weise zu stehlen, und versucht nun, die Passwörter herauszuholen, um sie auf Paypal oder so zu testen.
Vilx

4
Mitte 2012 und diese Antwort ist immer noch unklar. Wie verhindert ein langsamer Hashing-Algorithmus Angriffe auf Regenbogentabellen? Ich dachte, ein zufälliges Bytebereichssalz hätte es getan? Ich habe immer gedacht, dass die Geschwindigkeit des Hashing-Algorithmus bestimmt, wie viele Iterationen sie gegen den Hash senden können, den sie in einer bestimmten Zeit von Ihnen erhalten haben. Außerdem blockieren Sie NIEMALS einen Benutzer bei fehlgeschlagenen Anmeldeversuchen. Vertrauen Sie mir, dass Ihre Benutzer die Nase voll haben. Oft muss ich mich auf einigen Websites fast fünfmal anmelden, manchmal mehr, bevor ich mich an mein Passwort erinnere. Auch die zweite Durchgangsstufe funktioniert nicht, eine zweistufige Authentifizierung mit Handycode könnte jedoch möglich sein.
Sammaye

1
@Sammaye Ich würde dem bis zu einem gewissen Punkt zustimmen. Ich habe einen Block für 5 fehlgeschlagene Anmeldeversuche eingerichtet, bevor ich ihn schnell auf 7 und dann auf 10 erhöht habe. Jetzt sollte er auf 20 sitzen. Kein normaler Benutzer sollte 20 fehlgeschlagene Anmeldeversuche haben, aber er ist niedrig genug, um Brute-Force-Angriffe einfach zu stoppen
Bruce Aldridge

@BruceAldridge Ich persönlich würde denken, es wäre besser, Ihr Skript für eine zufällige Zeit anzuhalten, nachdem beispielsweise 7 fehlgeschlagene Anmeldungen und ein Captcha angezeigt wurden, anstatt es zu blockieren. Blockieren ist ein sehr aggressiver Schritt.
Sammaye

1
@Sammaye Ich stimme zu, dass permanente Blöcke schlecht sind. Ich beziehe mich auf einen temporären Block, der mit der Anzahl der fehlgeschlagenen Versuche zunimmt.
Bruce Aldridge

7

Hier ist eine aktualisierte Antwort auf diese alte Frage!

Der richtige Weg, um Passwörter in PHP seit 5.5 zu hashen, ist mit password_hash(), und der richtige Weg, sie zu überprüfen, ist mit password_verify(), und dies gilt immer noch in PHP 8.0. Diese Funktionen verwenden standardmäßig bcrypt-Hashes, es wurden jedoch auch andere stärkere Algorithmen hinzugefügt. Sie können den Arbeitsfaktor (effektiv, wie "stark" die Verschlüsselung ist) über die password_hashParameter ändern .

Obwohl bcrypt immer noch stark genug ist, gilt es nicht mehr als Stand der Technik . Mit den Varianten Argon2i, Argon2d und Argon2id ist ein besserer Satz von Passwort-Hash-Algorithmen namens Argon2 eingetroffen . Der Unterschied zwischen ihnen (wie hier beschrieben ):

Argon2 hat eine primäre Variante: Argon2id und zwei zusätzliche Varianten: Argon2d und Argon2i. Argon2d verwendet einen datenabhängigen Speicherzugriff, wodurch es für Kryptowährungen und Proof-of-Work-Anwendungen ohne Bedrohung durch Side-Channel-Timing-Angriffe geeignet ist. Argon2i verwendet einen datenunabhängigen Speicherzugriff, der für das Kennwort-Hashing und die kennwortbasierte Schlüsselableitung bevorzugt wird. Argon2id arbeitet als Argon2i für die erste Hälfte der ersten Iteration über den Speicher und als Argon2d für den Rest und bietet somit sowohl Seitenkanal-Angriffsschutz als auch Brute-Force-Kosteneinsparungen aufgrund von Zeit-Speicher-Kompromissen.

Argon2i-Unterstützung wurde in PHP 7.2 hinzugefügt und Sie fordern dies folgendermaßen an:

$hash = password_hash('mypassword', PASSWORD_ARGON2I);

und Argon2id-Unterstützung wurde in PHP 7.3 hinzugefügt:

$hash = password_hash('mypassword', PASSWORD_ARGON2ID);

Für die Überprüfung von Kennwörtern sind keine Änderungen erforderlich, da die resultierende Hash-Zeichenfolge Informationen darüber enthält, welche Algorithmen, Salt- und Arbeitsfaktoren bei der Erstellung verwendet wurden.

Ganz separat (und etwas redundant) bietet libsodium (hinzugefügt in PHP 7.2) auch Argon2-Hashing über die Funktionen sodium_crypto_pwhash_str ()und sodium_crypto_pwhash_str_verify(), die ähnlich wie die integrierten PHP-Funktionen funktionieren. Ein möglicher Grund für die Verwendung dieser ist, dass PHP manchmal ohne libargon2 kompiliert werden kann, wodurch die Argon2-Algorithmen für die Funktion password_hash nicht verfügbar sind. PHP 7.2 und höher sollte immer libsodium aktiviert haben, aber möglicherweise nicht - aber es gibt zumindest zwei Möglichkeiten, wie Sie diesen Algorithmus verwenden können. So können Sie einen Argon2id-Hash mit libsodium erstellen (auch in PHP 7.2, das ansonsten keine Argon2id-Unterstützung bietet)):

$hash = sodium_crypto_pwhash_str(
    'mypassword',
    SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
    SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);

Beachten Sie, dass Sie ein Salz nicht manuell angeben können. Dies ist Teil des libsodium-Ethos. Erlauben Sie Benutzern nicht, Parameter auf Werte zu setzen, die die Sicherheit gefährden könnten. Beispielsweise hindert Sie nichts daran, eine leere Salt-Zeichenfolge an die PHP- password_hashFunktion zu übergeben. libsodium lässt dich nichts so Dummes tun!



1

Wie wir alle wissen, ist das Speichern des Passworts im Klartext in der Datenbank nicht sicher. Das bcrypt ist eine Hashing-Passwort-Technik. Es wird verwendet, um die Passwortsicherheit aufzubauen. Eine der erstaunlichen Funktionen von bcrypt ist, dass es uns vor Hackern schützt. Es wird verwendet, um das Passwort vor Hacking-Angriffen zu schützen, da das Passwort in verschlüsselter Form gespeichert ist.

Mit der Funktion password_hash () wird ein neuer Passwort-Hash erstellt. Es verwendet einen starken und robusten Hashing-Algorithmus. Die Funktion password_hash () ist sehr gut mit der Funktion crypt () kompatibel. Daher können von crypt () erstellte Passwort-Hashes mit password_hash () verwendet werden und umgekehrt. Die Funktionen password_verify () und password_hash () sind nur die Wrapper um die Funktion crypt () und erleichtern die genaue Verwendung erheblich.

SYNTAX

string password_hash($password , $algo , $options)

Die folgenden Algorithmen werden derzeit von der Funktion password_hash () unterstützt:

PASSWORD_DEFAULT PASSWORD_BCRYPT PASSWORD_ARGON2I PASSWORD_ARGON2ID

Parameter: Diese Funktion akzeptiert drei Parameter, wie oben erwähnt und unten beschrieben:

Passwort : Hier wird das Passwort des Benutzers gespeichert. algo : Dies ist die Konstante des Passwortalgorithmus, die kontinuierlich verwendet wird, während der Algorithmus angegeben wird, der verwendet werden soll, wenn das Hashing des Passworts stattfindet. Optionen : Es ist ein assoziatives Array, das die Optionen enthält. Wenn dies entfernt wird und nicht enthalten ist, wird ein zufälliges Salz verwendet, und die Verwendung von Standardkosten erfolgt. Rückgabewert : Es gibt das Hash - Passwort auf Erfolg oder False bei Fehlern.

Beispiel :

Input : echo password_hash("GFG@123", PASSWORD_DEFAULT); Output : $2y$10$.vGA19Jh8YrwSJFDodbfoHJIOFH)DfhuofGv3Fykk1a

Die folgenden Programme veranschaulichen die Funktion password_hash () in PHP:

<?php echo password_hash("GFG@123", PASSWORD_DEFAULT); ?>

AUSGABE

$2y$10$Z166W1fBdsLcXPVQVfPw/uRq1ueWMA6sLt9bmdUFz9AmOGLdM393G

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.