I'm trying to create email verification but for some reason, I can't update my MySQL table. I've tried various different ways of doing it but I can't seem to get it to update.
<?php
$connect = mysqli_connect("x", "x", "x", "x");
if(isset($_GET['email']) && !empty($_GET['email']) AND isset($_GET['hash']) && !empty($_GET['hash'])){
// Verify data
$email = mysqli_escape_string($_GET['email']); // Set email variable
$hash = mysqli_escape_string($_GET['hash']); // Set hash variable
$statement = mysqli_prepare($connect, "SELECT * FROM users WHERE email = ? AND hash = ?");
mysqli_stmt_bind_param($statement, "ss", $email, $hash);
mysqli_stmt_execute($statement);
mysqli_stmt_store_result($statement);
$count = mysqli_stmt_num_rows($statement);
mysqli_stmt_close($statement);
if($count < 1){
// We have a match, activate the account
$statement = mysqli_prepare($connect, "UPDATE users SET active ='1' WHERE email=? AND hash=?");
mysqli_stmt_bind_param($statement, "ss",$email, $hash);
mysqli_stmt_execute($statement);
mysqli_stmt_store_result($statement);
$count = mysqli_stmt_num_rows($statement);
mysqli_stmt_close($statement);
echo '<div class="statusmsg">Your account has been activated, you can now login</div>';
}
else{
// No match -> invalid url or account has already been activated.
echo '<div class="statusmsg">The url is either invalid or you already have activated your account.</div>';
}
}
else{
// Invalid approach
echo '<div class="statusmsg">Invalid approach, please use the link that has been send to your email.</div>';
}
?>
<!-- stop PHP Code -->
One of the probable reason of rows not getting updated because, you are checking of number of rows less than 1, which would be false, since you are expecting some rows from SELECT statement.
if($count < 1){
instead, it should be
if($count == 1){
In your IF challenge you are looking if there is NO entry in the db with that hash and rmail. But there should be one that is not yet active. So: There should be exactly 1 entry in the db.
if($count == 1){
That should do the trick.
You should further also look for
AND active=0
In your first SQL statement to avoid 2 activations or remove the hash from your db once the uset is activated.
Related
Before I ask the question, I know there are issues with the SQL Injection, I am going to take care of it most likely with PDO, I am just trying to figure this out for now.
I want PHP to read the status column from MySql and if the status column reads -1 where $username = ($_POST["username"]) I want PHP to initially send them to a "change password" screen and then after its changed send an update script to MySql to update column "status" from the default -1 to 1.
If its 1 I want it to log in as normal and if its -1 I want them to be forced to change their password basically and I am having trouble locating a way to do this.
Im assuming my update query would look something like this
$query = "UPDATE login SET status= 1 WHERE user_id='".$username ."' LIMIT 1";
My php login script so far
<?php
// Include config file
require_once 'LoginConn.php';
// Define variables and initialize with empty values
$username = $password = "";
$username_err = $password_err = "";
// Processing form data when form is submitted
if($_SERVER["REQUEST_METHOD"] == "POST"){
// Check if username is empty
if(empty(trim($_POST["username"]))){
$username_err = 'Please enter username.';
} else{
$username = trim($_POST["username"]);
}
// Check if password is empty
if(empty(trim($_POST['password']))){
$password_err = 'Please enter your password.';
} else{
$password = trim($_POST['password']);
}
// Validate credentials
if(empty($username_err) && empty($password_err)){
// Prepare a select statement
$sql = "SELECT username, password FROM scorecardusers WHERE username = ?";
if($stmt = mysqli_prepare($link, $sql)){
// Bind variables to the prepared statement as parameters
mysqli_stmt_bind_param($stmt, "s", $param_username);
// Set parameters
$param_username = $username;
// Attempt to execute the prepared statement
if(mysqli_stmt_execute($stmt)){
// Store result
mysqli_stmt_store_result($stmt);
// Check if username exists, if yes then verify password
if(mysqli_stmt_num_rows($stmt) == 1){
// Bind result variables
mysqli_stmt_bind_result($stmt, $username, $hashed_password);
if(mysqli_stmt_fetch($stmt)){
if(password_verify($password, $hashed_password)){
/* Password is correct, so start a new session and
save the username to the session */
session_start();
$value= $sql['password'];
$_SESSION['username'] = $username;
header("location: Test.php");
} else{
// Display an error message if password is not valid
$password_err = 'The password you entered was not valid.';
}
}
} else{
// Display an error message if username doesn't exist
$username_err = 'No account found with that username.';
}
} else{
echo "Oops! Something went wrong. Please try again later.";
}
}
// Close statement
mysqli_stmt_close($stmt);
}
// Close connection
mysqli_close($link);
}
?>
UPDATED: I added this to my query and it updates correctly.
mysqli_query ($link, "UPDATE Users SET Status = '1' WHERE Status ='-1'and username ='".$username ."' LIMIT 1");
If anyone could help me understand how to pull the status area from my sql and redirect them to the change password page if the status =-1 that would be awesome.
I am just trying to figure out how to redirect to change password if Status =-1 in mysql..
SECOND UPDATE: Ive made a second query that checks the information and sends you to the change password log however I know this is not correct. I feel as if I am getting a lot closer to the answer though. The code works as far as getting me to the right screen but its not getting the correct information. I need the username to be a variable based on what the agent puts in. Again, any help here would be really appreciated.
$id_get = "SELECT Status, username FROM scorecardusers WHERE Status = '-1' and username='MyName' LIMIT 1";
if ($result=mysqli_query($link, $id_get))
{
// Fetch one and one row// check if first time log in
while ($row = mysqli_fetch_assoc($result))
{
$Status=$row["Status"];
$username=$row["MyName"];
if ($Status== "-1" && $username =="MyName");
var_dump($result);
header("location: ChangePW.php");
}
// Free result set
mysqli_free_result($result);
}
I added the update query I posted above after the following code. This has solved my problem and accurately check if the user was logged in before, if they have the website directs them to a change password, if they have logged in it send them to the website. I created a separate query to select status and username so it would not interfere with the password. I am assuming this would work with any user name considering it is completely variable as long as they never logged in before it should direct them. If there is any issues with this code id love to know, however it is working correctly for me.
$id_get = "SELECT Status, username FROM Users WHERE Status = '-1' and username = ?";
if($Check = mysqli_prepare($link, $id_get)){
// Bind variables to the prepared statement as parameters
mysqli_stmt_bind_param($Check, "s", $param2_username);
// Set parameters
$param2_username = $username;
$param_status=$status;
// Attempt to execute the prepared statement
if(mysqli_stmt_execute($Check)){
// Store result
mysqli_stmt_store_result($Check);
if(mysqli_stmt_num_rows($Check) == 1){
// Bind result variables
mysqli_stmt_bind_result($Check, $username, $param2_status);
if(mysqli_stmt_fetch($Check)){
if ($param_status== "-1" && $param2_username == '?');
header("location: ChangePassword.php");
}
}
}
}
I've done a login and registration for my site and it works fine.
Now I just want to make a simple profile page where the user can see all their details.
I'm only able to get back the username, so I'm unsure how to get the rest of their details.
Here is the code for registering and logging in:
function selectUser($conn, $username, $password)
{
$query = "SELECT password FROM login WHERE username = :username";
$stmt = $conn->prepare($query);
$stmt->bindValue(':username', $username);
$stmt->execute();
if ($row = $stmt->fetch(PDO::FETCH_OBJ))
{
if (md5($password) == $row->password)
{
$_SESSION['username'] = $username;
//$_SESSION['password'] = $password; DO NOT DO THIS
echo "Welcome, you are now logged in as " . $username;
return true;
}
return false;
} else {
//echo "Your details were not found";
return false;
}
}
function selectNew($conn, $name, $username, $password, $contact, $occupation, $role, $picture)
{
$query = "INSERT INTO login VALUES (NULL, :name, :username, :password, :contactNumber, :occupation, :role, :pic)";
$stmt = $conn->prepare($query);
$stmt->bindValue(':name', $name);
$stmt->bindValue(':username', $username);
$stmt->bindValue(':password', $password);
$stmt->bindValue(':contactNumber', $contact);
$stmt->bindValue(':occupation', $occupation);
$stmt->bindValue(':role', $role);
$stmt->bindValue(':pic', $picture);
$affected_rows = $stmt->execute();
if ($affected_rows == 1)
{
return true;
} else {
return false;
}
}
Don't worry, the password has been hashed.
heres what I've tried:
function selectUser($conn, $username, $password)
{
$query = "SELECT * FROM login WHERE username = :username";
$stmt = $conn->prepare($query);
$stmt->bindValue(':username', $username);
$stmt->execute();
$row = $stmt->fetch();
echo $row['occupation'];
echo $row['role'];
}
2nd attempt:
if(isset($_SESSION["username"]))
{
echo "Welcome, you are now logged in as <b>".$_SESSION['username']."</b> <img class='clientView' src='images/loginIcon.png' alt='client'>"; }
else {
echo "You are currently not logged in";
}
$user = $_SESSION["username"];
$query = "SELECT * FROM login WHERE username = :username";
$term = $conn->prepare($query);
$term->bindValue(':username', $user);
$term->execute();
if ($username = $term->fetch(PDO::FETCH_OBJ))
{
echo "<li><h3>" . $user->username ." ". $user->user_ID . "</h3></li>";
}
The simple answer is to replace your query in selectUser(...) with SELECT * FROM login WHERE username = :username. Note the * after the SELECT command, which functions as a wild card and thus asks for every single column of each row it finds (instead of just the password column as you are currently asking for.
You could then, as you iterate over the returned rows, access other columns of the user via your $row variable. Just like you access the user's hashed password with $row->password, you could access $row->contactNumber.
A note about good practice:
Depending on the case, I would not recommend doing a wildcard (*) SELECT command at login. In fact, I would recommend simply hashing the password prior to the query and attempting to then qualify your query with WHERE username = :username AND password = :password (obviously, bind the hashed password to :password). Instead of asking for the password column, or wildcard columns, you could SELECT the row's unique ID.
This way, you don't even need to iterate over the returned rows at all...you only have to make sure any row returned (see num_rows) to see if the user can be successfully "logged in". You can then cache the returned row's unique ID into your session, and then do subsequent queries as necessary for other pieces of information...such as the user's role or contact number. This effectively brings the complexity of all of your query processing down from linear time to constant time...a minor, but still non-trivial, improvement for an application.
Also, as a word of warning, this login system is very simple and easily spoofed. PHP sessions provide some security, but they are not full-proof. A sniffer snagging the session cookie will allow them to log in as the user whom they sniffed it from. I would recommend looking into adding in your own session layer as well once you have the rest of your login system implemented. And absolutely use SSL.
A note about optimization:
Using a wildcard (*) in a SELECT command is actually a prime place for a speed bottleneck to occur. If you know exactly what columns you want from the database, it is best to ask for them explicitly. For example, instead of *, you could do password,occupation,role.
$_SESSION['username'] = $username;
You have user name in session right.
Just pass this value in where condition of mysql .and get the entire record from login table .just show where ever you want to show.
You can change your SELECT statement to return the other values you want to store in your $_SESSION variables and then access them each with $row->{variable}
Just make sure you populate the $_SESSION after you do your password check
Not dissimilar to your existing code - use the session variable you set when the user logs in
$username=!empty( $_SESSION['username'] ) ? $_SESSION['username'] : false;
$sql='select * from `login` where `username`=:username;';
$stmt=$conn->prepare( $sql );
if( $stmt && $username ){
$stmt->bindValue(':username',$username);
$result=$stmt->execute();
if( $result ){
$rs=$stmt->fetchAll( PDO::FETCH_OBJ );
/* display results from $rs */
/*
There should most likely only be one record!!
*/
$row=$rs[0];
foreach( $row as $column => $value )echo $column.' '.$value;
}
}
I have a file register.php for my site.
$username=filter_var($_POST['username'], FILTER_SANITIZE_STRING);
$sql="SELECT username FROM users WHERE username=?";
$stmt=$con->prepare($sql);
$stmt->bind_param('s', $username);
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
while ($row = $result->fetch_object()) {
if(empty($row->username)) {
echo "different username. IS OK !!";
} else {
echo "<font color='red'>*</font>".$username."<font color='red'> : this username already exist in DB!!</font><br />";
}
}
Here I compare the ($username from form) with ($row->username from DB)
$row->username exist only if $username is already register. So, if this exist, will go on ELSE part.
If $row->username doesn't exist (is empty) , because SELECT from sql will find no attribute, my code must go on IF part and display this message "different username. IS OK !!"
Why IF part doesn't work? I know sql will return 0, or NULL or "" or something like this. If I put a username which is not already registered, the code don't show the specific message. Because in this part I want to change the message with the code which permit to register new username.
empty($row->username) is not really empty if SQL can't find something to return?
Another problem is : I have a registered username "Test". MySQL is not case-sensitive, but in php, I can register another username "test". The code in php will run as a new register, but in MySQL nothing will happening because "username is unique" there. So, in "check username" from above, I need to check 2 insensitive variables ($username and $row->username). Because if in DB I have Test, and I want to register new user "test", this code will let me to do it. I want to show a error message if this happening.
All you need is to verify if no rows are returned.
$username=filter_var($_POST['username'], FILTER_SANITIZE_STRING);
$sql="SELECT username FROM users WHERE username=?";
$stmt=$con->prepare($sql);
$stmt->bind_param('s', $username);
$stmt->execute();
$count = $stmt->num_rows;
if ($count == 0) {
// Username not in table
} else {
// Username already exists
]
To be sure that all your usernames are lowercase, simply convert it to lowercase before inserting it.
$username = strtolower($username);
i think you should covert your database and php variable into same case then compare it for checking.
$username=filter_var($_POST['username'], FILTER_SANITIZE_STRING);
$sql="SELECT LCASE(username) AS username FROM users WHERE username=?";
$stmt=$con->prepare($sql);
$stmt->bind_param('s', strtolower($username)); // used strtolower();
$stmt->execute();
Try this code, hope this helps...
thanks for fast answers. Honestlly, I don't know where was the issue, because now it's run perfect, without strtolower method. This is my code now:
$sql="SELECT username FROM users WHERE username=?";
$stmt=$con->prepare($sql);
$stmt->bind_param('s', $username);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_object();
$stmt->close();
if(empty($row->username)) {
// if a cell from below is empty, go on ELSE part
if (!empty($username) && !empty($password) && !empty($email) && !empty($country)) {
$sql="INSERT INTO users (username, password, email, borned, gender, country, phone, register_date) VALUES (?,?,?,?,?,?,?, now())";
$stmt = $con->prepare($sql);
$stmt->bind_param('sssssss', $username, $password, $email, $borned, $gender, $country, $phone);
$stmt->execute();
$stmt->close();
header ("Location: login.php");
} else {
echo "<font color='red'>*Must complete required cells !!</font> ";
}
// end of register dates
} else {
echo "<font color='red'>*</font>".$username."<font color='red'> : this username already exist in DB!!</font><br />";
}
I have user "Test", if try to register "test" will give a message that the user is already registered.
Thanks again, the thread can be deleted is somebody consider that can not help somebody :D
Here is mysql_* code:
Activation mail and hash check
PDO:
Do anyone sees the solution ?
if (isset($_GET['email']) && !empty($_GET['email']) AND isset($_GET['hash']) && !empty($_GET['hash'])){
// Verify data
$search = $db->prepare("SELECT email, hash, active FROM users WHERE email=:email AND hash=:hash AND active=0");
$search->bindParam(':email', $_POST['email'], PDO::PARAM_STR);
$search->bindParam(':hash', $_POST['hash'], PDO::PARAM_STR);
$search->execute();
//$match = $search->fetch(PDO::FETCH_ASSOC);
$match = $search->rowCount();
There is a problem in this part of condition
if($match > 0){
// We have a match, activate the account
$db->prepare("UPDATE users SET active= 1 WHERE email=:email AND hash=:hash AND active=0");
$db->bindParam(':email', $_POST['email'], PDO::PARAM_STR);
$db->bindParam(':hash', $_POST['hash'], PDO::PARAM_STR);
$db->execute();
echo '<div class="statusmsg">Your account has been activated, you can now login</div>';
}else{
// No match -> invalid url or account has already been activated.
echo '<div class="statusmsg">The url is either invalid or you already have activated your account.</div>';
}
}else{
// Invalid approach
echo '<div class="statusmsg">Invalid approach, please use the link that has been send to your email.</div>';
}
The condition finishes the code here:
The url is either invalid or you already have activated your account.
But it should finish the code here:
Your account has been activated, you can now login.
You check $_GET but later using $_POST.
You have few mistakes in your code,
First the $_POST that you are using here does not exists.
$search->bindParam(':email', $_POST['email'], PDO::PARAM_STR);
$search->bindParam(':hash', $_POST['hash'], PDO::PARAM_STR);
You should be using $_GET
rowCount returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement executed by the corresponding PDOStatement object.
Thefore rowcount, is not reliable on select,
below is working code that you can use to achieve what you looking for.
if (isset($_GET['email']) && !empty($_GET['email']) AND isset($_GET['hash']) && !empty($_GET['hash'])){
$email = $_GET['email'];
$hash = $_GET['hash'];
$search = $db->prepare("SELECT email, hash, active FROM users WHERE email=:email AND hash=:hash AND active=0");
$search->bindParam(':email', $email, PDO::PARAM_STR);
$search->bindParam(':hash', $hash, PDO::PARAM_STR);
$search->execute();
$match = $search->fetchall(PDO::FETCH_ASSOC);
if(count($match) > 0){
//then match exists activate the profile
$stmt = $db->prepare("UPDATE users SET active= 1 WHERE email= ? AND hash= ? AND active=0")->execute(array($email,$hash));
if(!$stmt){
print_r($db->errorInfo());
}else{
//account activated
echo '<div class="statusmsg">Your account has been activated, you can now login</div>';
}
}else{
// No match -> invalid url or account has already been activated.
echo '<div class="statusmsg">The url is either invalid or you already have activated your account.</div>';
}
}else{
// Invalid approach
echo '<div class="statusmsg">Invalid approach, please use the link that has been send to your email.</div>';
}
The problem in the flow is bound to this line of code:
$match = $search->rowCount();
You're executing a SELECT query and the rowCount is only available on INSERT, DELETE or UPDATE queries.
Instead, to find out if there is at leas match, you can use fetch() to get the "first" row, and if it exists, there was a match:
...
$match = $search->fetch();
if ($match) {
...
As another alternative approach to prevent that line of code you can in theory as well remove the first SELECT query in full and execute the second UPDATE query first and then check with the rowCount whether or not the user was updated.
There is more left open to comment with your code, like the logic you validate the hashes as they have no time-limit and the generation of the hash is even unknown but often critical.
Reference:
Row count with PDO
I have written a script in php to reset user's password, and how do I check if password is updated in a table?
For example, if a data in the tuple/column has been changed, then send email. Please check comments in the script.
$dbcc = mysqli_connect(HOST,NAME,PASSWORD,DATABASE) or die('Error can not connect to database');
$query = "SELECT uid,email FROM `corporate` WHERE (email='$chk_email')";
$result = mysqli_query($dbc, $query);
//found
if(#mysqli_num_rows($result) == 1)
{
$ROW = mysqli_fetch_array($result);
$sent_email = $ROW['email']; //get email
$id = $ROW['uid']; //get uid
$new_password = generatePassword(8);//generates 8 char long random password
$enc_password = md5($new_password); //encrypt
$statement = "UPDATE corpoorate SET password=".$enc_password." WHERE uid ='$id'";
$go = mysqli_query($dbcc,$statement) or die(mysqli_error());
mysqli_close($dbcc);
/*
* HOW DO I CHECK IF PASSWORD IS UPDATED IN THE DATABASE?
* IF IT IS, SEND EMAIL
* IF $go==true does not work!
**/
if($go==true){
$sendmessage = "We have generated a new password token for you.\n Your password is reset to ".$new_password." \n Please note that this password is not secure. Once you login, please reset your password.\n ";
mail($sent_email,'Password Reset',$sendmessage,'From: address#gmail.com');
}
header("Location : http://limozoor.com/login/signin.php");
exit();
}//if
mysqli_close($dbcc);
Why don't you use mysqli_affected_rows?
// remove: $go = mysqli_query($dbcc,$statement) or die(mysqli_error());
$qry =# mysqli_query($dbcc, $statement);
$aff =# mysqli_affected_rows($dbcc);
if ($qry === true && $aff > 0) {
mail(...);
}
From manual;
mysqli_query:
Returns FALSE on failure. For successful SELECT, SHOW, DESCRIBE or EXPLAIN queries mysqli_query() will return a mysqli_result object. For other successful queries mysqli_query() will return TRUE.
mysqli_affected_rows:
An integer greater than zero indicates the number of rows affected or retrieved. Zero indicates that no records where updated for an UPDATE statement, no rows matched the WHERE clause in the query or that no query has yet been executed. -1 indicates that the query returned an error.
http://php.net/manual/en/mysqli.affected-rows.php
http://php.net/manual/en/mysqli.query.php
Because of your or die(mysqli_error());-condition the password will always be updated in the table if it reaches those lines of execution.
However, I am sceptic towards your if(#mysqli_num_rows($resultt) == 1) because if there is any error in your first SQL-query, you are supressing all error messages there (by using #), which makes me think that you never even try to execute the UPDATE statements.