Sicheres Passwort Hashing

Nachdem in letzter Zeit immer und immer wieder Webseiten vor allem dadurch auffallen, dass sie Passwörter im Klartext speichern, hier zwei Funktionen aus der User-Klasse eines meiner letzten Projekte…

Übrigens: Wenn Ihr bei Klick auf ‚Passwort vergessen‘ von einer Webseite das Passwort im Klartext per Email zugesendet bekommt, macht man dort genau das falsch. Schaut dann mal in deren AGB oder Datenschutzbestimmung, ob sie dort behaupten die Passwörter zu hashen. Oft sind die nur Copy&Paste. Und bittet sie das Verfahren umzustellen. Oder schreibt Heise.

Der Code

Für die meisten Probleme gibt es Standards, so auch für das Passwort-Hashing. Es gibt also keinen Grund sich selbst mit Funktionen wie (der nicht mehr zu benutzenden) md5() und String-Operationen seine eigene crypt-Funktion zu schreiben.

Hier nun also eine Lösung mit php, weil die grade hier rumlag.

    /* Code Licence: Public Domain
     *
     * $this->pwhash wird in der DB gesichert bzw. daraus gelesen.
     * die Konfiguration in $_CONFIG sieht z.B. so aus:
     *
     * // Hier ist ein zufälliger Wert pro Webseite einzutragen:
     * $_CONFIG["pw_sitekey"]="897a-d21lk.a8q";
     * // Und hier der Hash-Also. sha1, sha512, NICHT md5!
     * $_CONFIG["pw_hashalgo"]="sha512";
     *
     */

    public function setPassword($password){
        global $_CONFIG;
        $site_key=$_CONFIG["pw_sitekey"];
        $algo=$_CONFIG["pw_hashalgo"];
        $nonce=uniqid();
        $hash = hash_hmac($algo, $password . $nonce, $site_key);
        $this->pwhash = $nonce.':'.$hash;
    }

    public function verifyPassword($password){
        global $_CONFIG;
        $site_key=$_CONFIG["pw_sitekey"];
        $algo=$_CONFIG["pw_hashalgo"];
        $nonce= strstr($this->pwhash, ':',TRUE);
        $hash = strstr($this->pwhash, ':',FALSE);
        $chash= ":".hash_hmac($algo, $password . $nonce, $site_key);
        return ($hash == $chash);
    }

Demo

Was kommt da nun also heraus? Dazu eine kleine Demo

        //Passwort
        $password="very secret";

        //Konfiguration:
        $site_key="12345";
        $algo="sha512";

        $nonce=uniqid();
        $hash = hash_hmac($algo, $password . $nonce, $site_key);
        $pwhash = $nonce.':'.$hash;

        //ausgabe zur Kontrolle
        echo "pwhash= $pwhash\n";

        //zu pruefendes Passwort:
        $check_password="very secret";

        $vnonce= strstr($pwhash, ':',TRUE);
        $vhash = strstr($pwhash, ':',FALSE);
        $chash= ":".hash_hmac($algo, $check_password . $vnonce, $site_key);

        if ($vhash == $chash){
            echo "passwords match!\n";
        }

Die Ausgabe bei übereinstimmenden Passwörtern:

pwhash= 50000b502b234:b038dfb61ba609604e7be78f3aa2cd84fb03970031f5e8d6f2eacc10542741dda5b7f41a716f3f78c305f6898d9b82838ce6b9df15dc94d00c37af393cc51de1
passwords match!

Passwort vergessen!

Wie setzt man das nun organisatorisch mit dem ‚Passwort vergessen‘ Link zusamen? Ganz einfach. Wenn die Nutzer ihr Passwort vergessen können sie sich an ihre Emailadresse einen Link zusenden, mit dessen Hilfe sie auf der Webseite ihr Passwort selbst zurücksetzen können. Der Link enthält dabei ein zufällig erzeugtes Token und verliert nach Benutzung (erfolgreiches Ändern des Passworts) seine Gültigkeit.

Nachtrag: Und natürlich sollte der Link wenn er nicht benutzt wurde nach X Tagen ebenfalls invalidiert werden.

Dieser Beitrag wurde unter de, IT, Kaputt, php, security, Technik veröffentlicht. Setze ein Lesezeichen auf den Permalink.