I am trying to make my website protected from sql injections. So I decided to change my code and replace it with prepared statements. I think I made a minor misstake in the code below.
<?php
session_start();
$host= 'localhost';
$user='root';
$pass='';
$db='gameforum';
$conn= mysqli_connect($host, $user, $pass, $db);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$username = $_POST['username'];
$password = $_POST['password'];
$rpassword = $_POST['rpassword'];
$email = $_POST['email'];
if ($password!==$rpassword) {
$_SESSION['err']="Passwords did not match, please try again!";
header("Location: index.php");
$conn->close();
}
else {
$stmt = $conn->prepare("INSERT INTO users (username, password, rpassword, email) VALUES (?, ?, ?, ?)");
if(!$stmt){
echo "false";
}else {
$stmt->bind_param("ssss", $username, $password, $rpassword, $email);
if ($stmt->execute === TRUE) {
$redirectUrl = 'index.php';
$_SESSION['registrationsuccessful']="Your account was successfully created! You may now log in to your account.";
header("Location: index.php");
}else{
$_SESSION['alreadyexists']="Username or email already exists!";
header("Location: index.php");
$stmt->close();
$conn->close();
}
$stmt->close();
$conn->close();
}
}
The problem I am facing now is that I get the message "user already exists" when I try to create an account that do not actually exist. Thanks!
You have already executed the execute statement. Remove one of them. Alternatively check for success on only one of the execute statement
I believe the reason for the problem was the second usage of $stmt->execute() - but a few other modifications could be made to the code.
Create the db connection IF the initial logic if ( $password!==$rpassword ) test succeeds ~ seems pointless otherwise. I would use one session variable for this rather than 3 - it makes it easier to check the values later on other pages perhaps.
Assign the result of the first $stmt->execute() to a variable and use that vaiable in further logic tests if needed.
As for error messages - it is fine ( and indeed preferable ) to display verbose error messages for development but never in production - hence removed $conn->connect_error.
One other thing, mixing of procedural and object orientated code is probably not considered good practise - better to stick to one or other ( OO is easier I think )
<?php
session_start();
$username = $_POST['username'];
$password = $_POST['password'];
$rpassword = $_POST['rpassword'];
$email = $_POST['email'];
$_SESSION['registration_status']="";
if ( $password!==$rpassword ) {
$_SESSION['registration_status']="Passwords did not match, please try again!";
exit( header( "Location: index.php" ) );
} else {
$host= 'localhost';
$user='root';
$pass='';
$db='gameforum';
$conn= mysqli_connect($host, $user, $pass, $db);
if( $conn->connect_error ) die( "Connection failed" );
$stmt = $conn->prepare("INSERT INTO users (`username`, `password`, `rpassword`, `email`) VALUES (?, ?, ?, ?)");
if( $stmt ){
$stmt->bind_param("ssss", $username, $password, $rpassword, $email);
$result = $stmt->execute();
/* use the return value from stmt->execute() */
$_SESSION['registration_status'] = $result ? "Your account was successfully created! You may now log in to your account." : "Username or email already exists!";
$stmt->close();
}
$conn->close();
exit( header( "Location: index.php" ) );
}
}
?>
You can try this,
<?php
// if session not start, start now
!session_id() ? session_start() : null;
$mysql_server = "localhost";
$mysql_user = "root";
$mysql_password = "";
$mysql_db = "gameforum";
// connect db connection
$conn = new mysqli($mysql_server, $mysql_user, $mysql_password, $mysql_db);
// chck if connection has error
if ($conn->connect_errno) {
printf("Connection failed: %s \n", $conn->connect_error);
exit();
}
// db encoding
$conn->set_charset("utf8");
// when POST happen
if (isset($_POST) && !empty($_POST)) {
// convert POST array key as PHP variable
extract($_POST);
// if password matched with confirm password
if ($password === $rpassword) {
// create insert query with prepare
$stmt = $conn->prepare("INSERT INTO users (username, password, rpassword, email) VALUES (?, ?, ?, ?)");
// if prepare fine, there is no query or mysql error
if ($stmt) {
// bind real values
$stmt->bind_param("ssss", $username, $password, $rpassword, $email);
// if query executed
if ($stmt->execute()) {
// success message & redirect
$_SESSION['registrationsuccessful'] = "Your account was successfully created! You may now log in to your account.";
header("Location: index.php");
exit();
} else {
// query error & redirect
$_SESSION['alreadyexists'] = "There was an error or Username/Email already exists!";
header("Location: index.php");
exit();
}
}
} else {
// password matched failed
$_SESSION['err'] = "Passwords did not match, please try again!";
header("Location: index.php");
exit();
}
}
I am not closing connection because, PHP will close all open files and connections at the end of the script.
Related
I am a beginner to PHP. I tried not to put $conn->prepare($sql_stmt) in one variable and just applied method chaining. But I got "Error while executing".
<?php
include_once 'dbh.inc.php';
if(isset($_POST['submit_btn']))
{
$fullname = $_POST['name'];
$username = $_POST['username'];
$password = $_POST['password'];
$sql_stmt = "INSERT INTO signup (name, username, passwrd) VALUES (?,?,?);";
//prepare and bind
$conn->prepare($sql_stmt)->bind_param("sss", $fullname, $username, $password);
//execute
if($conn->prepare($sql_stmt)->execute())
{
echo "User created";
}
else
{
echo "Error while executing";
}
}
else
{
echo "Unable to sign up.";
}
However if I instantiate $sql = $conn->prepare($sql_stmt) like below
<?php
include_once 'dbh.inc.php';
if(isset($_POST['submit_btn']))
{
$fullname = $_POST['name'];
$username = $_POST['username'];
$password = $_POST['password'];
$sql_stmt = "INSERT INTO signup (name, username, passwrd) VALUES (?,?,?);";
//prepare and bind
$sql = $conn->prepare($sql_stmt);
$sql->bind_param("sss", $fullname, $username, $password);
//execute
if($sql->execute())
{
echo "User created";
}
else
{
echo "Error while executing";
}
}
else
{
echo "Unable to sign up.";
}
It works and returns "User created". Why is that so?
Method chaining is not possible with mysqli. The return value of bind_param() is a boolean. It does not return self. You must call the methods like you showed in the second example:
$sql = $conn->prepare($sql_stmt);
$sql->bind_param("sss", $fullname, $username, $password);
$sql->execute();
In fact, mysqli is not very suitable to be used on its own in your application. If you want something simpler, then use PDO. If for some strange reason you must use mysqli, then you require some kind of abstraction layer that will prevent you from dealing with mysqli functions directly.
As of PHP 8.1, you can pass parameters directly in mysqli_stmt::execute(), which enables you to do method chaining in one line:
$sql = $conn->prepare($sql_stmt)->execute([$fullname, $username, $password]);
Also, please stop checking the return value of execute. You should enable mysqli error reporting instead. How to get the error message in MySQLi?
Hi I'm trying to insert data in my database. But I keep on getting the same error for example:
Error: INSERT INTO users (username, password) VALUES ('fff', '$2y$10$YUd1AErIj4RGRnjkFkYlkOn.s9OV62sq8.HVGO2jeE8dSthpgp6ey');
without any details which is very frustrating. I'm new to PHP and SQL so it's not the best written code ever and I know I should use prepared statements.
<?php
require_once '../connection/connection.php';
/**
* Created by PhpStorm.
* User: ezrab
* Date: 3/14/2018
* Time: 5:40 PM
*/
$username = $_POST['username'];
$password = $_POST['password'];
//var_dump($hashed_password);
if (isset($_POST['submit'])) {
if (!empty($username) || !empty($password)) {
if (preg_match('/^[A-Za-z]?[A-Za-z ]*$/', $username) || preg_match('/^[A-Za-z]?[A-Za-z ]*$/', $password)) {
$hashPwd = password_hash($password, PASSWORD_DEFAULT);
$sql = "INSERT INTO users (username, password) VALUES ('$username', '$hashPwd');";
if ($conn->query($sql) === TRUE) {
echo "Worked!";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
}
} else {
echo "You can't use certain characters.";
}
} else {
echo "You have to fill in all fields.";
}
} else {
echo "THOU SHALL NOT PASS!";
}
$conn->close();
EDIT: Added my connection.php file for more information.
<?php
$servername = "-----";
$username = "-----";
$password = "------";
$dbname = "------";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$conn->close();
EDIT:
Take
$conn->close();
out of connection.php and problem should be solved
You were opening and then immediately closing the connection before a query could be made
I can't figure this out. I've googled it and a lot of answers refer to blindValue as the solution but I've also tried that with no luck.
The problem is that the SELECT statement is returning zero records but it should return one record. If I hard code the values into the SQL statement it works but passing them in as parameters isn't. Can some one please help me out with this? Thanks.
<?php
function checklogin($email, $password){
try
{
// Connection
$conn;
include_once('connect.php');
// Build Query
$sql = 'SELECT pkUserID, Email, Password, fkUserGroupID FROM tbluser WHERE Email = :email AND Password = :password';
// $sql = 'SELECT pkUserID, Email, Password, fkUserGroupID FROM tbluser WHERE Email = "a" AND Password = "a"';
// Prepare the SQL statement.
$stmt = $conn->prepare($sql);
// Add the value to the SQL statement
$stmt->bindParam(':email', $email, PDO::PARAM_STR);
$stmt->bindParam(':password', $password, PDO::PARAM_STR);
// Execute SQL
$stmt->execute();
// Get the data in the result object
$result = $stmt->fetchAll(); // $result is NULL always...
// echo $stmt->rowCount(); // rowCount is always ZERO....
// Check that we have some data
if ($result != null)
{
// Start session
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
// Search the results
foreach($result as $row){
// Set global environment variables with the key fields required
$_SESSION['UserID'] = $row['pkUserID'];
$_SESSION['Email'] = $row['Email'];
}
echo 'yippee';
// Return empty string
return '';
}
else {
// Failed login
return 'Login unsuccessful!';
}
$conn = null;
}
catch (PDOexception $e)
{
return 'Login failed: ' . $e->getMessage();
}
}
?>
the connect code is;
<?php
$servername = 'localhost';
$username = 'admin';
$password = 'password';
try {
// Change this line to connect to different database
// Also enable the extension in the php.ini for new database engine.
$conn = new PDO('mysql:host=localhost;dbname=database', $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// echo 'Connected successfully';
}
catch(PDOException $e)
{
echo 'Connection failed: ' . $e->getMessage();
}
?>
I'm connecting to mySQL. Thanks for the help,
Jim
It was a simple but stupid error.
I had a variable called $password also in the connect.php file which was overwriting the $password that I was passing to the checklogin.
Jim
I have a script that updates/creates user from an iOS device. Now i want to have the script also check if the user already exists in the database. I am going to restrict this to username for now, so no more than ONE unique username may exist. I have an if-statement in my PHP but i cannot get it to work - help please :).
<?php
header('Content-type: application/json');
if($_POST) {
$username = $_POST['username'];
$password = $_POST['password'];
if($username && $password) {
$db_name = 'dbname';
$db_user = 'dbuser';
$db_password = 'dbpass';
$server_url = 'localhost';
$mysqli = new mysqli('localhost', $db_user, $db_password, $db_name);
$userexists = mysql_query("SELECT * FROM users WHERE username='$username'");
/* check connection */
if (mysqli_connect_errno()) {
error_log("Connect failed: " . mysqli_connect_error());
echo '{"success":0,"error_message":"' . mysqli_connect_error() . '"}';
}
if(mysql_num_rows($userexists) != 0) {
echo '{"success":0,"error_message":"Username Exist."}';
}
else {
$stmt = $mysqli->prepare("INSERT INTO users (username, password, email) VALUES (?, ?, ?)");
$password = md5($password);
$stmt->bind_param('sss', $username, $password, $email);
/* execute prepared statement */
$stmt->execute();
if ($stmt->error) {error_log("Error: " . $stmt->error); }
$success = $stmt->affected_rows;
/* close statement and connection */
$stmt->close();
/* close connection */
$mysqli->close();
error_log("Success: $success");
if ($success > 0) {
error_log("User '$username' created.");
echo '{"success":1}';
}
else {
echo '{"success":0,"error_message":"Username Exist."}';
}
}
}
else {
echo '{"success":0,"error_message":"Passwords does not match."}';
}
}
else {
echo '{"success":0,"error_message":"Invalid Username."}';
}
}
else {
echo '{"success":0,"error_message":"Invalid Data."}';
}
?>
You could SELECTthe table before trying to insert username. If it already exists (= you have a result) you dont simply insert.
Better yet, use ON DUPLICATE IGNORE or something like that.
I'm trying to code a registration system for a system I am making. Currently, I am receiving a MySQL error that makes me want to tear my head out each and every time I see it.
function UserRegister($user,$pass,$email,$first,$last)
{
$sqlfirst = mysql_real_escape_string($first);
$sqllast = mysql_real_escape_string($last);
$sqluser = mysql_real_escape_string($user);
$hashpass = crypt($pass);
$sqlpass = mysql_real_escape_string($hashpass);
$sqlemail = mysql_real_escape_string($email);
$sql = "SELECT *
FROM planerentalusers
WHERE user = '$sqluser' ";
if($result = mysqli_query($GLOBALS['db'],$sql))
{
$rowcount=mysqli_num_rows($result);
if($rowcount == 1)
{
echo "ERROR: There is already an account with that username! Click <a href='/PHPCalTest/login.php>here </a>to login if this is you. Otherwise, go back and try a different username.";
}
else
{
$sql2 = "INSERT INTO planerentalusers (first,last,user,pass,email) VALUES ('$sqlfirst','$sqllast','$sqluser','$sqlpass','$sqlemail')";
$result2 = mysqli_query($GLOBALS['db'],$sql);
if($result2 == true)
{
return true;
}
else return false;
}
}
else return false;
mysqli_free_result($result);
}
Above is the function that throws the error.
there is no PHP stack trace that is being thrown, so here is what I pinpointed it to: the query is failing. But how, I do not understand. Perhaps someone can point me in the right direction.
That is not a direct answer to your question. It has been solved somewhere between the comment lines.
Now, you can streamline and secure your code if you will:
use prepared statements. It's only natural since you are already using mysqli_* extension. Parameters that you pass to the prepared INSERT statement will be properly escaped.
utilize INSERT IGNORE syntax and check for affected rows with affected_rows. That way you do all you need to do hitting your database only once.
For INSERT IGNORE to work properly you have to have a UNIQUE constraint on username column.
ALTER TABLE planerentalusers ADD UNIQUE (username);
Now if you issue an INSERT IGNORE statement and a username doesn't exist a row will be inserted and affected_rows will return 1. If a username already exists then IGNORE clause will allow your INSERT statement to complete without throwing an error and affected_rows will return 0.
That being said an improved version of your function might look like
function UserRegister($db, $username, $pass, $email, $first, $last) {
$sql = "INSERT IGNORE INTO planerentalusers (first, last, username, pass, email) VALUES (?, ?, ?, ?, ?)";
// prepare the statement
$stmt = $db->prepare($sql);
if (!$stmt) {
die('Can\'t prepare: ' . $db->error); //TODO better error handling
}
// bind parameters
$stmt->bind_param('sssss', $first, $last, $username, $pass, $email);
if (!$stmt) {
die('Can\'t bind parameters: ' . $db->error); //TODO better error handling
}
// execute
$stmt->execute();
if (!$stmt) {
die('Query execution failed: ' . $db->error); //TODO better error handling
}
// get the number of affected rows
$affected_rows = $stmt->affected_rows;
// close the statement
$stmt->close();
return $affected_rows;
}
and the calling code
$first = $_POST['first'];
$last = $_POST['last'];
$username = $_POST['username'];
$pass = crypt($_POST['pass']);
$email = $_POST['email'];
//create a connection to the database
$db = new mysqli('localhost', 'user', 'password', 'dbname');
if ($db->connect_errno) {
die('Connection failed: ' . $db->connect_error); //TODO better error handling
}
if (!UserRegister($db, $username, $pass, $email, $first, $last)) {
echo "ERROR: There is already an account with that username! Click <a href='/PHPCalTest/login.php'>here </a>to login if this is you. Otherwise, go back and try a different username.";
} else {
echo "Account successfully created";
}
Note that
A reference to an open db connection is explicitly passed to the function instead of using $_GLOBALS['db']
presentation logic (echoing an error message and a link) is moved out to the calling code
Basic error handling is implemented throughout the function