I've created a register and email verification system on my website. The process is as follows:
- User submits email and password
- Verification link is emailed
- User info is swapped from unverified to verified DB if link is visited
However, I've noticed an interesting "bug" if you'd even call it that.
If I simply open the email in Hotmail without even visiting the link, somehow the user info is swapped from unverified to verified as if the link was clicked.
I'm baffled...
How is this so? Why would this be occurring?
You can try it for yourself by visiting http://www.pillar.fyi/redflagreviews/signinup.php/
By default, the slider is in the "register" position, whereas the other position is for logging in.
PHP (it's messy, forgive me)
<?php
session_start();
session_regenerate_id();
if ($_SESSION["session"])
{
echo "<script type='text/javascript'> location.href = 'http://www.pillar.fyi/redflagreviews/index.php' </script>";
}
else
{
if (!empty($_SERVER["QUERY_STRING"])) # ... && regex to filter out junk
{
parse_str($_SERVER["QUERY_STRING"]);
include "include/connect.php";
# compare token
$statement = $connect->prepare("SELECT entry_time, account_email, account_password, token FROM users_unverified WHERE account_email = :account_email");
$statement->bindParam(":account_email", $email);
$statement->execute();
$result = $statement->fetch(PDO::FETCH_ASSOC);
if ($result["token"]
&& $result["token"] === $token
&& $result["entry_time"] > time() - 600) # token matches
{
# move info to users_verified
$statement = $connect->prepare("INSERT INTO users_verified (account_email, account_password, joined) VALUES (:account_email, :account_password, :joined)");
$statement->bindParam(":account_email", $result["account_email"]);
$statement->bindParam(":account_password", $result["account_password"]);
$statement->execute(array(
":account_email" => $result["account_email"],
":account_password" => $result["account_password"],
":joined" => date("Y-m-d")
));
# delete old entry
$statement = $connect->prepare("DELETE FROM users_unverified WHERE account_email = :account_email");
$statement->bindParam(":account_email", $email);
$statement->execute();
# now redirect to login screen (or update message)
echo "Your email address has been verified and is now active. Sign in below to begin sharing!";
$connection = null; # may be useless
}
else # being hacked or token has expired
{
if ($result["token"]) { echo 'true'.'<br><br>'; }
else { echo '1. false -- $result["token"] -- '.$result["token"].'<br><br>'; }
if ($result["token"] === $token) { echo 'true'.'<br><br>'; }
else { '2. false -- $result["token"] === $token -- ' . $result["token"]. ' === ' .$token.'<br><br>'; }
if ($result["entry_time"] > time() - 600) { echo 'true'.'<br><br>'; }
else { '3. false -- $result["entry_time"] > time() - 600 -- '. $result["entry_time"] . ' > ' . time() - 600 .'<br><br>'; }
echo "Your verification code has expired. Would you like us to send you a new verification link?";
# if yes, then... (add num_id? to SELECT query above)
#####################################################
#####################################################
}
}
else # might cause issues as ELSE
{
if ($_SERVER["QUERY_STRING"]) echo $_SERVER["QUERY_STRING"];
else echo 'query string is false<br><br>';
if ($_SERVER["REQUEST_METHOD"] === "POST")
{
include "include/connect.php";
if ($_POST["action"] === "0") # register
{
######################################
### ###
### prohibit certain domains ###
### such as spamgourmet.org ###
### ###
######################################
$statement = $connect->prepare("SELECT account_email FROM users_verified WHERE account_email = :account_email");
$statement->bindParam(":account_email", $_POST["email"]);
$statement->execute();
$result = $statement->fetch(PDO::FETCH_ASSOC);
if ($result["account_email"]) # email already in use
{
echo "The email account " . $result['account_email'] . " has been registered already.";
}
else # new account, unknown email address
{
# first, check users_unverified to see if verification process is active
$statement = $connect->prepare("SELECT entry_time FROM users_unverified WHERE account_email = :account_email");
$statement->bindParam(":account_email", $_POST["email"]);
$statement->execute();
$result = $statement->fetch(PDO::FETCH_ASSOC);
if ($result["entry_time"]
&& $result["entry_time"] > time() - 600) # verification process is already active
{
echo "Your account is awaiting verification. The verification code will remain active for another "."###"." Would you like us to resend the verification code?";
}
else # initiate the verification process
{
# delete old entry
$statement = $connect->prepare("DELETE FROM users_unverified WHERE account_email = :account_email");
$statement->bindParam(":account_email", $_POST["email"]);
$statement->execute();
# send verification code
echo "An email with a verification link has been sent to your email address. Please verify your ownership of the email account by clicking the link in that email. The verification code will expire in 10 minutes!";
$password = password_hash($_POST["password"], PASSWORD_DEFAULT);
$token = md5($password, FALSE);
$statement = $connect->prepare("INSERT INTO users_unverified (entry_time, account_email, account_password, token) VALUES (:entry_time, :account_email, :account_password, :token)");
$statement->execute(array(
":entry_time" => time(),
":account_email" => $_POST["email"],
":account_password" => $password,
":token" => $token
));
# send verification email
include "include/sanitize.php";
$email = sanitize($_POST["email"]);
mail($_POST["email"],
"Account Verification",
"Please click the following link to activate your account: http://www.pillar.fyi/redflagreviews/signinup.php/?email=".$email."&token=".$token,
"From: aj@pillar.fyi",
"-f aj@pillar.fyi");
}
}
$connect = null;
}
else # sign in
{
$statement = $connect->prepare("SELECT account_password FROM users_verified WHERE account_email = :account_email");
$statement->bindParam(":account_email", $_POST["email"]);
$statement->execute();
$result = $statement->fetch(PDO::FETCH_ASSOC);
if ($result["account_password"]
&& password_verify($_POST["password"], $result["account_password"])) # successfully logged in, redirect to main page
{
$connect = null;
$_SESSION["session"] = $_POST["email"];
echo "<script type='text/javascript'> location.href = 'http://www.pillar.fyi/redflagreviews/index.php' </script>";
}
else if ($result["account_password"]) # failed login
{
echo "That is the wrong password.";
$idPersist = $_POST["email"]; # maybe sanitize?
}
else {
echo "That account does not exist.";
}
}
}
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>‎</title>
<meta charset="UTF-8">
<!--link href="css/index.css" rel="stylesheet"-->
</head>
<body>
<form accept-charset="UTF-8" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" enctype="application/x-www-form-urlencoded" method="POST">
<input type="range" name="action"
min="0" max="1" step="1" value="0" id="action"
> <!-- use PHP variable to set as register or sign in -->
<input type="text" name="email" placeholder="email" value="<?php echo $idPersist; ?>" <?php if (!$idPersist) echo "autofocus"; ?> required>
<input type="password" name="password" placeholder="password" <?php if ($idPersist) echo "autofocus"; ?> required autocomplete>
<input type="submit" value="➥">
</form>
<!--script type="text/javascript" src="js/index.js"></script-->
</body>
</html>