简体   繁体   中英

What could cause a PHP insert into a MYSQL database to wrongly insert data from another user into a row?

A student of mine was saving her score for a learning game to a MySQL database but somehow a different person's name ended up being stored in her database row. How is this possible? Here is the PHP for the insert.

// Get Configuration file
require "configenzymatic.php";

// Connect to your server

$dbh = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $user, $pass, array(PDO::MYSQL_ATTR_FOUND_ROWS => true));

$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

///////////////////////////////////////////////////////
// Status Checker
///////////////////////////////////////////////////////
if ($_GET["status"]) {
    echo "online";
    exit;
}

///////////////////////////////////////////////////////
// Upload new score
///////////////////////////////////////////////////////

//set POST data as data to be checked and updated
$firstname = $_POST['firstname'];
$lastname = $_POST['lastname'];
$email = $_POST['email'];
$password = $_POST['password'];
$level1right = $_POST['level1right'];
$level1wrong = $_POST['level1wrong'];
$level2right = $_POST['level2right'];
$level2wrong = $_POST['level2wrong'];
$level3right = $_POST['level3right'];
$level3wrong = $_POST['level3wrong'];
$level4right = $_POST['level4right'];
$level4wrong = $_POST['level4wrong'];

// check for email and set hash variable
$stm = $dbh->prepare("SELECT * FROM $tname WHERE email=?");
$stm->bindValue(1, $email, PDO::PARAM_STR);
$stm->execute();

while ($row = $stm->fetch(PDO::FETCH_ASSOC)) {
    $hashes = array($row['hash']);
    $hash = $row['hash'];
    $id = $row['id'];
    foreach ($hashes as $hash) {
        // If hash matches password, then...

        if (password_verify($password, $hash)) {
            // Everything is cool -- Insert the data into the database (update)

            $stmt = $dbh->prepare("
UPDATE $tname 
   SET firstname = :firstname
     , lastname = :lastname
     ,  hash = :hash
     , level1right = :level1right
     , level1wrong = :level1wrong
     , level2right = :level2right
     , level2wrong = :level2wrong
     , level3right = :level3right
     , level3wrong = :level3wrong
     , level4right = :level4right
     , level4wrong = :level4wrong 
 WHERE email = :email 
   AND id = :id");
            $stmt->execute(array($firstname, $lastname, $hash, $level1right, $level1wrong, $level2right, $level2wrong, $level3right, $level3wrong, $level4right, $level4wrong, $email, $id));
            $affected_rows = $stmt->rowCount();

            // check if row inserted

            /* Return number of rows that were updated */
            $count = $stmt->rowCount();
            echo "$count";
        }
    }
}

The student inputted her name but someone else's name got inserted. I am totally baffled by this. Does anyone have any idea how this could occur? The person whose name was inserted in place of my student's added data at 12:30:44 today and my student added her data at 13:44:15. How did this data get mixed?

I'm not certain why you had your update wrapped in multiple loops, but it's entirely possible that users with the same password hash could exist, and (I think) would explain the behaviour you're seeing.

You are, presumably, looking to update the single user with the email and password submitted in the form? I assume you also have constraints on your table to ensure that email addresses are unique. So, you're grabbing the single user that matches that email, and checking their password. If it matches, update the single record with the same database ID. No loops!

// get password hash
$stm = $dbh->prepare("SELECT id, hash FROM $tname WHERE email=?");
$stm->execute([$_POST["email"]]);

$row  = $stm->fetch(PDO::FETCH_ASSOC);
$hash = $row['hash'];
$id   = $row['id'];
if (!password_verify($_POST["password"], $hash)) {
    // verification failed, do something to present an error to the user
    die();
}
$stmt = $dbh->prepare(
    "UPDATE $tname
        SET firstname=:firstname, lastname=:lastname,
            level1right=:level1right, level1wrong=:level1wrong,
            level2right=:level2right, level2wrong=:level2wrong,
            level3right=:level3right, level3wrong=:level3wrong,
            level4right=:level4right, level4wrong=:level4wrong
        WHERE id=:id"
);
$stmt->execute([
    ":firstname"   => $_POST["firstname"],
    ":lastname"    => $_POST["lastname"],
    ":level1right" => $_POST["level1right"],
    ":level1wrong" => $_POST["level1wrong"],
    ":level2right" => $_POST["level2right"],
    ":level2wrong" => $_POST["level2wrong"],
    ":level3right" => $_POST["level3right"],
    ":level3wrong" => $_POST["level3wrong"],
    ":level4right" => $_POST["level4right"],
    ":level4wrong" => $_POST["level4wrong"],
    ":id"          => $id
]);
$count = $stmt->rowCount();
echo "$count";

Also note that using named parameters in PDO requires the use of an associative array. Not sure how your original code would update anything at all without that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM