I´m currently creating a login script in PHP. When a user tries to login <5 times in a short period of time the account get´s temporary locked and the user get´s an email in order to reactivate the account instantly. This is all working fine, the user fails to login, the database is updated and the email is sent. Then when I try to do this very same thing a second time with the second or third email, the database simply doesn't update. The only way to get it to work for the next email is to remove all values created from the first email in the database.
Here is the relevant code for the login php file.
<?php
include_once 'db_connect.php';
include_once 'functions.php';
...
} elseif (login($email, $password, $mysqli) == 2)
$theid = getId($email, $mysqli); /// this is a function that get´s id based on email located in functions.php
if ($stmt = $mysqli->prepare("SELECT emailSent FROM login_attempts WHERE user_id = ? LIMIT 1"))
{
$stmt->bind_param('i', $theid);
$stmt->execute();
$stmt->bind_result($skickat);
$stmt->fetch();
$stmt->close();
if ($skickat == '0') /// email sent
{
$thehash = md5( rand(0,10000) ); /// Just a random md5 hash for reactivation
$emailsent = 1;
if ($insert_stmt = $mysqli->prepare("UPDATE `secure_login`.`login_attempts` SET `ResetHash` = ?, `emailSent` = ? WHERE `login_attempts`.`user_id` = ?")) {
$insert_stmt->bind_param('sss', $thehash, $emailsent, $theid);
if (! $insert_stmt->execute()) {
header('Location: ../error.php?err=Registration failure: INSERT');
exit();
}
}
else {
header('Location: ../error.php?err=Registration failure: INSERT');
exit();
}
else /// alread sent, we already sent email. Show error that decribe email is sent and it´s locked
{
$_SESSION['lgnerror']= '<p class="error">Error 2</p>';
header('Location: ../login.php');
exit();
}
/// Email code starts from here
$_SESSION['lgnerror']= '<p class="error">Error 1</p>';
Here is the getId function located in functions.php
function getId($email, $mysqli)
{
if ($stmt = $mysqli->prepare("SELECT id FROM members WHERE email = ? LIMIT 1"))
{
$stmt->bind_param('i', $email);
$stmt->execute();
$stmt->bind_result($resultat);
$stmt->fetch();
$stmt->close();
return $resultat;
}
else
{
// Could not create a prepared statement
header("Location: ../error.php?err=Database error: cannot prepare statement");
exit();
}
}
Why does the code only work for the first email and not when you try it a second time? I think the error lies somewhere in the code provided above, if needed just comment and I will provide more code.
The code on tests shows $_SESSION['lgnerror'] "error 2" on the 5th+ attempt when it schould show "error 1" on the 5th attemt. This indicates that $skickat != '0', even though the databse indicates that emailSent == '0' for the second emails id. I think the error probably is that that the user id is not updated so it just checks for the first email id which == '1'. I´m still a beginner at PHP and Mysqli so that might not be the error so please take a look and let me know what you think. What do you think is the error and how could I fix this so I could use this for more accounts than one, preferably with the security of prepared statements?
Im not sure but I think you got a mistake here.
In the function getId you do a bind_param of the email as an integer where it should be a string.
It should be like this:
function getId($email, $mysqli)
{
if ($stmt = $mysqli->prepare("SELECT id FROM members WHERE email = ? LIMIT 1"))
{
$stmt->bind_param('s', $email);
$stmt->execute();
$stmt->bind_result($resultat);
$stmt->fetch();
$stmt->close();
return $resultat;
}
else
{
// Could not create a prepared statement
header("Location: ../error.php?err=Database error: cannot prepare statement");
exit();
}
}
Related
I am new to PHP and still learning and I have come across this problem while writing a PHP page and I think there is an error at line 65, I am pretty sure the logic is fine. some mistake in the syntax I think, could you please point it out for me
The objective of this page is it will verify the entered email, checks whether present in DB farmers if present it will generate a token and insert the token in password_resets DB, then send email to the user with the token in a link.
<?php
// Initialize the session
session_start();
// Include config file
require_once "config.php";
// Define variables and initialize with empty values
$email = "";
$email_err = "";
// Processing form data when form is submitted
if($_SERVER["REQUEST_METHOD"] == "POST"){
// Validate new email
if(empty(trim($_POST["email"]))){
$email_err = "Please enter the email.";
} else{
$email = trim($_POST["email"]);
}
$sql = "SELECT email FROM farmers WHERE email = ?";
// Check input errors before updating the database
if($stmt = $mysqli->prepare($sql)){
// Bind variables to the prepared statement as parameters
$stmt->bind_param("s", $param_email);
// Set parameters
$param_email = $email;
// Attempt to execute the prepared statement
if($stmt->execute()){
// Store result
$stmt->store_result();
// Check if email exists
if($stmt->num_rows == 1){
// generate a unique random token of length 100
$token = bin2hex(random_bytes(50));
$sql = "INSERT INTO password_reset(email, token) VALUES (?, ?)";
// Bind result variables
if($stmt = $mysqli->prepare($sql)){
$stmt->bind_param("ss", $param_email, $param_token);
// Set parameters
$param_email = $email;
$param_token = $token;
if($stmt->execute()){
// Send email to user with the token in a link they can click on
$to = $email;
$subject = "Reset your password on site.in";
$msg = "Hi there, click on this link to reset your password on our site";
$msg = wordwrap($msg,70);
$headers = "From: info#site.in";
mail($to, $subject, $msg, $headers);
header('location: pending.php?email=' . $email);
}
else{
echo "Oops! Something went wrong. Please try again later.";
}
}
else{
echo "Oops! Something went wrong. Please try again later.";
}
}
else{
// Display an error message if email doesn't exist
$email_err = "No account found with that email.";
}
// Close statement
$stmt->close();
}
}
// Close connection
$mysqli->close();
}
?>
PHP Fatal error: Uncaught Error: Call to a member function close() on
boolean in /var/www/html/pwdrstmail.php:78\nStack trace:\n#0 {main}\n
thrown in /var/www/html/pwdrstmail.php on line 78, it is line
$stmt->close();
You are using the database object $stmt twice, for two database queries.
The first query is executing successfully, but the second is likely failing and returning the boolean false when you reassign it on the line: if($stmt = $mysqli->prepare($sql)){....
The error you are seeing (quoted above) is because you are calling $stmt->close() after this has happened, and at this point $stmt equals false, and is no longer a database object you can close.
For some reason my last two variables will not input into the database. I am referring to $verifyKey and $keyExpire. Here is my code with comments. I am adding the entire page to make sure I don't have the wrong character somewhere it isn't supposed to be. Effectively this is a registration page that inserts the information into the database and gives a verification key for email verification later.
I have the fields matched up with the code in the database and they are set to longtext and text. I don't want to insert directly as I am trying to get this method to work with all 5 variables.
<?php
// This is not seen by the end user so this file is placed in the unseen folder
// Check if the user used the sign up button
if (isset($_POST['signup-submit'])) {
// uses the database handler
require 'dbh.php';
$username=$_POST['uid'];
$email=$_POST['mail'];
$password=$_POST['pwd'];
$passwordcnfrm=$_POST['pwd-cnfrm'];
$verifyKey=md5(time().$username);
$keyExpire=date("U")+ 86400;
// Checks to see if any field are empty
if(empty($username)||empty($email)||empty($password)||empty($passwordcnfrm)) {
// This header returns the username and/or email address so the user doesn't have to retype it
header("Location:../signup.php?error=emptyfields&uid=".$username."&mail=".$email);
exit();
}
// Checks if both the user and email are invalid
else if (!filter_var($email, FILTER_VALIDATE_EMAIL)&&!preg_match("/^[a-zA-Z0-9]*$/",$username)) {
header("Location:../signup.php?error=invalidmailuid");
exit();
}
// Checks if the email is valid
else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
header("Location:../signup.php?error=invalidmail&uid=".$username);
exit();
}
// Checks if the username is valid.
else if (!preg_match("/^[a-zA-Z0-9]*$/",$username)) {
header("Location:../signup.php?error=invaliduid&mail=".$email);
exit();
}
// Checks to see if the password and confirm password match
else if ($password !== $passwordcnfrm){
header("Location:../signup.php?error=passwordcheck&uid=".$username."&mail=".$email);
exit();
}
// Checks to see if the username is already in use or password is invalid
else {
$sql = "SELECT uidUsers FROM users WHERE uidUsers=?";
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt,$sql)) {
header("Location:../signup.php?error=sqlerror");
exit();
}
else {
mysqli_stmt_bind_param($stmt,"s",$username);
mysqli_stmt_execute($stmt);
mysqli_stmt_store_result($stmt);
$resultCheck = mysqli_stmt_num_rows();
if ($resultCheck < 0){
header("Location:../signup.php?error=usertaken&mail=".$email);
exit();
}
else {
//Inserts into database
$sql = "INSERT INTO users (uidUsers,emailUsers,pwdUsers,verify,expires) VALUES (?,?,?,?,?);";
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt,$sql)) {
header("Location:../signup.php?error=sqlerror");
exit();
}
else {
$hashedPwd =password_hash($password,PASSWORD_DEFAULT);
mysqli_stmt_bind_param($stmt,"sssss",$username,$email,$hashedPwd,$verifyKey,$keyExpire);
mysqli_stmt_execute($stmt);
header("Location:../signup.php?signup=success");
}
}
}
}
//Closes session to db
mysqli_stmt_close($stmt);
mysqli_close($conn);
}
else {
header("Location:../signup.php");
}
In description of mysqli_stmt::bind_param there is this criptic note:
If data size of a variable exceeds max. allowed packet size
(max_allowed_packet), you have to specify b in types and use
mysqli_stmt_send_long_data() to send the data in packets.
http://php.net/manual/en/mysqli-stmt.bind-param.php
Practically it means that for every longtext (and blob) field you have to additionally call mysqli_stmt_send_long_data after mysqli_stmt_bind_param. Otherwise you have this strange behavious when everything works ok, but fields are not set.
I want to record the user ID from the current logged in user who enters data into the form which in turn is recorded to a database table
At present the insert query is running and updating all but the user id..the user id variable is definitely working as I am able to echo it out without any issues on the same page
Code is as follows;
$barcode = $_POST['barcode'];
$weight = $_POST['weight'];
$userId = $_SESSION['userId'];
//error handling begins
// check for any empty inputs.
if (empty($barcode) || empty($weight)) {
header("Location: ../record.php?error=emptyfields&barcode=".$barcode."&weight=".$weight);
exit();
}
//we check if valid barcode entered. In this case ONLY letters and numbers.
else if (!preg_match("/^[a-zA-Z0-9]*$/", $barcode)) {
header("Location: ../record.php?error=invalidbarcode&barcode=".$weight);
exit();
}
// check for an invalid weight. In this case ONLY numbers.
else if (!preg_match("/^[0-9].*$/", $weight)) {
header("Location: ../record.php?error=invalidweight&barcode=".$barcode);
exit();
}
else {
$sql = "INSERT INTO trimrecords (barcode, weight, createdby) VALUES (?,?,?);";
// initialize a new statement using the connection from the dbh.inc.php file.
$stmt = mysqli_stmt_init($conn);
// prepare SQL statement AND check if there are any errors with it.
if (!mysqli_stmt_prepare($stmt, $sql)) {
// If there is an error send the user back to the record page.
header("Location: ../record.php?error=sqlerror");
exit();
}
else {
// If there is no error continue the script!
// bind the type of parameters we expect to pass into the statement, and bind the data from the user.
mysqli_stmt_bind_param($stmt, "ssi", $barcode, $weight, $userId);
// execute the prepared statement and send it to the database!
// data is registered to Db at this stage
mysqli_stmt_execute($stmt);
// send back with success
header("Location: ../record.php?record=success");
exit();
}
}
Add session_start() to the top and all worked.
The goal is to create profile settings page, where users can change their email address, but before updating ask them to enter their password.
So, I need to fetch password from database and compare it to password entered by user, and if they match update email address.
But, when I try to implement logic, I get a blank page. Code work till query. It stop working when $passwordCheck = true and $resultCheck = 0 (see edited below).
Edited (2019.01.09): redirects work till header('Location: ../edituser.php?success=emailupdate'). When enter new email, correct password and click submit button, it shows changeemail.php (my action script) page instead of redirect to edituser.php with success messsage. I assume that something get broken and script stuck. Also, added complete script.
Edited (2019.01.10): my question is: how to implement password confirmation before updating data using PHP. Above description shows how I tried to implement it. I'm looking for information about solution, different approaches to this problem or some help with my code. Sorry if I not clarify my question detailed enough. I also add profile settings picture below.
Picture 1. Profile settings page
changeemail.php script:
// Check for submit
if (isset($_POST['submit'])) {
// Get form data
$update_id = $_POST['update_id'];
$email = test_input($_POST['email']);
$confirm_password = test_input($_POST['confirm_password']);
// Store post id in session
session_start();
$_SESSION['update_id'] = $update_id;
// Check for empty field
if (empty($email)) {
header('Location: ../edituser.php?error=emptyemail');
exit();
} else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { // Check email
header('Location: ../edituser.php?error=invalidemail');
exit();
} else if (empty($confirm_password)) {
header('Location: ../edituser.php?error=emptyconfirmpassword');
exit();
} else {
// Check if email already exist
$query = "SELECT email, password FROM users WHERE email = ?";
// Create prepared statement
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt, $query)) {
header('Location: ../edituser.php?error=sqlerror');
exit();
} else {
mysqli_stmt_bind_param($stmt, 's', $email);
mysqli_stmt_execute($stmt);
mysqli_stmt_store_result($stmt);
mysqli_stmt_bind_result($stmt, $email, $password);
$resultCheck = mysqli_stmt_num_rows($stmt);
while (mysqli_stmt_fetch($stmt)) {
// Check if passwords match
$passwordCheck = password_verify($confirm_password, $password);
if ($passwordCheck == false) {
header('Location: ../edituser.php?error=wrongconfirmpassword');
exit();
} else if ($passwordCheck == true && $resultCheck > 0) {
header('Location: ../edituser.php?error=emailtaken');
exit();
}
// Update email
$query = "UPDATE users SET email = ? WHERE id = ?";
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt, $query)) {
header('Location: ../edituser.php?error=sqlerror');
exit();
} else {
mysqli_stmt_bind_param($stmt, 'si', $email, $update_id);
mysqli_stmt_execute($stmt);
header('Location: ../edituser.php?success=emailupdate');
exit();
}
}
}
}
mysqli_stmt_close($stmt);
// Close connection
mysqli_close($conn);
} else {
header('Location: ../edituser.php');
exit();
}
As I said before - check your logic......
Sometimes that is best done in a 'reverse' manner - - - for instance, in this case, you say
When enter new email, correct password and click submit button, it shows changeemail.php (my action script) page instead of redirect to edituser.php with success messsage. I assume that something get broken and script stuck.
So, in other words....
EXPECTED RESULT: redirect to edituser.php
CURRENT RESULT: shows emailupdate (this is not clear in the question, though I believe that this is the case - you are redirecting to edituser.php?success=emailupdate. If this is not the case, clarify! My example below will presume this....)
So, let's follow the logic...... (remove all non-logic and/or redirection code to find why it is hitting where it is....)
First, let's look at why it isn't going where you want......
(EXPECTED RESULT: redirect to edituser.php)
Removing all code except what you expect, we get
// Check for submit
if (isset($_POST['submit'])) {
// DO ALL SORTS OF STUFF
} else {
// NEVER SELECT THIS IF THERE IS A 'SUBMIT' POSTED
header('Location: ../edituser.php');
exit();
}
However, to get to this page, you HAVE a 'submit', so the 'expected' line will NEVER be selected!
Now, let's look at what IS happening - and why......
// Check for submit
if (isset($_POST['submit'])) {
// Get form data
$update_id = $_POST['update_id'];
$email = test_input($_POST['email']);
$confirm_password = test_input($_POST['confirm_password']);
// Check for empty field
if (empty($email)) {
// WE HAVE EMAIL, SO THIS IS NOT HIT
} else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { // Check email
// EMAIL IS OK, SO NOT HIT
} else if (empty($confirm_password)) {
// PASSWORD IS OK, SO NOT HIT
} else {
// EMAIL FOUND SO WE ARE HERE
while (mysqli_stmt_fetch($stmt)) {
// Check if passwords match
$passwordCheck = password_verify($confirm_password, $password);
if ($passwordCheck == false) {
// PASSWORD IS OK, SO NO HIT
} else if ($passwordCheck == true && $resultCheck > 0) {
// OK, SO NO HIT
}
// Update email
$query = "UPDATE users SET email = ? WHERE id = ?";
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt, $query)) {
// NO FAILED SQL, SO NO HIT
} else {
// NO OTHER LOGIC, SO THIS _WILL_ BE A HIT........
// *****************************************
header('Location: ../edituser.php?success=emailupdate');
exit();
// *****************************************
}
}
}
}
}
Again, it is clear from your code that you will get the results you describe.
What I'm not sure of is really what you are expecting to do (so I can't really recommend code for you), though certainly the logic you are using is taking you where you are telling it to go - even though it is not what you would like to happen.....
You may need an additional check somewhere (to determine if you really need to update the email or just send them to the edituser page, etc.) - though you have to get the logic straight!
Hopefully this will show you a couple ways you can break down the code to get where you are wanting to go. I often START writing code with comments - then go back and fill in the logic with code. This both gives a simple way to follow along with the steps and gives well commented code for later debugging. I highly suggest you use such a system for your coding - certainly when starting out it is useful, but having well commented code will serve you well throughout your life.
To fix my issue I add another query:
$query = "SELECT password FROM users WHERE id = ?";
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt, $query)) {
header('Location: ../index.php?error=sqlerror');
exit();
} else {
mysqli_stmt_bind_param($stmt, "i", $update_id);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$row = mysqli_fetch_assoc($result);
Old query won't find password for updated email, because it's not exist yet, and will close connection.
Okay.. I am completely new to this PDO stuff.. I have tried to recreate my mysql script (working) to a PDO script (not working).. I have tested that my DB login informations is correctly programmed for PDO..
This is my PDO script...
<?
session_start();
//connect to DB
require_once("connect.php");
//get the posted values
$email=htmlspecialchars($_POST['email'],ENT_QUOTES);
$pass=md5($_POST['psw']);
//now validating the email and password
$sql - $conn_business->prepare( "SELECT email, password FROM members WHERE email='".$email."'");
$sql -> execute();
$count = $sql->rowCount();
$result = $sql -> fetch();
// Now use $result['rowname'];
$stmt = $conn_business->prepare("SELECT * FROM members WHERE email='".$email."'");
$stmt ->execute();
$act = $stmt -> fetch();
//if email exists
if($count > 0)
{
//compare the password
if(strcmp($result["password"],$pass)==0)
{
// check if activated
if($act["activated"] == "0")
{
echo "act"; //account is not activated yet
}
else
{
echo "yes"; //Logging in
//now set the session from here if needed
$_SESSION['email'] = $email;
}
}
else
echo "no"; //Passwords don't match
}
else
echo "no"; //Invalid Login
?>
And this is my old mysql script...
session_start();
require_once("connect.php");
//get the posted values
$email=htmlspecialchars($_POST['email'],ENT_QUOTES);
$pass=md5($_POST['psw']);
//now validating the username and password
$sql="SELECT email, password members WHERE email='".$email."'";
$result=mysql_query($sql);
$row=mysql_fetch_array($result);
$sql2="SELECT * FROM members WHERE email='".$email."'";
$result2=mysql_query($sql2);
$row2=mysql_fetch_array($result2);
$act = $row2['activated'];
//if username exists
if(mysql_num_rows($result)>0)
{
//compare the password
if(strcmp($row['password'],$pass)==0)
{
// check if activated
if($act == "0")
{
echo "act";
}
else
{
echo "yes";
//now set the session from here if needed
$_SESSION['email'] = $email;
}
}
else
echo "no";
}
else
echo "no"; //Invalid Login
Does anybody know, what I have done wrong? It is an automatically script.. It is called through AJAX and return data based on 'no', 'yes' and 'act' that tells the AJAX/jQuery script what to do.. As I said - the mysql script is working, so please if anyone could tell me what I have done wrong with the PDO script..
EDIT:
when it returns the data to the jQuery script, this should happen:
if yes: start session, redirect to page2.php with session started.
else if act: write in a field that the account is not activated.
else: write that email and password didn't match.
The thing is, that when I try to write the correct e-mail and password - it continues to write : "email and password didn't match" instead of redirecting.. When I say that it is not working it is because the mysql script does as described but the PDO script doesn't..
And I have tried to change the 'echo "no";' to 'echo "yes";' to see if the login would start anyway, but somehow it continues to write that the email and password didn't match..
SOLUTION:
I ahven't told this because I thought it was unnecessary, but the reason for it not to work was because of that i have had my old mysql code in comment marks on top of the page, so that the session_start command didn't work.. After deleting the old code it worked, but then I found something else to change, and that is in the PDO script when it is validating it says:
$sql - $conn_business->prepare( "SELECT email, password FROM members WHERE email='".$email."'");
and then I just changed the '-' after $sql to '=' and now, everything works perfectly... Anyhow thank you everybody.. hope this code can help others..
Did you even read the manual before you "started using" PDO?
That is not how prepared statements are supposed to be used! Your code is filled with SQL injections.
Why are you selecting same row twice ?
The strcmp() is not for checing if one string is identical to another.
And hashing passwords as simple MD5 is just a sick joke.
session_start();
//very stupid way to acquire connection
require_once("connect.php");
//get the posted values
$email = htmlspecialchars($_POST['email'],ENT_QUOTES);
if (filter_var( $email, FILTER_VALIDATE_EMAIL))
{
// posted value is not an email
}
// MD5 is not even remotely secure
$pass = md5($_POST['psw']);
$sql = 'SELECT email, password, activated FROM members WHERE email = :email';
$statement = $conn_business->prepare($sql);
$statement->bindParam(':email', $email, PDO::PARAM_STR);
$output = 'login error';
if ($statement->execute() && $row = $statement->fetch())
{
if ( $row['password'] === $pass )
{
// use account confirmed
if ( $row['activated'] !== 0 ) {
$output = 'not activated';
$_SESSION['email'] = $email;
}
$output = 'logged in';
}
}
echo $output;
i believe the second query in your scripts is not necessary you could simple do
SELECT * FROM members WHERE email=:EMAIL AND password=:PWS;
use bindParam method
$qCredentials->bindParam(":EMAIL",$EMAIL);
$qCredentials->bindParam(":PWS",$PWS);
then do more understable outputs rather than yes or no..
try "Unable to login: Invalid credentials supplied" for invalid types of values or "Unable to login: Invalid credentials, couldn't find user" for invalid user credentials.
You could try to start the session after the user has been successfully logged in your IF condition returning yes, and the methods
$PDOstatement->debugDumpParams()
$PDOstatement->errorInfo()
$PDOstatement->errorCode()
will help you understand what went wrong with a query!