Problem mit password_verify()

  • Hey,


    ich habe ein Problem. Wenn sich einer registriert bekommt das password noch ein pepper. Nun wenn ich den hash aus der DB mit der Passworteingabe vergleiche, mit password_verify() , dann kommt immer: Fehler beim Login. Und ich denke es kommt vom Pepper.


    Passwort wird mit Pepper gehast und der Hash wird in DB eingetragen und dann später wieder rausgezogen aus der db zum vergleich.


    Habe schon versucht das pepper auf der Login seite an das Passwort zu hängen aber da entsteht dann ein anderer Hash als in DB gespeichert.


    Wie kann ich den Login denn mit dem pepper machen. ?


    Stef

  • So hash ich das Password mit Pepper

    PHP
    1. //pepper erstellen
    2. $pepper = "Pfeffer";
    3. // ************** Pepper wieder hinzufügen ******************
    4. //Pasword hashen
    5. $passwordHash = password_hash($password . $pepper , PASSWORD_DEFAULT);

    So ist der login:

    PHP
    1. $pepper = "Pfeffer";
    2. if(password_verify($password . $pepper, $fetchRow->password)){
    3. header("Location: profil.php?$fetchRow->id" );
    4. $_SESSION['zugriff'] = true;

    Aber da sind dann am Ende 2 verschiedene Hashwerte. Ist ja auch klar.

  • Wenn dein PW-Hash richtig aus der DB geholt wird usw. müsste das eigentlich stimmen. Ohne Kontext sieht halt das $fetchRow->password ein wenig merkwürdig aus, aber ich denke mal, da liegt nicht dein Problem. Sonst wäre dir das vermutlich ja schon aufgefallen.


    Versuch dir mal alles mögliche auszugeben und dementsprechend genau nachzuvollziehen, was bei dir passiert und wo der Fehler liegt.


    Ansonsten müsstest du mal mehr Informationen geben. Was ist dein genaues Problem, gibt es Fehlermeldungen, ganzer Code etc.?

  • Es wird richtig aus der DB geholt, meines Erachtens. Im $fetchRow->password steht der hash drin. Also da wird der hash aus der DB gezogen.


    Ich erkenne weiterhin keinen Fehler..


    Meine beiden Scripts:


    Registration.php (Ein Teil davon):

    Login.php:

  • PHP
    1. var_dump('Gezogener Passwordhash aus DB:', $fetchRow->password);
    2. echo "<br>";
    3. var_dump('Eingegebenes Password mit dem Pepper: ', $password.$pepper);


    Folgende Ausgabe kommt vom Debug:


    string(30) "Gezogener Passwordhash aus DB:"
    string(60) "$2y$10$awVVwJUcz4hZFBt8czSopeF3dqr2Ju9U5ZsXqriTO2X/6G4WQ1ka."


    string(38) "Eingegebenes Password mit dem Pepper: "
    string(14) "Stef123Pfeffer"


    Ich komm bei dem error raus, wenn der hash mit dem password nicht zusammenpasst.


    Also bei der Ausgabe : Fehler


  • Lust ja, Zeit manchmal nicht so :D


    Register Script sehe ich nichts falsches.

    Aber im Login Script:


    Könnte hier dran liegen:

    PHP
    1. $password = filter_var($password, FILTER_SANITIZE_STRING);

    Du veränderst hier die Eingabe des Nutzers.

    Das pw musst Du genau so nehmen, wie es der Nutzer eingibt.


    Oder auch daran:

    PHP
    1. $fetchRow = $stmt->fetch(PDO::FETCH_OBJ);

    fetch zieht eine row - die nächste.

    Evtl sind schon mehrere Nutzer mit dem Namen in der db.

    Das Script würde hier ggf richtig reagieren, wenn der Nutzername einzigartig sein soll.



    Teste mal:

    Den Filter auf dem pw weg: auskommentieren oder löschen

    und

    PHP
    1. // ...
    2. $fetchRow = $stmt->fetch(PDO::FETCH_OBJ);
    3. var_dump($stmt->rowCount());


    kommt da 1 oder > 1 ?

  • $password = "Stef123";

    $pepper = "Pfeffer";

    $passwordHash = password_hash( $password . $pepper, PASSWORD_DEFAULT);

    $b = password_verify($password . $pepper, $passwordHash );

    echo ( $b==TRUE ) ? " passt " : " passt nicht ";


    Wenn man das so sieht, sagt der gesunde Programmiererverstand: "Das passt!".

    Denkste! Passt nämlich nicht!


    Es ist aber noch wirrer: Lässt man sich den erzeugten $passwordHash jedesmal ausgeben, gibt es jedes Mal einen anderen Hash! Da scheint es mit dem Vergleich mit in der DB gespeicherten Hashes schwierig zu werden. Doch es gibt Entwarnung: Das funktioniert tatsächlich!


    Des Rätsels Lösung:

    Für den PasswortHash können keine Variablen eingegeben werden, weil die $-Zeichen im Hash weitere Variablen auflösen wollen, was natürlich zu keinem vernünftigen Ergebnis führen würde. Üble Geschichte!

    Das Verifizieren würde z. B. Funktionieren, wenn der Aufruf lautet:

    password_verify($password.$pfeffer, '$2y$10$awVVwJUcz4hZFBt8czSopeF3dqr2Ju9U5ZsXqriTO2X/6G4WQ1ka');

    Vorausgesetzt, das war auch der zuletzt erzeugte Hash.


    Ist schon tricky, dieses password_hash / password_verify :)

  • Dadurch, dass du den Hash aus der DB ziehst, stimmt er ja mit dem Hash, der bei der Registrierung erstellt wurde, überein.


    Wenn man dann dasselbe Passwort angibt, sollte alles funktionieren. Dabei muss man, wie Cotton schon angemerkt hat, aufpassen, dass man das PW in keinster Weise verändert.


    Wenn die $ im Hash als Anfang einer neuen Variable gelten, würde mich sehr wundern. :D


    Wie denkst du denn, würde dann password_verify() eingesetzt werden können, wenn dies immer falsch interpretiert wird? Das Setzen eines konstanten Strings als Hash ist schließlich keine Alternative in einem dynamischen System.

  • Gegenfrage: Funktioniert es bei dir mit der Hash-Variablen? Dann muss es noch einen Trick geben, und ich lerne gern dazu ;)


    Edit:
    Ich sehe gerade, du hast das noch garnicht probiert, sonst hättest du das mit dem immer gleichen Hash nicht geschrieben.

    Aber danke fürs Gespräch.

  • Für den PasswortHash können keine Variablen eingegeben werden, weil die $-Zeichen im Hash weitere Variablen auflösen wollen

    Entweder verstehe ich Dich falsch, oder Du hast nen Denkfehler 8o


    Der hash ist ein String und steckt in der Variable. Wenn da ein $ im String steckt, dann heit das nicht, dass da versucht wird eine neue Variable zu erstellen.

    Es sei denn Du würdest zB eval($hash) nutzen, aber wozu?


    Der Hash ist immer ein anderer beim password_hash(), da der Salt bei jedem Durchgang neu erstellt wird.

    Deshalb kann man zB nicht

    $hash === $ddHash prüfen, sondern muss password_verify() nutzen.

    (der Salt steck im erzeugten Hash. password_verify kann also den gleichen Salt zum hashen des eingegebenen Passwortes nutzen)

  • Gegenfrage: Funktioniert es bei dir mit der Hash-Variablen? Dann muss es noch einen Trick geben, und ich lerne gern dazu ;)


    Edit:
    Ich sehe gerade, du hast das noch garnicht probiert, sonst hättest du das mit dem immer gleichen Hash nicht geschrieben.

    Aber danke fürs Gespräch.

    Bei mir funktioniert es.


    Der "Trick" ist, dass man mit password_hash() den einzigartigen Hash erstellt, diesen in der DB speichert und beim Login dann mit dem Passwort per password_verify() "vergleicht". Wichtig ist dabei, um das nochmal zu wiederholen, dass das Passwort in keinster Weise verändert wird und der richtige Hash aus der DB geholt wird. Viel mehr ist das nicht.


    Habe gerade auch nochmal dein Code-Schnipsel getestet. Auch das funktioniert übrigens. ;)

  • Zitat

    Sie müssen mindestens 6 Zeichen für ihr Password angeben

    Das ist ok. Aber das Formular ist wieder leer und ich muss alles noch mal eingeben =)


    Das kannst Du ändern indem Du die Daten in die Felder setzt, die schon gesendet wurden und valid sind.


    PHP
    1. <?
    2. // PHP ...
    3. ?>
    4. <!-- html ... -->
    5. <input type="text" value="<?php echo isset($_POST['name']) ? htmlspecialchars($_POST['name']) : '' ?>">



    Und das "böse" F5.

    Drück mal F5 bei erfolgreicher Registrierung.

    Dann wird das gleiche Formular (die gleichen Daten) noch einmal via Browser abgesandt.


    EDIT: bei Dir passiert btw alles richtig: die email ist schon vergeben - OK


    Das kannst Du mit header('Location: ...') verhindern.

    Wenn du per PHP also feststellst, dass alles io ist, dann leitest Du den User per header("Location: http://www.example.com/register_success"); um.

    Im Prinzip einfach eine "nichts könnende" Seite, die alleine für die Erfolgsmeldung da ist.


    Such mal im Netz nach erneute formular-übermittlung verhindern

  • Ja stimmt auch wieder. Muss ich noch machen.


    Habe das F5 garnicht mal gekannt. Aber ich überprüfe ja ob es schon ein eintrag in der db mit der email oder username gibt.


    Ja ok. Aber ich möchte doch ein douple-opt-in Verfahren erstellen. Sodass dieser erst wenn er auf den Link in der email klickt sich einloggen kann.

    Aber wie kann man es denn erreichen, dass erst nach dem klick auf den link, und der Überprüfung ob der hash als parameter mit dem hash aus der db überein stimmt, der User sich einloggen kann ?


    Dachte : 2 Tabellen erstellen, und die eine speichert die eingaben vom registrierformular. Und die andere Tabelle erhält diese daten erst, wenn der hash übereinstimmt und dann wird diese zum Login benutzt. Anders kann ich es mir nun nicht vorstellen.


    Ja werde ich mal machen.


    Danke