double opt in - Vefahren bei Registriervorgang

  • Hey zusammen,

    ich möchte das double opt in verfahren anwenden um wirklich sicher zu gehen das die email richtig ist die eingegeben worden ist und kein fake.
    Nun wie kann man sowas machen ?
    Ich habe schon danach gesucht aber nicht viel gefunden.

    Gruß,
    Stef

    • Offizieller Beitrag

    Hallo,

    Beim Registrieren einen Hashwert/-ID bilden und zu der E-Mail Adresse speichern.
    Dann einen Link mit diesem Hashwert als Parameter und der E-Mail Adresse per Mail schicken.

    User klickt auf den Link ( oder kopiert den in die Adresszeile des Browsers ) und Du musst dann nur noch in der Datenbank schauen, ob E-Mail und Hashwert zusammenpassen.

    Gruß Arne

  • Hey,

    ich habe nun mal die Datenbankverbindung aufgebaut und die ganzen Daten speichern lassen.

    Mein aktueller Stand:

    Da habe ich nun eine id die sich immer ändert. Und diese wird dann der email hinzugefügt als url parameter. Nun muss ich ja noch die hashid die bei der Regestration abgespeichert wird überprüfen ob email und hasid zusammenpassen.
    Aber wie mache ich es jetzt ? Ich habe gedacht eine weiteres statement die hashid und email selektieren, um dann zu überprüfen ob die zusammenpassen.

    Komisch die hash id bleibt immer gleich... Soll doch nicht ??? Mit uniqid(); erstellt man doch unterschiedliche ids ?? Mit hash(); weiß ich nicht wie ichs machen soll. Was soll man denn da hashen lassen ? Weil es gibt keine hash function die ohne werte einen hash-wert erstellt glaube ich. Habe bisher keine gefunden.

    Ich möchte, dass wenn jemand sich registriert noch nicht sich einloggen kann. Erst wenn dieser die Bestätigungsmail geklickt hat.
    Wenn ich nun aber die hashid schon in db speichere ist es doch unnötig des zu vergleichen. Weil der User eh sich einloggen kann.

    Aber wie soll ich denn rausbekommen ob der User den Link geklickt hat ?

    Gruß,
    Stef

    6 Mal editiert, zuletzt von Stef (6. September 2016 um 20:00)

    • Offizieller Beitrag

    Hallo,

    Eine einfache Möglichkeit von vielen ist, einen Hash aus der der E-Mail und dem Passwort zu bilden, bspw. auf diese Weise:

    PHP
    $sEmail = $_POST['email']; // E-Mail vom User eingegeben
    $sPasswordHash = password_hash( $_POST['password'] ); // Hash des Passwortes vom User eingeben
    $sHashId = hash( 'sha256', implode('$', [$sEmail, $sPasswordHash]) );
    
    
    $sDoubleOptInLink = 'http://apple231.bplaced.net/Registriervorgang/registration_weiterleitung.php?' . $sHashId;


    Diesen Link versendest Du dann per Mail an die eingegebene E-Mail Adresse. Zusätzlich speicherst Du den Hashwert in ein weiteres Feld bei dem User.
    Auf der Zielseite ( bei Dir: registration_weiterleitung.php ) kannst Du das bspw. so abfangen:

    PHP
    if ( !empty($_SERVER['QUERY_STRING']) ) {
    
    
        $aData = explode( '$', $_SERVER['QUERY_STRING'] );
    
    
    }

    In $aData hast Du jetzt ein Array mit der E-Mail und dem Hashwert zur Verification.
    Jetzt musst Du nur noch schauen, ob es einen Eintrag in der DB gibt, der mit der E-Mail Adresse und dem Hashwert übereinstimmt!

    Das ist wie gesagt nur eine von vielen Möglichkeiten und aufgrund der Überschaubarkeit auch ohne Datenfilterung etc.

    Gruß Arne

  • Hey,

    Mein aktueller Stand sieht wie folgt aus:

    PHP wirft mir dabei immer diese Exception :


    Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'rere' in 'where clause'' in /users/apple231/www/Registriervorgang/registration_weiterleitung.php:38 Stack trace: #0 /users/apple231/www/Registriervorgang/registration_weiterleitung.php(38): PDO->prepare('SELECT hashid F...') #1 {main} thrown in /users/apple231/www/Registriervorgang/registration_weiterleitung.php on line 38


    Das heißt, das die spalte nicht gefunden worden ist. Ich sag selektiere/wähle die spalte hashid aus wo benutzername ist $user. ($user ist der Session Benutzername dieser wiederum ist der Benutzername der eingegeben worden ist.) Aber wie kann es sein ?

    Und wie ich es nun vergleichen soll. Hab ich nochnicht gemacht.
    Sollte man da mittels fetch die einzelne Spalte durchgehen und dann in dieser bedingung die if statements reinmachen ?

    Gruß,
    Stef

    Einmal editiert, zuletzt von Stef (7. September 2016 um 18:44)

  • Hey,

    ich glaube den Fehler gefunden zu haben in sql statement es waren die anführungszeichen..

    Nun das nächste Problem. Es kommt zwar kein error mehr aber es wird nichts ausgegeben.

    Die Ausgabe mache ich so:

    PHP
    <?php 
                            if(isset($antwort) && count($fehler) === 0)
                            {
                                echo $antwort; 
                            } else if (isset($fehler) && count($fehler) > 0){
                                implode('<br>',$fehler) ;
                            }
                        ?>

    Warum gibt der da nix aus ?

    Gruß,
    Stef

    • Offizieller Beitrag

    Mir scheint, Du weißt nicht so recht, was Du tust, kann das sein?

    $check ist bei Dir ein Array
    $result ist eine ResourceId oder false
    Du vergleichst beide typsicher und erwartest was?

    In dem Fall sollte zwar die else if Bedingung greifen und $fehler zumindest dort befüllt werden.
    Jetzt musst Du debuggen. Lass Dir die Variablen mit var_dump() an den entsprechenden Stellen ausgeben und schau, ob darin das ist, was Du erwartest.

    Ich könnte mir vorstellen, dass die Query keine Datensätze oder sogar einen false liefert.
    Evtl. passiert die Ausgabe auch an der falschen Stelle?

    Sieht insgesamt noch sehr abenteuerlich aus...
    ;)

  • Hey,

    so ne richtige Ahnung habe ich nicht. Hab ich ja auch gesagt weil ichs noch nie gemacht habe und nicht weiß wies geht.

    Aber das ist ja klar wenn ich mit === auf den Typ prüfe das da eigentlich nichts ausgegeben wird. Da das eine ein array und das andere kein array ist.

    Habe nun die 3 vars ($statement, $check, $result) ausgeben lassen. Das kommt raus:

    statement: object(PDOStatement)#2 (1) { ['queryString']=> string(73) "SELECT hashid FROM Registrierdaten WHERE benutzername = "Stef Hunnington"" }
    result: bool(true)
    check:array(1) { [0]=> string(64) "a1d22d92f96a0bb64032aaf25ebb9b68b6b98122e755fa7406e03a5f0a73fd1d" }

    Jetzt fehlt da noch die hashid von Datenbank..Eine typsichere vergleichung ist doch da sinnvoll. Wie könnte man es dann sonst machen ?

    Abenteuerlich ? :D

    Stef

    Einmal editiert, zuletzt von Stef (8. September 2016 um 15:38)

    • Offizieller Beitrag

    Hallo,

    Die Ausgabe der Query kann nicht aus dem von Dir geposteten Script stammen, denn

    • fehlen dort die Quotes komplett
    • nutzt MySQL SingleQuotes für VARCHAR- und TEXT-Datentypen


    Desweiteren:
    Mit $result kannst Du nur prüfen, ob die Abfrage erfolgreich war, hast aber kein Ergebnis.
    Du vergleichst also in dem Moment typsicher einen Boolean ( $result ) mit einem Array ( $check ), da kann nur false bei rauskommen!

    Warum die Fehler nicht ausgegeben werden lassen wir mal für den Moment dahingestellt.
    Wichtig ist erstmal, was für eine Logik Du verfolgst, bzw. verfolgt werden muss!

    • Wurde die Query korrekt verarbeitet ( $result )?
    • Gibt es einen resultierenden Datensatz aus der Query ( fehlt bei Dir! )
    • Ist die resultierende HashId aus der Query gleich der aus $check?


    Diese drei Fälle musst Du mindestens verarbeiten, um dem Ziel näher zu kommen.

    Weiterhin nutzt Du $check anders, wie ich.
    Wenn Du den Link so versendest, wie ich, steht in $_SERVER['QUERY_STRING'] etwas in der Form:

    Code
    test@example.com$a1d22d92f96a0bb64032aaf25ebb9b68b6b98122e755fa7406e03a5f0a73fd1d


    Wenn Du dann per explode beim $ trennst, sollte Dein Array $check so aussehen:

    Code
    Array (
        [0] => test@example.com
        [1] => a1d22d92f96a0bb64032aaf25ebb9b68b6b98122e755fa7406e03a5f0a73fd1d
    )


    Bei Dir hat das Array allerdings nur ein Element, was mich vermuten lässt, dass Du die Mail mit dem $-Zeichen nicht vorangestellt hast.
    Die Abfrage innerhalb der Datenbank würde ich allerdings auf die E-Mail beziehen und nicht auf den Benutzernamen.


    EDIT ( Code grob überarbeitet ):
    Hier mal eine Variante, die zumindest von der Logik passen sollte, allerdings ungetestet offline im Editor angepasst!


    Die IF-Olympiade bekommt man sicher auch noch besser hin, aber hier soll erstmal nur die Logik aufzeigen, die Du verfolgen musst.

    Gruß Arne

  • Hey,

    doch die kommen wirklich aus meinem Script..

    Aso, ja das stimmt.

    Ok. Nein $check nutze ich wie du es mir oben in dem script gecodet hast.

    PHP
    $check = explode( '$', $_SERVER['QUERY_STRING'] );

    Ja mein Problem war ja dies, dass ich noch nicht überprüft habe ob es den selben Datensatz gibt, ich wusste ja nicht wie ichs machen soll.

    Paar Fragen:

    Was macht den ___DIR___ ?

    PHP
    $statement = $pdo->prepare("SELECT hashid FROM Registrierdaten WHERE benutzeremail = ?" );


    Warum wird da ein ? genommen als value der email ?

    PHP
    $statement->bindParam( 0, $userEmail, PDO::PARAM_STR );


    Was macht denn diese Zeile ?

    Vielen Dank schonmal :)

    EDIT:

    Habe nun deins getestet und es kommt folgender error wie bei mir :

    Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY093]: Invalid parameter number: Columns/Parameters are 1-based' in /users/apple231/www/Registriervorgang/registration_weiterleitung.php:38 Stack trace: #0 /users/apple231/www/Registriervorgang/registration_weiterleitung.php(38): PDOStatement->bindParam(0, 'Hans.klais...', 2) #1 {main} thrown in /users/apple231/www/Registriervorgang/registration_weiterleitung.php on line 38

    Gruß,
    Stef

    4 Mal editiert, zuletzt von Stef (8. September 2016 um 18:25)

    • Offizieller Beitrag

    Hallo,

    Zitat

    Ok. Nein $check nutze ich wie du es mir oben in dem script gecodet hast.

    Nein, tust Du nicht ;)
    Dann wäre wie gesagt auch die Mail Adresse drin. Du versendest - wie bereits gesagt - den Link ohne die Mail-Adresse.
    Kannst Du machen, aber dann benötigst Du das explode() nicht, weil Du eh nur den Hashwert hast.

    Zitat

    Was macht den ___DIR___ ?

    Damit bekommst Du den absoluten Serverpfad zu der Datei, in der Du dies anwendest: http://php.net/manual/de/language.constants.predefined.php

    Zitat

    Warum wird da ein ? genommen als value der email ?


    Das ist ein Platzhalter, numerisch basiert ist. PDO :: prepare() liefert ein PreparedStatement, es macht Sinn, diese auch zu verwenden, wenn man prepare() schon verwendet.

    Zitat

    Was macht denn diese Zeile ?


    Die bindet eine Variable an das PDOStatement: http://php.net/manual/de/pdostatement.bindparam.php

    Zitat

    Habe nun deins getestet und es kommt folgender error wie bei mir :

    Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY093]: Invalid parameter number: Columns/Parameters are 1-based' in /users/apple231/www/Registriervorgang/registration_weiterleitung.php:38 Stack trace: #0 /users/apple231/www/Registriervorgang/registration_weiterleitung.php(38-): PDOStatement->bindParam(0, 'Hans.klais...', 2) #1 {main} thrown in/users/apple231/www/Registriervorgang/registration_weiterleitung.php on line 38

    Das ist mein Fehler, die Parameter für das Prepared Statement sind nicht 0- sondern 1-basierend, die Zeile muß richtigerweise so lauten:

    PHP
    $statement->bindParam( 1, $userEmail, PDO::PARAM_STR );
  • Hey,

    naja egal jetzt.

    Ah ok. Danke jetzt kommt der error nichtmehr, jedoch wird nichts ausgegeben auf der Seite..

    Meine Ausgabe sieht wie folgt aus :

    Weiß nicht warum.

    Vielen Dank!

    Stef

    • Offizieller Beitrag

    Kannst Du bitte mal alles in Zusammenhang posten?!
    Du postest den ganzen Code und separat die Ausgabe, die nicht hinhaut.
    Wo genau Du diesen Code verwendest, sieht man nicht. Also bitte mal zusammenhängend, damit man erkennen kann, ob er überhaupt zur Ausgabe kommt.

    Danke und Gruß
    Arne

  • Hey,

    ja ok. Etwas ungeschickt von mir.

    Hier der ganze code:

    Dann wird beim bindparm die var useremail mit dem Wert in der Spalte Hashid gegeneinandergestellt und verglichen ?
    Gruß,
    Stef

    Einmal editiert, zuletzt von Stef (9. September 2016 um 20:26)

    • Offizieller Beitrag

    Hallo,

    Also bei der Fehlerausgabe hast Du kein echo verwendet! implode() allein gibt nichts aus.

    Zitat

    Dann wird beim bindparm die var useremail mit dem Wert in der Spalte Hashid gegeneinandergestellt und verglichen ?


    Nein. Das bindParam() bindet ja nur die E-Mail an die Query und zwar an den Platzhalter mit dem Basisindex 1.
    Die Query, die zur DB gesendet wird, sieht dann bspw. so aus:

    SQL
    SELECT `hashid` FROM `Registrierdaten` WHERE `email` = 'test@example.com'


    Die Query möchte also jeweils die Spalte `hashid` von den Datensätzen haben, wo die Spalte `email` = 'test@example.com' ist.
    I.d.R. sollte dabei max. nur ein Datensatz rauskommen, deshalb interessiert uns hier keine Schleife o.ä.

    Das ganze setzt natürlich voraus, dass es eine Spalte `email` in der Tabelle gibt!

    Ansonsten sehe ich auf den ersten Blick nichts weiter, was die Ausgabe verhindern könnte...

    Gruß Arne

  • Hey,

    ja ok. So ist das.

    Das habe ich vergessen, das echo.
    Ich möchte das die var $ausgabe ausgegeben wird-
    Aber warum wird da diese nicht ausgegeben ?

    Da ist doch eigentlich kein fehler ? Dann sollte doch die var $ausgabe ausgegeben werden.

    Danke.

    Gruß, Stef

    • Offizieller Beitrag

    Teste mal das:

    PHP
    echo ( !empty($fehler) )? implode( '<br>', $fehler ): $ausgabe;


    anstelle dessen:

    PHP
    if ( isset($fehler) && count($fehler) > 0 ) {
    
    
                   implode('<br>', $fehler) ;
    
    
                    } else if (isset($ausgabe) && count($fehler) === 0) {
                        echo $ausgabe;
                    }
  • Hey,

    habe es nun mit deinem versucht. Da kommt die ausgabe : Keine Übereinstimmung!

    Die hashid ist in der datenbank 0... Warum denn das ?
    Habe mal die ganzen db hashid vom test hier im Foto:

    http://prnt.sc/cgdw12
    Aber warum sind so viele 0 ? Und nicht die hash id die mit email versendet wird?

    Ich tuh ja die hashid da eintragen in db, da verstehe ich einfach nicht das da 0 eingetragen wird.

    Stef