Trying to update a field in my DB using a prepared statement - php

I have been on this all day and after searching many websites (including this one) i came to the conclusion that my question hasn't been asked before probably due to my incompetence.
I have a prepared statement here that i would like to update my password field in my DB depending on the username and email, the reason it is updating and not inserting is because its part of my security to not approve site photographers until they have been sent a link
<?php
if (isset($_POST['approved-photographer'])) {
require 'dbh.php';
$username = $_POST['username'];
$email = $_POST['mail'];
$password = $_POST['password'];
$password2 = $_POST['password-repeat'];
if (empty($username) || empty($email) || empty($password) ||
empty($password2))
{
header("location:signup.php?error=emptyfields&username=" . $username
. "&mail=.$email");
exit();
} elseif ($password !== $password2) {
header("location:approvedphoto.php?error=passwordcheck&username=" .
$username . "&mail=" . $email);
exit();
} else {
$sql = "SELECT Password
FROM photographers
WHERE Username= '$username'
AND Email= '$email'";
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt, $sql)) {
header("location:approvedphoto.php?error=sqlerror");
exit();
} else {
$sql = "INSERT INTO photographers (Password) VALUES (?)";
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt, $sql)) {
header("location:approvedphoto.php?error=sqlerror2");
exit();
} else {
$hashedpwd = password_hash($password, PASSWORD_DEFAULT);
mysqli_stmt_bind_param($stmt, "s", $hashedpwd);
mysqli_stmt_execute($stmt);
header("location:signin.php?signup=success");
exit();
}
}
}
}
Any Help would be greatly appreciated. Thanks for reading

The short answer for your MySQLi usage is you didn't bind the parameters, which you can do using mysqli_stmt_bind_param (Future readers, this last statement is now irrelevant due to edits). Overall your sql statements post-editing seem unclear, you would typically either be updating a password (in which case you need a WHERE clause so you don't update everyone's password), or you should be inserting a new user with a password.
This is a more-or-less tangential answer, but I would like to throw my hat into the ring for the use of PDO (instead of mysqli). MySQLi works with only one form of database flavor, MySQL. Additionally it allows for a much less object-oriented solution to db interactions. Here's an example of how you could accomplish this through PDO:
//specifies the driver, ip/database etc. Swap out for your ip and database used
$driverStr = 'mysql:host=<ip>;dbname=<database>;charset=utf8';
//you can set some default behaviors here for your use, I put some examples
//I left a link below so you can see the different options
$options = [
//spew exceptions on errors, helpful to you if you have php errors enabled
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
//substite what you need for username/password here as well, $options can be omitted
$conn = new PDO($driverStr, '<username>', '<password>', $options);
Link to the aforementioned attributes
Now that we've made our connection:
//I used a "named parameter", e.g. :password, instead of an anonymous parameter
$stmt = $conn->prepare("UPDATE Photographers SET password = :password WHERE Username = :username");
//with our prepared statement, there's a few ways of executing it
//1) Using #bind*
//there's also #bindValue for not binding a variable reference
//for params, PARAM_STR is default and can be safely omitted
$stmt->bindParam(':password', $password, PDO::PARAM_STR);
$stmt->bindParam(':username', $username);
$stmt->execute();
//2) Using execute directly
$stmt->execute(['password' => $password, 'username' => $username]);
Then, were the statement a query and not just a database update/insert, we can simply retrieve the results of the statement. By using #bindParam you can also just update the variable's values and re-execute the statement if you like, which may be useful to you for some other statements.
//see #fetch and #fetchAll's documentation for the returned data formatting
$results = $stmt->fetchAll(PDO::FETCH_OBJ); //return it as a php object
$results = $stmt->fetch(PDO::FETCH_NUM)[0]; //unsafely retrieve the first value as a number
Over the years I've found this to be much cleaner and more managable than any of the mysqli_* or even the deprecated mysql_* methods.

Related

php - unable to register user when using prepared statements

For some reason, my code isn't registering the user when they click register. I was first using regular mysqli, and when I changed the code to a prepared statement, it didn't work. If the registration was successful, it's supposed to show a message, and it is showing the message even though the values haven't been inserted into the database.
Here is my code:
<?php
session_start();
// variable declaration
$username = "";
$email = "";
$errors = array();
$_SESSION['success'] = "";
// connect to database
$db = mysqli_connect('localhost', 'root', 'password', 'cubetastic');
// REGISTER USER
if (isset($_POST['reg_user'])) {
// receive all input values from the form
$username = $_POST['username'];
$email = $_POST['email'];
$password_1 = $_POST['password_1'];
$password_2 = $_POST['password_2'];
// form validation: ensure that the form is correctly filled
// I don't think this part is relevant here
// register user if there are no errors in the form
if (count($errors) == 0) {
$_SESSION['verify'] = "Your account has been created, please verify it by clicking the activation link that has been sent to your email.";
$hash = md5(rand(0,1000)); // Generate random 32 character hash and assign it to a local variable.
// Example output: f4552671f8909587cf485ea990207f3b
$password = md5($password_1);//encrypt the password before saving in the database
$query = "INSERT INTO users (username, email, password, hash)
VALUES(?, ?, ?, ?)";
$stmt = mysqli_prepare($db, $query);
mysqli_stmt_bind_param($stmt, 's, s, s, s', $username, $email, $password, $hash);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
}
}
?>
The manual on mysqli_stmt_bind_param() clearly shows no commas are used in order to state which data types to pass as the arguments and in conjunction with that, the number of binds.
I have to state that MD5 is no longer considered to be a safe hashing method, especially in a live environment.
Use both password_hash() and password_verify() respectively and make sure that the (password) column is long enough to accomodate its length.
The manual suggests to use a minimum of 60 length, yet states that a length of 255 is a good bet.
References:
http://php.net/manual/en/function.password-hash.php
http://php.net/manual/en/function.password-verify.php
As I (also) stated in comments: Use mysqli_error($db) on the query. That would have thrown you something about this syntax error. Always use this during development testing and always read the official manuals; that's what they're there for.

prevent sql injection in mysqli [duplicate]

This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Can I mix MySQL APIs in PHP?
(4 answers)
Closed 3 years ago.
I am very new to mysqli earlier i am writing queries in mysql but mysqli is more advanced so, i am first time using it.
Below is my php code.
function clean($str) {
$str = #trim($str);
if(get_magic_quotes_gpc()) {
$str = stripslashes($str);
}
return mysql_real_escape_string($str);
}
$email = clean($_POST['email']);
$password = clean($_POST['password']);
//$password =md5($password);
if(empty($res['errors'])) {
$result = $mysqli->query("SELECT uid FROM users where email='$email' and password = '$password'");
if($result->num_rows == 1){
$res['success'] = true;
}
else{
array_push($res['errors'], 'Invalid login details');
$res['success'] = false;
}
}else{
$res['success'] = false;
}
echo json_encode($res);
}
clean function is not working as expected because sql queries return false if i enter username and password correct.
So, it seems like this is not valid in mysqli case.
I checked this link PHP MySQLI Prevent SQL Injection and got to know that we have to prepare query.
I can see there is an example but i am not able to understand how to prepare/bind if i have to use two or more form data.
Thanks for your time.
Updated code
$result = $mysqli->prepare("SELECT uid FROM users where email=:email and password = :password");
$result->execute([
':email' => $email,
':password' => $password]);
//$result->execute();
if($result->num_rows == 1){
//if(mysqli_num_rows($result) === 1) {
$res['success'] = true;
}
else{
array_push($res['errors'], 'Invalid login details');
$res['success'] = false;
}
As already stated in comments, you need to be consistent with your API choice. You can't mix APIs in PHP.
You started out with mysqli_*, so I'll continue with that. You had some mysql_* and PDO in there, and it might not be a bad idea to use PDO over mysqli_* - but if your server supports mysqli_*, there is nothing wrong with using that. See Choosing an API and decide for yourself (just stay away from mysql_*, it's outdated).
Using mysqli_*, you connect to the database like this (you didn't show your connection).
$mysqli = new mysqli("host", "username", "password", "database");
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: (".$mysqli->connect_errno.") ".$mysqli->connect_error;
}
$mysqli->set_charset("utf8");
As for preventing SQL injection in it self, all you need is to use prepared statements. You can still clean or sanitize your data if there are some kind of values you don't want sitting in your tables - but that's kind of another discussion.
You also need to know if your passwords are hashed in the database. They really should be, and you should be using password_hash($password, $algorithm) and password_verify($password, $hash) if you're on PHP5.5 and above (if not, look into something like password_compat).
You need to be consistent with your hashes too, you can't insert it with md5 and selecting it with no hash. It all needs to be the same. Because if you are selecting an md5 hash, and comparing it to an unhashed string, they will be different, and the query fails.
I'm showing you an example of using password_verify(), so that means that the password stored in the database will also need to be stored with password_hash() (or your query fails).
if ($stmt = $mysqli->prepare("SELECT uid, password FROM users where email=?")) {
$stmt->bind_param("s", $_POST['email']); // Bind variable to the placeholder
$stmt->execute(); // Execute query
$stmt->bind_result($userID, $password); // Set the selected columns into the variables
$stmt->fetch(); // ...and fetch it
if ($stmt->num_rows) {
if (password_verify($_POST['password'], $password)) {
// Password was correct and matched the email!
} else {
// Password was incorrect...
}
} else {
// Accountname not found
}
}
This is just a basic example, but it will get you started. Never trust user input, use prepared statements.
You can bind more variables like so:
$stmt = $mysqli->prepare("SELECT uid FROM users where email= ? and password = ?");
$stmt->bind_param('ss', $email, $password);
/* execute prepared statement */
$stmt->execute();
As you can see, you can expand on the bind_param() function. You can also add different type of variables:
i corresponding variable has type integer
d corresponding variable has type double
s corresponding variable has type string
b corresponding variable is a blob and will be sent in packets
From: http://php.net/manual/en/mysqli-stmt.bind-param.php
First of all, I suggest you learn PDO instead of MySQLi, just because it supports more drivers.
Second, you use mysql_real_escape_string, as you might see, that is a MySQL function, not a MySQLi function.
So where you have:
$result = $mysqli->query("SELECT uid FROM users where email='$email' and password = '$password'");
You should do something like:
<?php
$stmt = $dbConnection->prepare("SELECT uid FROM users where email = :email AND password = :password");
try{
$stmt->execute([
':email' => $email,
':password' => $password
]);
}
catch(Exception $e){
echo $e->getMessage(); //Remove when putting online
}
if($stmt->num_rows){
$res['success'] = true;
}
?>
You're presently mixing MySQL APIs/functions with mysql_real_escape_string(), then num_rows and then a PDO binding method where email=:email and password = :password which seems to have been taken from another answer given for your question.
Those different functions do NOT intermix.
You must use the same one from connection to querying.
Consult: Can I mix MySQL APIs in PHP?
It looks like you're wanting to setup a login script. I suggest you use the following and pulled from one of ircmaxell's answers:
Pulled from https://stackoverflow.com/a/29778421/
Just use a library. Seriously. They exist for a reason.
PHP 5.5+: use password_hash()
PHP 5.3.7+: use password-compat (a compatibility pack for above)
All others: use phpass
Don't do it yourself. If you're creating your own salt, YOU'RE DOING IT WRONG. You should be using a library that handles that for you.
$dbh = new PDO(...);
$username = $_POST["username"];
$email = $_POST["email"];
$password = $_POST["password"];
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $dbh->prepare("insert into users set username=?, email=?, password=?");
$stmt->execute([$username, $email, $hash]);
And on login:
$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $dbh->prepare($sql);
$result = $stmt->execute([$_POST['username']]);
$users = $result->fetchAll();
if (isset($users[0]) {
if (password_verify($_POST['password'], $users[0]->password) {
// valid login
} else {
// invalid password
}
} else {
// invalid username
}
It's safer and uses a safe password hashing method, rather than what you seem to want to use is MD5 $password =md5($password); and is no longer considered safe to use now.
References:
PDO connection http://php.net/manual/en/pdo.connections.php
PDO error handling http://php.net/manual/en/pdo.error-handling.php
To check if a user exists, you can see one of my answers https://stackoverflow.com/a/22253579/1415724
http://php.net/manual/en/mysqli.error.php
http://php.net/manual/en/function.error-reporting.php
Sidenote: If you do go that route, remember to read the manuals and that your password column is long enough to hold the hash. Minimum length is 60, but they recommend 255.
It is also unclear if your HTML form does have name attributes for the POST arrays, so make sure the form is using a POST method.
http://php.net/manual/en/tutorial.forms.php
I believe I have given you enough information to get started.
What you must NOT do, is to use the above with your present code and simply patching it. You need to start over.
Add error reporting to the top of your file(s) which will help find errors.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
// rest of your code
Sidenote: Displaying errors should only be done in staging, and never production.

Getting an error using mysqli_escape_string function

I get an error on the last line on mysqli_escape_string($hash)); by using the following code:
$hash = md5( rand(0,1000) );
$stmt = $mysqli->prepare("INSERT INTO users (username, password, hash) VALUES (?, ?, mysqli_escape_string($hash))");
$password = md5($password);
$stmt->bind_param('ss', $username, $password, mysqli_escape_string($hash));
It says, that the mysqli_escape_string($hash)) is a non-object.
But using only $hash instead doesn't help either
Can someone help?
There are far too many things wrong with your code and will be extremely difficult to provide a solution by fixing what you have now.
Firstly, MD5 is no longer considered safe to use for password storage.
Consult:
https://security.stackexchange.com/questions/19906/is-md5-considered-insecure
https://en.wikipedia.org/wiki/MD5
Plus, you're not using prepared statements correctly.
Consult: http://php.net/manual/en/mysqli.prepare.php
As I stated, the mysqli_escape_string() function requires a database connection be passed as the first parameter:
http://php.net/manual/en/mysqli.real-escape-string.php
Do yourself a favor and use this, one of ircmaxell's answers https://stackoverflow.com/a/29778421/
Pulled from his answer:
Just use a library. Seriously. They exist for a reason.
PHP 5.5+: use password_hash()
PHP 5.3.7+: use password-compat (a compatibility pack for above)
All others: use phpass
Don't do it yourself. If you're creating your own salt, YOU'RE DOING IT WRONG. You should be using a library that handles that for you.
$dbh = new PDO(...);
$username = $_POST["username"];
$email = $_POST["email"];
$password = $_POST["password"];
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $dbh->prepare("insert into users set username=?, email=?, password=?");
$stmt->execute([$username, $email, $hash]);
And on login:
$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $dbh->prepare($sql);
$result = $stmt->execute([$_POST['username']]);
$users = $result->fetchAll();
if (isset($users[0]) {
if (password_verify($_POST['password'], $users[0]->password) {
// valid login
} else {
// invalid password
}
} else {
// invalid username
}
Your code should be
$hash = md5( rand(0,1000) );
$stmt = $mysqli->prepare("INSERT INTO users (username, password, hash) VALUES (?, ?, ?)");
$password = md5($password);
$stmt->bind_param('sss', $username, $password, $hash);
You don't need to escape with parameterized queries.
Issues you had, your escape function was incorrect you need the object with the function when using OO approach.
$mysqli->real_escape_string($hash);
would have been what you wanted.
You also were binding that value again though which would have thrown an error and didn't set it in the variable types being passed.
A string that contains one or more characters which specify the types for the corresponding bind variables.
So
$stmt->bind_param('ss', $username, $password, mysqli_escape_string($hash));
should have had three 's's because there are three strings, and no need for the escaping.
Also md5ing passwords isn't the best practice anymore, take a look at:
Secure hash and salt for PHP passwords
https://security.stackexchange.com/questions/19906/is-md5-considered-insecure

Fetch results from database with mysqli prepared statements

I tried to fetch results from database with mysqli prepared statements and I wanted to store it in session, but I failed. I want to store id, username, password and mail to session. Can anyone review my code?
$kveri = $mysqli->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$kveri->bind_param('ss', $username, $password);
$kveri->execute();
$kveri->store_result();
$numrows = $kveri->num_rows;
if ( $numrows == 1 )
{
$kveri->bind_result($user_id, $user_username, $user_password, $user_mail);
while ( $kveri->fetch() )
{
$_SESSION['ulogovan'] = true;
$_SESSION['username'] = $user_username;
}
$kveri->close();
echo $_SESSION['username'];
}
Make sure you have turned on error reporting as follows:
mysqli_report(MYSQLI_REPORT_ALL);
You are likely to get the answer there. I ran your code flawlessly and it should work.
Note, I added the following code to the beginning to test (with constants defined but not shown here):
$mysqli = new mysqli(DB_SERVER, DB_USER, DB_PASS,DB_NAME);
As you can see, mysqli is quite inconvenient, if used as is. Thousand tricks, nuances and pitfalls.
To make database interactions feasible, one have to use some sort of higher level abstraction, which will undertake all the repeated dirty job.
Out of such abstractions PDO seems most conventional one:
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $password]);
$user = $stmt->fetch();
if ($user) {
$_SESSION['username'] = $user['username'];
}
see - when used wisely, a code not necessarily have to be of the size of "War and Peace"

Updating Multiple Fileds with logged in user and SHA1 Password

I've been looking around and can't find a place that is showing me an effective way to do this. Currently I have a query that runs when the user submits a form:
$query = "UPDATE user SET username='$_POST[username]',
nicename='$_POST[nicename]',
email='$_POST[email]',
password=(SHA1)'$_POST[password]',
position='$_POST[position]',
race='$_POST[race]',
type='$_POST[type]' WHERE username=$_SESSION[admin_login]";
I'm not sure on how to get this to actually work correctly. Sorry if it's been asked before, but I can't find a good solution to this anywhere. Thanks in advance for any help.
First of all entire thing is wrong : Why?
Because first of all you need to sanitize the input, which you are not doing, atleast you should use mysqli_real_escape_string like this :
$nicename = mysqli_real_escape_string($connect, $_POST['nicename']);
Reference
Secondly you should encrypt the password before you use it in your query like assign your encrypted password to a variable and than use it in your query, like this :
$hashed_pass = sha1($_POST['password']);
//Query goes here
and last but not the least instead of using super global $_SESSION variable directly in your query, use concatenate it.. like this
WHERE username='".$_SESSION[admin_login]."'";
Firstly, always remember Little Bobby Tables. Inserting data like that can lead to SQL injection attacks just like in that cartoon. I'd highly suggest you use prepared statements, this is a feature in both PDO and MySQLi which are methods of reading and writing to a database using PHP, some info on: PDO and some info on: MySQLi.
Whichever you choose to go with doesn't really matter, it's more about personal preference. I like PDO, so here's an example of binding the data and then executing your query using PDO:
$dbh = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
$password = sha1($_POST[password]);
$stmt = $dbh->prepare("UPDATE user SET username = :username, nicename = :nicename, email = :email, password = :password, position = :position, race = :race, type = :type WHERE = :username");
$stmt->bindParam(':username', $_POST['username']);
$stmt->bindParam(':nicename', $_POST['nicename']);
$stmt->bindParam(':email', $_POST['email']);
$stmt->bindParam(':password', $password);
$stmt->bindParam(':position', $_POST['position']);
$stmt->bindParam(':race', $_POST['race']);
$stmt->bindParam(':type', $_POST['type']);
$stmt->bindParam(':username', $_SESSION['admin_login']);
$stmt->execute();
$_POST and $_GET arrays can contain dangerous data, so you need prepare data from these arrays before inserting them into DB.
First, you need typecast values to right data types. In PHP you can use followed constructions: (string) for string data, (int) and (float) for numeric data, (bool) for boolean data.
Field email necessary checked for valid email, use Regex for it.
Follow code is sample of checking data:
<?php
$link = mysqli_connect('localhost', 'my_user', 'my_password', 'my_db');
$username = mysqli_real_escape_string($link, (string) $_POST['username']);
$nicename = mysqli_real_escape_string($link, (string) $_POST['nicename']);
$email = mysqli_real_escape_string($link, (string) $_POST['email']);
$email = preg_replace( '/^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*#[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.(([0-9]{1,3})|([a-zA-Z]{2,3})|(aero|coop|info|museum|name))$/', $email );
$password = sha1((string) $_POST['password']);
$position = mysqli_real_escape_string($link, (string) $_POST['position']);
$race = mysqli_real_escape_string($link, (string) $_POST['race']);
$type = mysqli_real_escape_string($link, (string) $_POST['type']);
$admin = $_SESSION['admin_login'];
$query = "UPDATE `user`
SET `username`='$username',
`nicename`='$nicename',
`email`='$email',
`password`='$password',
`position`='$position',
`race`='$race',
`type`='$type'
WHERE `username`='$admin'";
mysqli_query($link, $query);
mysqli_close($link);

Categories