Jump to content

benanamen

Members
  • Posts

    2,134
  • Joined

  • Last visited

  • Days Won

    42

Posts posted by benanamen

  1. I don't care how @Jaques1 responds (It took a few posts :happy-04: ). His knowledge of Security is NSA, CIA, & MI6 level expertise. Where ever he learned it, it wasn't from the Php Manual.

    • Like 2
  2. Since @muddy_funster brought up a specific case that I was going to post about, I will post here. Especially interested in @Jaques1 feedback.

     

    In the case where you want to track the reset data you obviously would need a separate password reset table. My question is, what would be the best/most secure way to handle it.

     

    Currently my forgot password reset code will add a row with the proper data needed for a reset along with an expiration datetime. On successful reset only the hash is deleted leaving only tracking data. Any hashes not reset are not usable after the timeout.

     

    The following code was originally written several years ago with only the hashing and password generation parts updated per the recommended code from @Jaques1.

     

    Anyone see any problems or recommend any changes?

     

    forgot.php

    <?php
    /*
     * Last Modified <!--%TimeStamp%-->6/23/2016 10:39 AM<!---->
     */
    
    require('./config.php');
    
    $show_error = false;
    if (!empty($_POST))
        {
        //------------------------------------------------------------------------
        // Trim $_POST Array
        //------------------------------------------------------------------------
    
        $_POST = trim_array($_POST);
    
        //------------------------------------------------------------------------
        // Validate Form Input
        //------------------------------------------------------------------------
    
        $error = array();
    
        if (empty($_POST['forgot']))
            {
            $error['forgot'] = 'Email Required.';
            }
    
        //------------------------------------------------------------------------
        // Check for errors
        //------------------------------------------------------------------------
    
        if (count($error))
            {
            $show_error = true;
            }
        else
            {
            // Check DB for matching username and password.
            $sql = "SELECT user_id, email FROM users WHERE email = ?";
    
            $stmt = $pdo->prepare($sql);
            $stmt->execute(array(
                $_POST['forgot']
            ));
            $row = $stmt->fetch();
    
            //---------------------------------------------------------------------------------------------
            // No Results - Redirect
            //---------------------------------------------------------------------------------------------
    
            if (!$stmt->rowCount())
                {
                die(header("Location: {$_SERVER['SCRIPT_NAME']}?fail"));
                }
    
            //---------------------------------------------------------------------------------------------
            // Check Reset table to see if there are multiple incomplete reset attempts
            // Block access if too many.
            //---------------------------------------------------------------------------------------------
    
            $user_id = $row['user_id'];
            /*
            $sql = "SELECT COUNT(*) from password_reset WHERE user_id = ? AND password_reset_key  <>'' " ;
            $stmt = $pdo->prepare($sql);
            $stmt->execute(array(
            $user_id
            ));
    
            $count = $stmt->fetchColumn();
            if ($count==3){
            echo '<div class="error_custom">Too Many Incomplete Password Resets. Contact Site Admin</div>';
            //echo $count;
            exit;
            }
            */
            //---------------------------------------------------------------------------------------------
            // Log Password Reset Data
            //---------------------------------------------------------------------------------------------
    
            // From http://forums.phpfreaks.com/topic/298729-forgotten-password/?hl=%2Bmcrypt_create_iv#entry1524084
            // generate 16 random bytes
            $raw_token = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
    
            // encode the random bytes and send the result to the user
            $encoded_token = bin2hex($raw_token);
    
            // hash the random bytes and store this hash in the database
            $token_hash = hash('sha256', $raw_token);
    
            /**
             * Interval specification.
             *
             * The format starts with the letter P, for "period." Each duration period is
             * represented by an integer value followed by a period designator. If the
             * duration contains time elements, that portion of the specification is
             * preceded by the letter T.
             *
             * @link http://www.php.net/manual/en/dateinterval.construct.php
             *
             * String to time option
             * $password_reset_expiration_datetime = date('Y-m-d H:i:s', strtotime("+5 min"));
             *
             */
            $period_designator = 'PT';
            $timespan          = 'M'; //Minutes
            //$period_designator = 'P'; $timespan ='D'; //Days
            $timespan_add      = 1; // Amount of days or minutes
    
            $time = new DateTime(date('Y-m-d H:i:s'));
            $time->add(new DateInterval($period_designator . $timespan_add . $timespan));
    
            $password_reset_expiration_datetime = $time->format('Y-m-d H:i:s');
    
            $sql  = "INSERT INTO password_reset (user_id, password_reset_username_email, password_reset_requesters_ip, password_reset_key,password_reset_expiration_datetime) values(?, ?, ?, ?, ?)";
            $stmt = $pdo->prepare($sql);
            $stmt->execute(array(
                $user_id,
                $_POST['forgot'],
                $_SERVER["REMOTE_ADDR"],
                $token_hash ,
                $password_reset_expiration_datetime
            ));
    
            //---------------------------------------------------------------------------------------------
            // Email Reset Data
            //---------------------------------------------------------------------------------------------
    
            $mail_to        = $row['email'];
            $mail_from_name = "Meet Market";
            $mail_subject   = "Lost Password";
            $mail_message   = "You have requested a forgotten password reset." . PHP_EOL . PHP_EOL;
    
            $mail_message .= "Click the link below or enter the following code on the Password Reset page. Reset Code: $encoded_token\n$url_website/reset.php?k=$encoded_token";
    
            // Send mail
            mail($mail_to, $mail_subject, $mail_message, "From: $mail_from_name <$email_from>\r\n");
    
            die(header("Location: reset.php?sent"));
    
            if (DEBUG == 1)
                {
                debug_show_sql();
                }
    
            } // End if
        } // End (!empty($_POST))
    
    
    
    include('./includes/header.php');
    ?>
    <div class="container">
    <?php
    if (isset($_GET['fail']))
        {
        echo '<div class="error_custom">Invalid Username or Email</div>';
        }
    
    //--------------------------------------------------------------------
    // Display Logo
    //--------------------------------------------------------------------
    
    logo();
    
    //---------------------------------------------------------------------------------------------
    // Forgot Password Form
    //---------------------------------------------------------------------------------------------
    
    if ($show_error)
        {
        show_form_errors($error);
        }
    ?>
    <form class="form-horizontal" action="<?= $_SERVER['SCRIPT_NAME'] ?>" method="post">
       <div class="form-group <?= !empty($error['forgot']) ? 'has-error' : '' ?>">
          <label class="col-md-4 control-label" for="forgot">Enter Email</label>
          <div class="col-md-4">
             <input id="forgot" name="forgot" type="text" placeholder="Email Address" class="form-control input-md">
             <span class="help-block">A password reset email will be sent to you.</span>
          </div>
       </div>
       <div class="form-group">
          <div class="col-md-offset-4  col-sm-10">
             <button id="submit" type="submit" name="submit" class="btn btn-primary">Reset Password</button>  <a href="./login.php">Login</a>
          </div>
       </div>
    </form>
    
    </div><!--/ container -->
    
    <?php
    include('./includes/footer.php');
    ?>
    

    reset.php

    <?php
    /*
     * Last Modified <!--%TimeStamp%-->3/11/2016 9:20 PM<!---->
     */
    
    session_start();
    require('./config.php');
    
    if ($_POST)
        {
        //------------------------------------------------------------------------
        // Trim $_POST Array
        //------------------------------------------------------------------------
    
        $_POST = trim_array($_POST);
    
        //------------------------------------------------------------------------
        // Validate Form Input
        //------------------------------------------------------------------------
    
        if (empty($_POST['reset_code']))
            {
            $error['reset_code'] = 'Reset Code required.';
            }
    
        if (empty($_POST['new_password']))
            {
            $error['new_password'] = 'New Password is required.';
            }
    
        if (empty($_POST['new_password_confirm']))
            {
            $error['new_password_confirm'] = ' Confirm New Password is required.';
            }
        elseif ($_POST['new_password'] != $_POST['new_password_confirm'])
            {
            $error['new_password_confirm'] = 'Passwords do not match.';
            }
    
        //---------------------------------------------------------------------------------------------
        // Check for errors
        //---------------------------------------------------------------------------------------------
    
        if (count($error))
            {
            $show_error = true;
            }
        else
            {
            // From http://forums.phpfreaks.com/topic/298729-forgotten-password/?hl=%2Bmcrypt_create_iv#entry1524084
            $encoded_token = $_POST['reset_code'];
    
            // decode the token and hash it
            $raw_token  = hex2bin($encoded_token);
            $token_hash = hash('sha256', $raw_token);
    
            // Check DB for matching reset key.
            $sql  = "SELECT user_id, password_reset_username_email, password_reset_key FROM password_reset WHERE password_reset_key=?";
            $stmt = $pdo->prepare($sql);
            $stmt->execute(array(
                $token_hash
            ));
            $row = $stmt->fetch(PDO::FETCH_ASSOC);
    
            //---------------------------------------------------------------------------------------------
            // No Results - Redirect
            //---------------------------------------------------------------------------------------------
    
            if (!$stmt->rowCount())
                {
                die(header("Location: {$_SERVER['SCRIPT_NAME']}?fail"));
                }
    
            //---------------------------------------------------------------------------------------------
            // Update Password
            //---------------------------------------------------------------------------------------------
    
            $hashed_new_password = password_hash($_POST['new_password'], PASSWORD_DEFAULT);
            $sql                 = "UPDATE users SET password= ? WHERE user_id = ?";
            $stmt                = $pdo->prepare($sql);
            $stmt->execute(array(
                $hashed_new_password,
                $row['user_id']
            ));
    
            /**
             * Delete used reset key. We are leaving unused keys to be able to see how
             * many times a user requests a reset before they complete a password reset.
             * Since reset keys are only valid for limited time there is no risk leaving
             * the unused keys.
             *
             * TODO: DEV: Might be a good idea to block password resets after X times of not
             * completing a password reset. i.e. User has submitted 3 password reset requests
             * and hasnt entered reset key and new password. They are now blocked. Alert admin.
             *
             * $sql = "SELECT COUNT(*) from password_reset WHERE user_id = ? AND password_reset_key  <>'' " ;
             *
             **/
    
            $sql  = "UPDATE password_reset SET password_reset_key = ? WHERE user_id = ? AND password_reset_key = ?";
            $stmt = $pdo->prepare($sql);
            $stmt->execute(array(
                NULL,
                $row['user_id'],
                $row['password_reset_key']
            ));
    
            if (DEBUG == 1)
                {
                debug_show_sql();
                }
    
            //---------------------------------------------------------------------------------------------
            // Send Reset Email
            //---------------------------------------------------------------------------------------------
    
            $mail_to        = $row['password_reset_username_email'];
            $mail_from_name = "Some Site";
            $mail_subject   = "Password has been reset";
            $mail_message   = "Password has been reset";
    
            // Send mail
            mail($mail_to, $mail_subject, $mail_message, "From: $mail_from_name <$email_to>\r\n");
    
            die(header("Location: login.php?reset"));
    
            } // End if ($validated)
        } // End (!empty($_POST))
    
    //---------------------------------------------------------------------------------------------
    // Reset Code Form
    //---------------------------------------------------------------------------------------------
    
    include('./includes/header.php');
    
    if (isset($_GET['fail']))
        {
        echo '<div class="error_custom">Invalid code or time expired</div>';
        }
    
    logo(); // Display Logo
    
    if (isset($_GET['sent']))
        {
        echo '<div class="success">A reset code has been been emailed to you. Enter code below to reset your password or click link in email.</div>';
        }
    
    isset($show_error) ? show_form_errors($error) : ''; // Display Form errors if any
    
    if (isset($_GET['k']))
        {
        $reset_code = $_GET['k'];
        }
    if (isset($_POST['reset_code']))
        {
        $reset_code = $_POST['reset_code'];
        }
    ?>
    <form class="form-horizontal" action="<?= $_SERVER['SCRIPT_NAME'] ?>" method="post">
    
       <div class="form-group <?= !empty($error['reset_code']) ? 'has-error' : '' ?>">
          <label class="col-md-4 control-label" for="reset_code">Reset Code</label>
          <div class="col-md-5">
             <input id="reset_code" name="reset_code" type="text" placeholder="Reset Code" class="form-control input-md" value="<?= !empty($reset_code) ? htmlspecialchars($reset_code) : '' ?>">
          </div>
       </div>
    
       <div class="form-group <?= !empty($error['new_password']) ? 'has-error' : '' ?>">
          <label class="col-md-4 control-label" for="new_password">Enter New Password</label>
          <div class="col-md-5">
             <input id="new_password" name="new_password" type="text" placeholder="Enter New Password" class="form-control input-md" value="<?= !empty($_POST['new_password']) ? htmlspecialchars($_POST['new_password']) : '' ?>">
          </div>
       </div>
    
       <div class="form-group <?= !empty($error['new_password_confirm']) ? 'has-error' : '' ?>">
          <label class="col-md-4 control-label" for="new_password_confirm">Confirm New Password</label>
          <div class="col-md-5">
             <input id="new_password_confirm" name="new_password_confirm" type="text" placeholder="Confirm New Password" class="form-control input-md" value="<?= !empty($_POST['new_password_confirm']) ? htmlspecialchars($_POST['new_password_confirm']) : '' ?>">
          </div>
       </div>
    
       <div class="form-group">
          <div class="col-md-offset-4 col-sm-10">
             <button id="submit" type="submit" name="submit" class="btn btn-primary">Reset Password</button>  <a href="./login.php">Login</a>
          </div>
       </div>
    
    </form>
    <?php
    include('./includes/footer.php');
    ?>
    
  3.  

     

    I'm even impressed to know you can keep interactivity with the server after passing from the sql daemon into the core OS

     

    Don't be. That's not even how I did it.

     

    * @Jaques1 is just being @Jaques1 and he is right to be serious about security related issues. Even a computer that is not plugged in the wall and not connected to the net needs to be secured.

    • Like 1
  4. @muddy_funster, I have gained complete access to a server through an SQL injection exploit. It is really not difficult if you know what you're doing. Security should always be taken seriously.

  5. In my "defense" my response was directed to the "Experts" that posted on this thread and not to the OP. An expert doesn't need an explanation of obsolete code. Anyone with enough expertise to respond with help to the OP "should" know enough to spot the obsolete code and at least point it out even if you are going to help fix it. To my surprise, nobody did. Had I responded to the OP, I would have directed OP to the PDO tutorial that I just did. In the words of Forrest Gump, "That's all I have to say about that".

  6. Troll? Hardly! No one in this thread even mentioned a single thing about the OP's obsolete code. So I am a troll because I pointed out something VERY important, not only for the OP, but for the web?

     

    How are you helping when you don't even tell the OP that his code is obsolete and point him the way of PDO or at least Mysqli? Help with bad code all you want but at least tell the OP so he knows.

     

     

     

     

    In due time, assuming the user pursues learning PHP, they will eventually see the error in their legacy methods

     

    In due time? For real? The OP is here right now. Teach him the right way while he is here, not some mystical time in the future.

     

     

    OP: Check out this link on how to use PDO: https://phpdelusions.net/pdo

    • Like 1
  7. Benanamen say: Stop teaching people how to use code that has been deprecated starting over TEN years ago. There is just no excuse to keep that kind of code alive. Better to teach the OP's PDO or at the least Mysqli. There is no reward waiting for you in programmers heaven for fixing obsolete code. Word on the street is you may even get sent somewhere else for doing it.

    • Like 1
  8. Since I don't know what it is you're actually trying to accomplish I can't give you a good answer. You have provided no code. Thats pretty much the first thing you need to provide if you have it. How about an explanation of what you're trying to accomplish, the big picture. (Not how to do what you think needs to be done.)

     

    The ol what have you tried, what was the result, what is the expected result, along with a good overview of what is to be accomplished... I am sure you know of this.

  9. The user should not dictate how you store the data. Allow the user whatever format you decide for him to enter it but piece it back together to store it as a proper date time field. You can parse that out anyway you want later.

  10. Click the info button on your remote control and it will give you the start and end time of your show. You could also look in the TV Guide if you have one. Some systems can give you all the start and end times for that particular show during the programming period.

     

    * split posted for some reason

  11. Any duration needs a start and end. Simply have a start and end datetime columns in your database, unless one of them is calculated off one of the dateimes, then you just need one date time column that is either a start or end date time.

  12. I've not done any exhaustive testing on that.

     

    I have done pretty exhaustive testing. I wasn't going to say something is the correct way to do it just because @Jaques1 said so or anyone else for that matter. I want to know, by code if something is wrong or not.

     

    Although I preach the request method on forums, I still use if ($_POSTin apps that I am the sole coder and are not public access.

     

     

    Checking for fields to determine if the form was submitted is ok, checking for buttons could be problematic.

     

    Your right. The IE8 issue is specific to how it handles submit and will completely fail with notice of any kind that it did.

     

     

     I would always make my submit check one of either if ($_POST) or if (isset($_POST['someTextField']))

     

    Either of those will work. Checking for submit will not.

     

     

    Also as ginerjm says, if someone isn't submitted the data my script expects, then I don't care. If the data isn't correct then the script can't do it's job. 

     

    On this issue, it doesn't matter one bit if all the data is correct. IE8 behaves exactly the same as if you didn't click the submit button. The script cannot "Do it's job" if it never runs.

  13. Pretty sure he is referring to me. After many back and forth with @Jaques1 and actual testing, it is a fact that using request method is the correct and failproof way to go.

     

    At this point I am pretty burned out on explaining the whys. Perhaps @Jaques1 will do it. One particular instance counting on a button to be submitted that will completely fail is with IE8. I don't want to hear about how its older or not many people use it. It is the default version for windows 7 and unless someone does an upgrade, that is what they have. If you don't care that your script will completely fail for all those users, then so be it.

     

    As Jaques1 would tell you, "It is naive to expect that every user is going to use YOUR form to submit data". And in the case of a user using cURL, they are not going to be submitting your button in the request and will have no way of even knowing you are expecting it for the script to work. You have to make way too many assumptions doing anything other than the foolproof 

     

    if ($_SERVER['REQUEST_METHOD'] == 'POST')

     

    Do your own testing or ask @Jaques1 to explain it in detail. I was previously a if ($POST){} coder before @Jaques1 undisputedly schooled me.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.