#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2013
    Location
    S. Texas
    Posts
    8
    Rep Power
    0

    Consolidate tutorial thread: Howto-basic-but-secure-login-system


    I found the thread:
    how-to-program-a-basic-but-secure-login-system

    to be very instructive but with some 300+ posts, the net results have been difficult to extract!

    I'd be very grateful to get access to a full net-result of the numerous exchanges between "E-oreo", "derplumo", "jacques1" and others who collaborated.

    My new user status prevents me from including the full URL, but it was started by E-oreo.

    Thanks in advance to "derplumo" or others who may share the net results.

    Dave Nuttall
    San Antonio, TX
  2. #2
  3. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Posts
    436
    Rep Power
    8
    Hi,

    Sorry for the late response, and this is not a new version. This is simply because I changed a url which won't be a problem, and otherwise it will be 5 minutes of debugging (sorry for this kind of answer ). If you have any problems with it, leave a shout

    the files:

    forgot_password.php:
    PHP Code:
    <?php    

        
    require("common.php");    
        require(
    "lib/rnum.php");   
        require(
    "lib/mail.php");   
        require(
    "lib/password.php");    

        if(!empty(
    $_POST))    
        { 

            if(
    filter_var($_POST['email'], FILTER_VALIDATE_EMAIL))    
            {       

                
    $hash hash('sha256'$_POST['email']);   
                
    $time = new DateTime('24 hours ago');    
                
    $time_formatted $time->format('Y-m-d H:i:s');    

                
    $count_stmt $db->prepare('     
                    SELECT COUNT(*)  as count   
                    FROM sent_emails   
                    WHERE email_address = :email_address AND timestamp >= :time    
                '
    );    
                
    $count_stmt->execute(array(     
                    
    ':email_address' => $hash,    
                    
    ':time' => $time_formatted     
                
    ));     

                
    $times $count_stmt->fetch();    
                
    $email $_POST['email'];   
                
    $user_stmt $db->prepare('    
                    SELECT    
                        user_id    
                    FROM    
                        users    
                    WHERE    
                        email = :email    
                '
    );    
                
    $user_stmt->execute(array(    
                    
    ':email' => $_POST['email']    
                ));    
                
    $user_id $user_stmt->fetchColumn();    
                if(
    $user_id)  // is the mail of a user? 
                
    {          
                    if(
    $times['count'] < 10)   
                    {   
                        
    $deactivation_stmt $db->prepare('    
                        UPDATE    
                        responses    
                        SET    
                        active = 0    
                        WHERE    
                        user = :user    
                        '
    );    
                        
    $deactivation_stmt->execute(array(    
                        
    ':user' => $user_id    
                        
    ));    

                        
    $password_token rnum();   

                        
    $query "    
                        INSERT INTO responses (    
                        reset_key,   
                        user,   
                        secret,   
                        request_timestamp,   
                        request_ip   
                        ) VALUES (    
                        :reset_key,   
                        :user,   
                        :secret,   
                        NOW(),   
                        :request_ip   
                        )"
    ;   

                        
    $secret password_hash($password_tokenPASSWORD_BCRYPT, array("cost" => 10));     
                        
    $reset_key rnum();   
                         
                        
    $request_ip getenv('REMOTE_ADDR');   

                        
    $query_params = array(    
                        
    ':reset_key' => $reset_key,   
                        
    ':user' => $user_id,   
                        
    ':secret' => $secret,   
                        
    ':request_ip' => $request_ip   
                        
    );    

                        
    $stmt $db->prepare($query);    
                        
    $result $stmt->execute($query_params);                    

                        
    $mail_to      $email;   
                        
    $mail_subject 'Forgot password';   
                        
    $mail_body "Hallo,  
                            <br><br>  
                            you or somebody else requested a password reset for your user account at http:/domain.com.  
                            <br><br>  
                            To set a new password, please visit this link:  
                            <br><br>  
                            http://www.domain.com/password_reset.php?reset_key=" 
    $reset_key "&user=" $user "&password_token=" $password_token ."  
                            <br><br>  
                            Do not share the secret code in this link until you've used it. The code will expire in 30 minutes.   
                            <br><br>  
                            If the request was not from you, simply ignore this email. Your password will _not_ be changed.  
                            <br><br>  
                            Do you have further questions? Please contact us at info@domain.com.  
                            <br><br>  
                            Best regards,  
                            <br><br>  
                            domain.com"



                        if(
    mail_f ($mail_to$mail_subject$mail_body) == 1)   
                        {   
                            
    $new_stmt $db->prepare('     
                            INSERT INTO sent_emails (   
                            email_address,   
                            timestamp   
                            ) VALUES (   
                            :email_address,   
                            NOW()   
                            )'
    );    
                            
    $new_stmt->execute(array(     
                            
    ':email_address' => $hash           
                            
    ));          
                        }   
                    }   
                }   
                else{ 
    // is the mail not in the system  
                    
    if($times['count'] < 1)   
                    {   
                        
    $email_adress $_POST["email"];     
                        
    $hash hash('sha256'$email_adress);   
                        
    # the following is for an unregistered address that hasn't reached its request limit yet   

                        # you only need one query   
                        
    $unsub_data_stmt $db->prepare('     
                            SELECT    
                                unsubscribed   
                                , email_key   
                            FROM    
                                unsubscribed_email_addresses    
                            WHERE   
                                email_address = :hash   
                        '
    );   
                        
    $unsub_data_stmt->execute(array(     
                            
    ':hash' => $hash    
                        
    ));     
                        
    $unsub_data $unsub_data_stmt->fetchColumn();   

                        
    // If we don't have a record of the address yet, or if the address isn't unsubscribed,   
                        // send an email; in case of a new record, generate a new token, otherwise, use the old one;   
                        // $valid_token determines whether the newly generated token has been stored and can actually   
                        // be used; if not, it shouldn't be in the mail   
                        
    $send_mail $valid_token false;   
                        if ( 
    $unsub_data === false )   
                        {   
                            
    $send_mail true;   
                            
    $unsub_token rnum();   
                            
    $unsubscribe_stmt $db->prepare('     
                                INSERT INTO unsubscribed_email_addresses (     
                                    email_address   
                                    , email_key   
                                ) VALUES (     
                                    :email_key   
                                    , :email_address    
                                )  
                            '
    );     
                            
    $valid_token $unsubscribe_stmt->execute(array(   
                                
    ':email_address' => $email_hash   
                                
    ':email_key' => $unsub_token   
                            
    ));   
                        }   
                        elseif ( !
    $unsub_data['unsubscribed'] )   
                        {   
                            
    $send_mail $valid_token true;   
                            
    $unsub_token $unsub_data['email_key'];   
                        }   

                        if ( 
    $send_mail )   
                        {   
                            
    $email_adress $_POST["email"];  
                            
    $mail_subject "Forgot password";  
                            
    $mail_body "Hallo,  
                                <br><br>  
                                you or somebody else entered your email address into the password reset form at http://domain.com, but your address is not registered in our system.  
                                <br><br>  
                                If you have an account on our website, you must have used a different email address. Please try again with your other addresses.  
                                <br><br>  
                                If you did not use our form, we apologize for this email. Please ignore it. If you never want to receive the email again, you can mark your address as blocked in our system:  
                                <br><br>  
                                http://www.domain.com/no_mail.php?email_key=" 
    $unsub_token "  
                                <br><br>  
                                Do you have further questions? Please contact us at info@domain.com.  
                                <br><br>  
                                Best regards,  
                                <br><br>  
                                domain.com"
    ;   

                            
    # put the mail text into an external template; only append the token if $valid_token   
                            
    if( mail_f($email_adress$mail_subject$mail_body) )    
                            {    
                                
    $sent_stmt $db->prepare('      
                                    INSERT INTO sent_emails (   
                                        email_address,    
                                        , timestamp    
                                    ) VALUES (    
                                        :email_address   
                                        , NOW()    
                                    )   
                                '
    );     
                                
    $sent_stmt->execute(array(      
                                    
    ':email_address' => $hash            
                                
    ));   
                            }   
                        }  
                    }   
                }   
                echo 
    "Unless your limit has been reached, we'll send you an email";  
            }   
            else{   
                echo 
    "This emailadress is invalid!";   
            }                  
        }   
    ?> <html> 
        <body> 
            <h1>Forgot password</h1> 
            <form action="forgot_password.php" method="post">    
                Email:   
                <input type="text" name="email" value="" />    
                <br /><br />    
                <input type="submit" value="Recover" />    
            </form>    
                         
        </body> 
    </html>

    password_reset.php:
    PHP Code:
    <?php    

           
        
    require("common.php");    
        require(
    "lib/password.php");   



        
    $reset_key $_GET["reset_key"];   
        
    $user $_GET["user"];   
        
    $password_token $_GET["password_token"];   
           

        if(!empty(
    $_POST))    
        {    
            
    $reset_echo true
            
    $reset_successful false;  
            
    $reset_key $_POST['reset_key'];   
            
    $user $_POST['user'];   
            
    $password_token $_POST['password_token'];   

            
    $query "    
                SELECT    
                    user   
                    , secret  
                    , request_timestamp  
                FROM  
                    responses   
                WHERE    
                    reset_key = :reset_key  
                    AND user = :user  
                    AND NOT used  
                    AND active  
                "
    ;  
                
            
    $query_params = array(    
                
    ':reset_key' => $reset_key,  
                
    ':user' => $user   
            
    );    
                
              
            
    $stmt $db->prepare($query);    
            
    $result $stmt->execute($query_params);    
               
            
    $row $stmt->fetch();    

            if(
    $row)   
            {               
                
    $created DateTime::createFromFormat('Y-m-d G:i:s'$row['request_timestamp']);    
                if ( 
    $created >= new DateTime('30 minutes ago') )    
                {   
                    if ( 
    password_verify($password_token$row['secret']) )   
                    {   
                        
    $query "    
                            UPDATE users    
                            SET    
                                password = :password   
                            WHERE   
                            user_id = :user_id    
                            "
    ;    

                        
    $hash password_hash($_POST['password'], PASSWORD_BCRYPT, array("cost" => 10));            

                        
    $query_params = array(    
                            
    ':password' => $hash,    
                            
    ':user_id' => $row['user']   
                        );    


                        
    $stmt $db->prepare($query);    
                        
    $result $stmt->execute($query_params);    
                        
    $reset_successful true;  
                    }    
                }                        
            }           
        }   
    ?> <html> 
        <body> 
            <h1>Reset password</h1> 
            <?php   
                
    if ($reset_echo) { 
                    if (
    $reset_successful)  
                        echo 
    'Your password has been reset!';  
                    else 
                        echo 
    'This token has already been used, expired or inactive, please request a new one';   
                } 
            
    ?> 
            <form action="password_reset.php" method="post">     
                New password:   
                <input type="text" name="password" value="" />    
                <br /><br />    
                <input type="hidden" name="reset_key" value="<?php echo $reset_key?>" />   
                <input type="hidden" name="user" value="<?php echo $user?>" />   
                <input type="hidden" name="password_token" value="<?php echo $password_token?>" />   
                <input type="submit" value="Login" />    
            </form>   
        </body> 
    </html>

    no_mail.php:
    PHP Code:
    <?php  

    require("common.php");  


    if( !empty(
    $_GET['email_key']) )  
    {  
        
    $key_stmt $db->prepare('  
            SELECT  
                unsubscribed  
            FROM  
                unsubscribed_email_addresses  
            WHERE  
                email_key = :email_key  
        '
    );  
        
    $key_stmt->execute(array(  
            
    ':email_key' => $_GET['email_key']  
        ));  
        
    $unsub $key_stmt->fetchColumn();  
        if ( 
    $unsub === false )            // key doesn't exist  
            
    echo 'Invalid email key. Please check the URL in your previous notification email.';  
        else                            
    // key exists  
        
    {  
            if ( !
    $unsub )                // not unsubscribed yet  
            
    {  
                
    $unsub_stmt $db->prepare('  
                    UPDATE  
                        unsubscribed_email_addresses  
                    SET  
                        unsubscribed = 1  
                    WHERE  
                        email_key = :email_key  
                '
    );  
                
    $unsub_stmt->execute(array(  
                    
    ':email_key' => $_GET['email_key']  
                ));  
                if ( 
    $unsub_stmt->rowCount() )  
                    
    $unsub true;  
            }  
            if ( 
    $unsub )  
                echo 
    'Your email address has been blocked in our system. You will no longer receive notification emails.';  
            else  
                echo 
    'There was a technical issue. Please try again later.';  
        }  
    }  
    else  
        echo 
    'Missing email key. Please check the URL in your previous notification
    Last edited by derplumo; December 8th, 2013 at 05:07 AM. Reason: resolved a bug
  4. #3
  5. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Posts
    436
    Rep Power
    8
    Hi,

    Just wanted to share that this is script is still in ALPHA phase, I will update it in the next week. In this update there will be a fix for the 'used' tokens which now you can still use.

    Also I'm glad to see that there are already 1800 people who have seen this
  6. #4
  7. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Posts
    436
    Rep Power
    8
    Ok done, I will post the scripts tonight or tomorrow, stay tuned. This version will include a captcha for registration and the forgot password system to avoid abuse by -for example- spambots.
  8. #5
  9. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Posts
    436
    Rep Power
    8

IMN logo majestic logo threadwatch logo seochat tools logo