Somebody help me with my code. I create a edit password page containing the Current Password, New Password and Confirm Password. Here's my code:
edit_password.php
<form action="editpassword_process.php" method="post">
<table>
<tr class="form-group has-feedback has-success">
<td><h4>Current Password</h4></td>
<td><div class="control-group input-lg"><input type="password" placeholder="" passfield="true" id="currentpassword" name="currentpassword"></div></td> <!-- class="input-sm" required -->
<td><span id="messagebox"></span></td>
</tr>
<tr>
<td><h4>New Password</h4></td>
<td><div class="control-group input-lg"><input type="password" placeholder="" passfield="true" id="newpassword1" name="newpassword1"></div></td> <!-- required class="input-sm" -->
</tr>
<tr>
<td><h4>Confirm Password</h4></td>
<td><div class="control-group input-lg"><input type="password" placeholder="" passfield="true" id="newpassword2" name="newpassword2" onKeyUp="checkPass(); return false;"></div></td> <!-- required class="input-sm" -->
<span id="confirmMessage" class="confirmMessage"></span>
</tr>
</table>
<button class="btn btn-info">Submit</button>
</form>
Here's my code of editpassword_process.php
<?php
include('connection.php');
$currentpw = $_POST['currentpassword'];
$newpw = $_POST['newpassword1'];
$confirmnewpw = $_POST['newpassword2'];
$res = mysql_query("SELECT user_password FROM `tbl_userlist` WHERE userid = '".$_SESSION['userid']."'");
if($currentpw != mysql_result($res, 0)){
echo "You entered an incorrect password";
}
if($newpw = $confirmnewpw){
$sql = mysql_query("UPDATE tbl_userlist SET user_password = '$newpw' WHERE userid = '".$_SESSION['userid']."'");
}
if($sql){
echo "You have successfully changed your password";
}
else{
echo "The new password and confirm pasword fields must be the same";
}
?>
When i click the submit it appears an alert that shows Validated OK but my database didn't update.
Thank you in advance
There are a few things wrong with your code, but I'll start with why your user password isn't updating. You haven't started a session anywhere in your code. Anywhere you use sessions, you need to start them with:
session_start();
This should be the very first thing you do after your opening <?php tag.
You're assigning(=) and not comparing (==) in a lot of your if(){.. comparison blocks, these will evaluate to TRUE, running the condition.
Now on to the bad, you're using a deprecated library. All mysql_* functions are deprecated and removed as of PHP7. It's best to get ahead of the curve-ball by learning either of these 2 libraries:
PDO
MySQLi Prepared Statements
Either of them will mitigate any SQL Injections you'd come across with your currently vulnerable code. Not to mention the fact that you're storing passwords in plain text. Imagine the implications when (not if) your database is hacked, I hope this isn't a production environment.
PHP makes it super simple to hash a password, just check out:
password_hash()
password_verify()
They'll sort out your password hashing.
To simplify what you're doing, this would be a PDO example along with hashing of your passwords to show you how simple it is to achieve what you're trying to do:
<?php
session_start();
include('connection.php');
$currentpw = $_POST['currentpassword'];
$newpw = $_POST['newpassword1'];
$confirmnewpw = $_POST['newpassword2'];
// start your PDO object
$db = new PDO('mysql:host=localhost;dbname=DATABASE', 'username','password');
$statement = $db->prepare("SELECT user_password FROM `tbl_userlist` WHERE userid = :userid");
$statement->execute(array(':userid' => $_SESSION['userid']));
// check if we have a row
if ($statement->rowCount() > 0) {
$data = $statement->fetch(PDO::FETCH_ASSOC);
$current_password_hash = $data['user_password'];
// check current password is correct.
if (!password_verify($currentpw, $current_password_hash)) {
// wrong "current" password.
die("You entered an incorrect password");
}
// check that both passwords match.
if (trim($confirmnewpw) !== trim($newpw)) {
// new passwords dont match
die("The new password and confirm pasword fields must be the same");
}
// can only get here if passwords match.
// so hash the new password and store in the database.
$newpwhash = password_hash(trim($confirmnewpw), PASSWORD_BCRYPT, array('cost' => 11));
// now lets update table to add new password hash.
$update = $db->prepare("UPDATE tbl_userlist SET user_password = :newpwhash WHERE userid = :userid");
if($update->execute(array(':newpwhash' => $newpwhash, ':userid' => $_SESSION['userid']))) {
// password updated successfully.
die("You have successfully changed your password");
} else {
// failed to update, check logs to ammend.
die('Failed to update password.');
}
} else {
// wrong "current" password.
die("No password found for you...");
}
Needless to say, this will mean you'll have to change your login process too, but it's simple. All you'll need to do is fetch the password and harness password_verify(), and voila, you're sorted.
(Not to mention, a ton more secure.)
Change following things
<?php
session_start(); // Start session as you are using it
include('connection.php');
$currentpw = $_POST['currentpassword'];
$newpw = $_POST['newpassword1'];
$confirmnewpw = $_POST['newpassword2'];
$res = mysql_query("SELECT user_password FROM `tbl_userlist` WHERE userid = '".$_SESSION['userid']."'");
if($currentpw != mysql_result($res, 0)){
echo "You entered an incorrect password";
}
if($newpw = $confirmnewpw){ // Compare using == or ===
$sql = mysql_query("UPDATE tbl_userlist SET user_password = '$newpw' WHERE userid = '".$_SESSION['userid']."'");
// mysql_query("UPDATE tbl_userlist SET user_password = '$newpw' WHERE userid = '".$_SESSION['userid']."'", $connectionVar);
}
if($sql){ // Here You have not executed the query now use $connection->query($sql)
// OR mysql_query($connection,$sql); any other
echo "You have successfully changed your password";
}
else{
echo "The new password and confirm password fields must be the same";
} ?>
when you running this code :
"UPDATE tbl_userlist SET user_password = '$newpw' WHERE userid = '".$_SESSION['userid']."'"
that's mean you update tbl_userlist with criteria userid from session. value from session can be used if you start the session using code session_start();
Related
I have a problem generating Bcrypt passwords. Instead of typing them manual in the hash variable and if statement I would like to generate them in forms HTML. I am not sure how to do that.
<?php
/*in the if statment make sure that the password is the same as in hash variable*/
$options = array('cost' => 12);
echo "Bcrypt: ";
echo $hash = password_hash("yourpassword", PASSWORD_BCRYPT, $options);
echo "<br>";
echo "Verify now:<br>";
if (password_verify('yourpassword', $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
?>
<p>Please enter a value to encrypt!</p>
<form action="invite.php">
Key:<br>
<input type="text" name="firstname"><br>
<input type="submit" value="Submit">
</form>
I'll make an attempt at answering this and if this is what the question's about.
You can assign a variable to a POST array (and using a post method for the form) to be passed as the first parameter for both functions, and check if it's not empty and using isset() against a named input/submit.
N.B.: The code below was written to be used inside the same file, as I used action="". If you intend on using this in two separate files, then by all means change the action to the filename used for it.
<?php
/*in the if statment make sure that the password is the same as in hash variable*/
$options = array('cost' => 12);
echo "Bcrypt: ";
if(isset($_POST['submit'])){
if(!empty($_POST['firstname'])){
$their_input = $_POST['firstname'];
echo $hash = password_hash($their_input, PASSWORD_BCRYPT, $options);
echo "<br>";
echo "Verify now:<br>";
if (password_verify($their_input, $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
}
else{
echo "You left this empty.";
}
}
?>
<p>Please enter a value to hash!</p>
<form action="" method="post">
Key:<br>
<input type="text" name="firstname"><br>
<input type="submit" name="submit" value="Submit">
</form>
Sidenote: If this is intended for passwords, then you should change the input type to "password" instead of "text".
If you later want to use this as a login system, then have a look at one of ircmaxell's answers https://stackoverflow.com/a/29778421/
If uses PDO and with a prepared statement.
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
}
Important sidenote about column length:
If and when you do decide to use password_hash() or the compatibility pack (if PHP < 5.5) https://github.com/ircmaxell/password_compat/, it is important to note that if your present password column's length is anything lower than 60, it will need to be changed to that (or higher). The manual suggests a length of 255.
You will need to ALTER your column's length and start over with a new hash in order for it to take effect. Otherwise, MySQL will fail silently.
I'm trying to has a password in PHP using password_hash and password_verify. I am correctly hashing the password as it is being into the database hashed, but when I attempt to unhash the password whilst logging in, it doesn't seem to want to work. The password is being recieved from an Android application but after echoing both the username and the password, they are correct to what they should be. To hash the password, I am using PASSWORD_DEFAULT as the hashing technique.
Code:
<?php
error_reporting(0);
require_once('dbconnect.php');
$username = $_POST["username"];
$password = $_POST["password"];
$result = $conn->query("SELECT * FROM User WHERE username ='$username'");
if(empty($result)){
die("Username doesn't exist");
}
$dbpass = $conn->query("SELECT password FROM User WHERE username = '$username'");
if (password_verify($password, $dbpass)){
$stmt = "SELECT * FROM User WHERE username='$username' and password='$password'";
$check = mysqli_fetch_array(mysqli_query($conn, $stmt));
if(isset($check)){
echo "success";
}else{
echo "Invalid Username or Password";
}
}
else {
echo "password not unhashing";
}
$conn->close();
Am I missing something obvious?
First, use prepared statements to remove the threat of SQL injection, or your login screen becomes an attack vector. Then the problem is you're not getting the actual dbpass, you're getting a result set containing $dbpass, without dereferencing it.
Try it this way:
//username in where clause is coming from the user, don't execute it
//also fetch a clean copy of the username from the database we can trust to do things with like display -- assuming we filtered it on the way into the database.
$stmnt = $conn->prepare('select username,password from user where username = ?') or die('...');
//username must be a string, and to keep it clear it came from a user, and we don't trust it, leave it in POST.
$stmnt->bind_param('s',$_POST['username']) or die('...');
//Do the query.
$stmnt->execute() or die('...');
//Where to put the results.
$stmnt->bind_result($username,$dbpass);
//Fetch the results
if($stmnt->fetch()) //get the result of the query.
{
if(password_verify($_POST['password'],$dbpass))
{
//The password matches.
}
else
{
//password doesn't match.
}
}
else
{
//username is wrong.
}
I have a profile.php wich echo the user info i want. After searching alittle on google and watching a few tutorials, im stuck. I want every user registered to my website to get a profile (unique), that is access able for everyone. The link i plan on using is example.com/user/id where "id" is replaced with the id of the users profile you want to view.
Register.php
// This if statement checks to determine whether the registration form has been submitted
// If it has, then the registration code is run, otherwise the form is displayed
if(!empty($_POST))
{
// Ensure that the user has entered a non-empty username
if(empty($_POST['username']))
{
// Note that die() is generally a terrible way of handling user errors
// like this. It is much better to display the error with the form
// and allow the user to correct their mistake. However, that is an
// exercise for you to implement yourself.
die("<div class='red'>Please enter a username.</div>");
}
// Ensure that the user has entered a non-empty password
if(empty($_POST['password']))
{
die("<div class='red'>Please enter a password.</div>");
}
// Make sure the user entered a valid E-Mail address
// filter_var is a useful PHP function for validating form input, see:
// http://us.php.net/manual/en/function.filter-var.php
// http://us.php.net/manual/en/filter.filters.php
if(!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL))
{
die("<div class='red'>Invalid E-Mail Address</div>");
}
// We will use this SQL query to see whether the username entered by the
// user is already in use. A SELECT query is used to retrieve data from the database.
// :username is a special token, we will substitute a real value in its place when
// we execute the query.
$query = "
SELECT
1
FROM users
WHERE
username = :username
";
// This contains the definitions for any special tokens that we place in
// our SQL query. In this case, we are defining a value for the token
// :username. It is possible to insert $_POST['username'] directly into
// your $query string; however doing so is very insecure and opens your
// code up to SQL injection exploits. Using tokens prevents this.
// For more information on SQL injections, see Wikipedia:
// http://en.wikipedia.org/wiki/SQL_Injection
$query_params = array(
':username' => $_POST['username']
);
try
{
// These two statements run the query against your database table.
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("<div class='red'>Failed to run query: </div>" . $ex->getMessage());
}
// The fetch() method returns an array representing the "next" row from
// the selected results, or false if there are no more rows to fetch.
$row = $stmt->fetch();
// If a row was returned, then we know a matching username was found in
// the database already and we should not allow the user to continue.
if($row)
{
die("<div class='red'>This username is already in use</div>");
}
// Now we perform the same type of check for the email address, in order
// to ensure that it is unique.
$query = "
SELECT
1
FROM users
WHERE
email = :email
";
$query_params = array(
':email' => $_POST['email']
);
try
{
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex)
{
die("<div class='red'>Failed to run query: </div>" . $ex->getMessage());
}
$row = $stmt->fetch();
if($row)
{
die("<div class='red'>This email address is already registered</div>");
}
// An INSERT query is used to add new rows to a database table.
// Again, we are using special tokens (technically called parameters) to
// protect against SQL injection attacks.
$query = "
INSERT INTO users (
username,
password,
salt,
email
) VALUES (
:username,
:password,
:salt,
:email
)
";
// A salt is randomly generated here to protect again brute force attacks
// and rainbow table attacks. The following statement generates a hex
// representation of an 8 byte salt. Representing this in hex provides
// no additional security, but makes it easier for humans to read.
// For more information:
// http://en.wikipedia.org/wiki/Salt_%28cryptography%29
// http://en.wikipedia.org/wiki/Brute-force_attack
// http://en.wikipedia.org/wiki/Rainbow_table
$salt = dechex(mt_rand(0, 2147483647)) . dechex(mt_rand(0, 2147483647));
// This hashes the password with the salt so that it can be stored securely
// in your database. The output of this next statement is a 64 byte hex
// string representing the 32 byte sha256 hash of the password. The original
// password cannot be recovered from the hash. For more information:
// http://en.wikipedia.org/wiki/Cryptographic_hash_function
$password = hash('sha256', $_POST['password'] . $salt);
// Next we hash the hash value 65536 more times. The purpose of this is to
// protect against brute force attacks. Now an attacker must compute the hash 65537
// times for each guess they make against a password, whereas if the password
// were hashed only once the attacker would have been able to make 65537 different
// guesses in the same amount of time instead of only one.
for($round = 0; $round < 65536; $round++)
{
$password = hash('sha256', $password . $salt);
}
// Here we prepare our tokens for insertion into the SQL query. We do not
// store the original password; only the hashed version of it. We do store
// the salt (in its plaintext form; this is not a security risk).
$query_params = array(
':username' => $_POST['username'],
':password' => $password,
':salt' => $salt,
':email' => $_POST['email']
);
try
{
// Execute the query to create the user
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("<div class='red'>Failed to run query:</div> " . $ex->getMessage());
}
// This redirects the user back to the login page after they register
header("Location: /login");
// Calling die or exit after performing a redirect using the header function
// is critical. The rest of your PHP script will continue to execute and
// will be sent to the user if you do not die or exit.
die("Redirecting to /login");
}
?>
Login.php
<?php
// This variable will be used to re-display the user's username to them in the
// login form if they fail to enter the correct password. It is initialized here
// to an empty value, which will be shown if the user has not submitted the form.
$submitted_username = '';
// This if statement checks to determine whether the login form has been submitted
// If it has, then the login code is run, otherwise the form is displayed
if(!empty($_POST))
{
// This query retreives the user's information from the database using
// their username.
$query = "
SELECT
id,
username,
password,
salt,
email,
bio,
warnings,
clan,
timestamp,
title,
FROM users
WHERE
username = :username
";
// The parameter values
$query_params = array(
':username' => $_POST['username']
);
try
{
// Execute the query against the database
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("<div class='red'>Failed to run query: </div>" . $ex->getMessage());
}
// This variable tells us whether the user has successfully logged in or not.
// We initialize it to false, assuming they have not.
// If we determine that they have entered the right details, then we switch it to true.
$login_ok = false;
// Retrieve the user data from the database. If $row is false, then the username
// they entered is not registered.
$row = $stmt->fetch();
if($row)
{
// Using the password submitted by the user and the salt stored in the database,
// we now check to see whether the passwords match by hashing the submitted password
// and comparing it to the hashed version already stored in the database.
$check_password = hash('sha256', $_POST['password'] . $row['salt']);
for($round = 0; $round < 65536; $round++)
{
$check_password = hash('sha256', $check_password . $row['salt']);
}
if($check_password === $row['password'])
{
// If they do, then we flip this to true
$login_ok = true;
}
}
// If the user logged in successfully, then we send them to the private members-only page
// Otherwise, we display a login failed message and show the login form again
if($login_ok)
{
// Here I am preparing to store the $row array into the $_SESSION by
// removing the salt and password values from it. Although $_SESSION is
// stored on the server-side, there is no reason to store sensitive values
// in it unless you have to. Thus, it is best practice to remove these
// sensitive values first.
unset($row['salt']);
unset($row['password']);
// This stores the user's data into the session at the index 'user'.
// We will check this index on the private members-only page to determine whether
// or not the user is logged in. We can also use it to retrieve
// the user's details.
$_SESSION['user'] = $row;
// Redirect the user to the private members-only page.
header("Location: /");
die("Redirecting to: /");
}
else
{
// Tell the user they failed
print("<div class='red'>Login Failed.</div>");
// Show them their username again so all they have to do is enter a new
// password. The use of htmlentities prevents XSS attacks. You should
// always use htmlentities on user submitted values before displaying them
// to any users (including the user that submitted them). For more information:
// http://en.wikipedia.org/wiki/XSS_attack
$submitted_username = htmlentities($_POST['username'], ENT_QUOTES, 'UTF-8');
}
}
?>
Profile.php
<!DOCTYPE html>
<html>
<head>
<!--START HEAD-->
<?php include 'head.php'; ?>
<link rel='stylesheet' type='text/css' href="/profile.css"/>
<title><?php print htmlentities($_SESSION['user']['username'], ENT_QUOTES, 'UTF-8'); ?>'s profile</title>
<!--END HEAD-->
</head>
<body>
<!--START BODY-->
<?php
// At the top of the page we check to see whether the user is logged in or not
if(empty($_SESSION['user']))
{
include'navigationbar.php';
} else {
include'navigationbar2.php';
}
?>
<!--MAIN CONTENT-->
<div class="movedown"></div>
<div class="content">
<!--MAIN PROFILE-->
<div class="username">
<?php if(empty($_SESSION['user']['clan'])){
print "";
}else{
print "[".htmlentities($_SESSION['user']['clan'], ENT_QUOTES, 'UTF-8')."]";} ?>
<?php print htmlentities($_SESSION['user']['username'], ENT_QUOTES, 'UTF-8'); ?>
<div class="fr">ID: <?php print htmlentities($_SESSION['user']['id'], ENT_QUOTES, 'UTF-8'); ?></div></div>
<div class="spacing"></div>
<div class="fl">
<!--AVATAR-->
<div class="avatarback">
<div class="avatar"><img src="/profile/avatar/default.jpg" width="180px"/></div>
</div>
<!--TITLES / SIGNS-->
<div class="signs"><br/>
<div class="title"><div class="fl">Title: </div>
<div class="fr"><?php print htmlentities($_SESSION['user']['title'], ENT_QUOTES, 'UTF-8'); ?> </div></div><!--TITLE-->
<div class="spacing"></div>
<div class="warnings"><div class="fl">Warnings: </div>
<div class="fr"><?php print htmlentities($_SESSION['user']['warnings'], ENT_QUOTES, 'UTF-8'); ?> </div></div><!--WARNINGS-->
<div class="spacing"></div>
<div class="joined"><div class="fl">Joined: </div>
<div class="fr"><?php print htmlentities($_SESSION['user']['timestamp'], ENT_QUOTES, 'UTF-8'); ?> </div></div><!--JOINED-->
</div>
</div>
<div class="padding"></div>
<!--BIO-->
<div class="bioback">
<?php if(empty($_SESSION['user']['bio'])){
print "This user has not set any bio yet...";
}else{
print htmlentities($_SESSION['user']['bio'], ENT_QUOTES, 'UTF-8');} ?>
</div>
<!--SETTINGS TAB-->
<div class="etc-back">
<div>Message</div>
<div class="spacing2"></div>
<div>Settings</div>
<div class="spacing2"></div>
<div>AdminPanel</div>
</div>
<br/>
<br/>
<div class="padding"></div>
<div class="email">
<div class="fl">E-mail:</div><div class="fr"><?php print htmlentities($_SESSION['user']['email'], ENT_QUOTES, 'UTF-8'); ?></div>
</div>
<div class="spacing"></div>
<div class="padding"></div>
<div class="email">
<div class="fl">Website:</div><div class="fr"><?php print "<a href='". htmlentities($_SESSION['user']['website'], ENT_QUOTES, 'UTF-8')."'>".htmlentities($_SESSION['user']['website'], ENT_QUOTES, 'UTF-8')."</a>"; ?></div>
</div>
<div class="spacing"></div>
<div class="padding"></div>
<div class="email">
<div class="fl">Clan Page:</div><div class="fr"><?php if(empty($_SESSION['user']['clanpage'])){
print "No Clan Page Found!";
}else{
print htmlentities($_SESSION['user']['clanpage'], ENT_QUOTES, 'UTF-8');} ?></div>
</div>
</div>
</body>
</html>
I'm trying to follow along with a tutorial for creating a login system for a website but some of the code isn't running. Signing up works fine and I can see the entries in my database, but logging in fails even if I have the correct details. Here is my login.php code:
<?php
// First we execute our common code to connection to the database and start the session
require("common.php");
// This variable will be used to re-display the user's username to them in the
// login form if they fail to enter the correct password. It is initialized here
// to an empty value, which will be shown if the user has not submitted the form.
$submitted_username = '';
// This if statement checks to determine whether the login form has been submitted
// If it has, then the login code is run, otherwise the form is displayed
if(!empty($_POST))
{
// This query retrieves the user's information from the database using
// their username.
$query = "SELECT id, username, password, salt, email FROM users WHERE username = :username";
// The parameter values
$query_params = array(
':username' => $_POST['username']
);
try
{
// Execute the query against the database
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
// This variable tells us whether the user has successfully logged in or not.
// We initialize it to false, assuming they have not.
// If we determine that they have entered the right details, then we switch it to true.
$login_ok = false;
// Retrieve the user data from the database. If $row is false, then the username
// they entered is not registered.
$row = $stmt->fetch();
if($row)
{
// Using the password submitted by the user and the salt stored in the database,
// we now check to see whether the passwords match by hashing the submitted password
// and comparing it to the hashed version already stored in the database.
$check_password = hash('sha256', $_POST['password'] . $row['salt']);
for($round = 0; $round < 65536; $round++)
{
print("Checking password");
$check_password = hash('sha256', $check_password . $row['salt']);
}
if($check_password === $row['password'])
{
// If they do, then we flip this to true
$login_ok = true;
}
}
// If the user logged in successfully, then we send them to the private members-only page
// Otherwise, we display a login failed message and show the login form again
if($login_ok)
{
// Here I am preparing to store the $row array into the $_SESSION by
// removing the salt and password values from it. Although $_SESSION is
// stored on the server-side, there is no reason to store sensitive values
// in it unless you have to. Thus, it is best practice to remove these
// sensitive values first.
unset($row['salt']);
unset($row['password']);
// This stores the user's data into the session at the index 'user'.
// We will check this index on the private members-only page to determine whether
// or not the user is logged in. We can also use it to retrieve
// the user's details.
$_SESSION['user'] = $row;
// Redirect the user to the private members-only page.
header("Location: secret.html");
die("Redirecting to: secret.html");
}
else
{
// Tell the user they failed
//print("Login Failed.");
// Show them their username again so all they have to do is enter a new
// password. The use of htmlentities prevents XSS attacks. You should
// always use htmlentities on user submitted values before displaying them
// to any users (including the user that submitted them). For more information:
// http://en.wikipedia.org/wiki/XSS_attack
$submitted_username = htmlentities($_POST['username'], ENT_QUOTES, 'UTF-8');
}
}
?>
<h1>Login</h1>
<form action="login.php" method="post">
Username:<br />
<input type="text" name="username" value="<?php echo $submitted_username; ?>" />
<br /><br />
Password:<br />
<input type="password" name="password" value="" />
<br /><br />
<input type="submit" value="Login" />
</form>
Register
Here is my register.php code:
<?php
// First we execute our common code to connection to the database and start the session
require("common.php");
// This if statement checks to determine whether the registration form has been submitted
// If it has, then the registration code is run, otherwise the form is displayed
if(!empty($_POST))
{
// Ensure that the user has entered a non-empty username
if(empty($_POST['username']))
{
// Note that die() is generally a terrible way of handling user errors
// like this. It is much better to display the error with the form
// and allow the user to correct their mistake. However, that is an
// exercise for you to implement yourself.
die("Please enter a username.");
}
// Ensure that the user has entered a non-empty password
if(empty($_POST['password']))
{
die("Please enter a password.");
}
// Make sure the user entered a valid E-Mail address
// filter_var is a useful PHP function for validating form input, see:
// http://us.php.net/manual/en/function.filter-var.php
// http://us.php.net/manual/en/filter.filters.php
if(!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL))
{
die("Invalid E-Mail Address");
}
// We will use this SQL query to see whether the username entered by the
// user is already in use. A SELECT query is used to retrieve data from the database.
// :username is a special token, we will substitute a real value in its place when
// we execute the query.
$query = "
SELECT
1
FROM users
WHERE
username = :username
";
// This contains the definitions for any special tokens that we place in
// our SQL query. In this case, we are defining a value for the token
// :username. It is possible to insert $_POST['username'] directly into
// your $query string; however doing so is very insecure and opens your
// code up to SQL injection exploits. Using tokens prevents this.
// For more information on SQL injections, see Wikipedia:
// http://en.wikipedia.org/wiki/SQL_Injection
$query_params = array(
':username' => $_POST['username']
);
try
{
// These two statements run the query against your database table.
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
// The fetch() method returns an array representing the "next" row from
// the selected results, or false if there are no more rows to fetch.
$row = $stmt->fetch();
// If a row was returned, then we know a matching username was found in
// the database already and we should not allow the user to continue.
if($row)
{
die("This username is already in use");
}
// Now we perform the same type of check for the email address, in order
// to ensure that it is unique.
$query = "
SELECT
1
FROM users
WHERE
email = :email
";
$query_params = array(
':email' => $_POST['email']
);
try
{
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex)
{
die("Failed to run query: " . $ex->getMessage());
}
$row = $stmt->fetch();
if($row)
{
die("This email address is already registered");
}
// An INSERT query is used to add new rows to a database table.
// Again, we are using special tokens (technically called parameters) to
// protect against SQL injection attacks.
$query = "
INSERT INTO users (
username,
password,
salt,
email
) VALUES (
:username,
:password,
:salt,
:email
)
";
// A salt is randomly generated here to protect again brute force attacks
// and rainbow table attacks. The following statement generates a hex
// representation of an 8 byte salt. Representing this in hex provides
// no additional security, but makes it easier for humans to read.
// For more information:
// http://en.wikipedia.org/wiki/Salt_%28cryptography%29
// http://en.wikipedia.org/wiki/Brute-force_attack
// http://en.wikipedia.org/wiki/Rainbow_table
$salt = dechex(mt_rand(0, 2147483647)) . dechex(mt_rand(0, 2147483647));
// This hashes the password with the salt so that it can be stored securely
// in your database. The output of this next statement is a 64 byte hex
// string representing the 32 byte sha256 hash of the password. The original
// password cannot be recovered from the hash. For more information:
// http://en.wikipedia.org/wiki/Cryptographic_hash_function
$password = hash('sha256', $_POST['password'] . $salt);
// Next we hash the hash value 65536 more times. The purpose of this is to
// protect against brute force attacks. Now an attacker must compute the hash 65537
// times for each guess they make against a password, whereas if the password
// were hashed only once the attacker would have been able to make 65537 different
// guesses in the same amount of time instead of only one.
for($round = 0; $round < 65536; $round++)
{
$password = hash('sha256', $password . $salt);
}
// Here we prepare our tokens for insertion into the SQL query. We do not
// store the original password; only the hashed version of it. We do store
// the salt (in its plaintext form; this is not a security risk).
$query_params = array(
':username' => $_POST['username'],
':password' => $password,
':salt' => $salt,
':email' => $_POST['email']
);
try
{
// Execute the query to create the user
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
// This redirects the user back to the login page after they register
header("Location: login.php");
// Calling die or exit after performing a redirect using the header function
// is critical. The rest of your PHP script will continue to execute and
// will be sent to the user if you do not die or exit.
die("Redirecting to login.php");
}
?>
<h1>Register</h1>
<form action="signup.php" method="post">
Username:<br />
<input type="text" name="username" value="" />
<br /><br />
E-Mail:<br />
<input type="text" name="email" value="" />
<br /><br />
Password:<br />
<input type="password" name="password" value="" />
<br /><br />
<input type="submit" value="Register" />
</form>
I have managed to narrow the problem down to:
$row = $stmt->fetch();
if($row)
{
// Using the password submitted by the user and the salt stored in the database,
// we now check to see whether the passwords match by hashing the submitted password
// and comparing it to the hashed version already stored in the database.
$check_password = hash('sha256', $_POST['password'] . $row['salt']);
for($round = 0; $round < 65536; $round++)
{
print("Checking password");
$check_password = hash('sha256', $check_password . $row['salt']);
}
if($check_password === $row['password'])
{
// If they do, then we flip this to true
$login_ok = true;
}
}
as the "print("checking password");" never happens. Can anyone spot a problem in the code? Thank you in advance for your help.
I realised what my problem was... I was attempting to login with the email instead of the username! Thank you all for your help.
I am new to PHP and MYSQL and am working on a registration/login form project to build up my knowledge but I am a bit stuck, so hope you can help.
I have a database on PHPMyAdmin and my registration form searches to see if the email address already exists if not to insert all the information into the database. This works fine. I also have a login form which searched for the email address and password to see if they matched any in the database, if so to log in. This worked fine.
My issue came when I started to learn about password salts/hashing. I can still register okay but when I try to login with details already in the database it doesn't seem to match the passwords up to allow me to log in.
register.php
<?php
error_reporting(E_ALL);
require 'db_connect.php';
// Stores the information submitted from the form via the $_POST variable
// if the request method in the form is POST then execute the following code (read the submitted information - send the email and redirect to the header location
// if it is NOT POST then it will skip this code block and show blank contact form
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$fname = trim($_POST["fname"]);
$lname = trim($_POST["lname"]);
$cname = trim($_POST["cname"]);
$email = trim($_POST["email"]);
$pass1 = trim($_POST["pass1"]);
$pass2 = trim($_POST["pass2"]);
// VALIDATIONS
// All required fields must be entered
if ($fname == "" OR $lname == "" OR $email == "" OR $pass1 == "" OR $pass2 == "") {
$error_message = "You must fill in all the required fields.";
}
// password must contain 6 characters min
if (strlen($pass1) < 6) {
$error_message = "Password must be at least 6 characters long";
}
//passwords must match each other
if ($pass1 != $pass2) {
$error_message = "Passwords do not match";
}
// hash and salt password - PASSWORD_DEFAULT uses the php default hashing algorithm -
// cost is the expense used to generate the hash (higher the number the more secure but slower the page load)
$password_save = password_hash($pass1 . SALT , PASSWORD_DEFAULT, array('cost' => 10 ));
// if there's not a previous error message run a database query to look if the email address entered matches any already in the database.
if (!isset ($error_message)){
$query = "SELECT * FROM registration_tbl WHERE email = '".$email."'";
$query_run = mysqli_query($dbc, $query);
// if the query locates more than 0 (i.e 1+) records with matching email addresses then echo out the error
// else insert all new form data in to the database and echo a success message
if (mysqli_num_rows($query_run)>0) {
$error_message = "Email Address ".$email." is already registered";
} else {
$sql = "INSERT INTO registration_tbl (first_name,last_name,company_name,email,password,reg_datetime) VALUES ('".$fname."','".$lname."','".$cname."','".$email."','".$password_save."', NOW())";
$query_run = mysqli_query($dbc, $sql);
echo "Registration Successful";
}
}
login.php
<?php
error_reporting(E_ALL);
require 'db_connect.php';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$email = trim($_POST["email"]);
$pass = trim($_POST["pass"]);
// VALIDATIONS
// both fields must be entered to log in
if($email == "" OR $pass == "") {
$error_message = "Both fields must be completed ";
}
$hashandsalt = password_hash($pass . SALT, PASSWORD_DEFAULT, array('cost' => 10 ));
// if no error message is set - send a query to the database for all records in the registration_tbl where the email matches one in the database
// if the query returns less than 1 record (i.e no matches in the database) show error message
// else if found in the database save the login details to session variables and then
// redirect to the logged-in.php page
if (!isset ($error_message)) {
$query = "SELECT * FROM registration_tbl WHERE email ='".$email."'";
$query_run = mysqli_query($dbc, $query);
if (mysqli_num_rows($query_run)<1 ){
$error_message = "Your login details do not match, please double check and try again1";
} else {
while($row = mysqli_fetch_array($query_run)) {
echo ($row['password']) ."<br>";
echo $pass ."<br>";
echo $hashandsalt;
if (password_verify($pass, $hashandsalt)){
$_SESSION['firstname'] = $row['first_name'];
$_SESSION['lastname'] = $row['last_name'];
$_SESSION['email'] = $row['email'];
$_SESSION['password'] = $row['password'];
$_SESSION['id'] = $row['ID'];
header("location: logged-in.php");
} else {
$error_message = "Your login details do not match, please double check and try again";
}
}
}
}
}
?>
<div class="wrapper">
<?php
if(!isset ($error_message)) {
echo '<p>Please complete the log in details </p>';
} else {
echo $error_message;
}
?>
<form method="post" action="login.php">
<table>
<tr>
<th>
<label for="email"> Email Address </label>
</th>
<td>
<input type="email" name="email" id="email" value="<?php if(isset($email)) { echo htmlspecialchars($email); } ?>">
</td>
</tr>
<tr>
<th>
<label for="pass"> Password </label>
</th>
<td>
<input type="password" name="pass" id="pass">
</td>
</tr>
</table>
<input type="submit" value="Log in">
</form>
The results of the 3 echos I have in the login.php
echo ($row['password']) ."<br>";
This one will show the hashed password from the database
echo $pass ."<br>";
This one will show whatever password is entered
echo $hashandsalt;
This one shows a new hashed password which differs each time the page is refreshed
This is where my query is I am obviously missing something which is not allowing the password entered to match up to the already stored hashed password.
I have scoured the internet including number of stack overflow posts but I can't quite seem to figure out what I have done wrong. This is my first post so I hope I am posting enough information for you.
Any ideas guys?
p.s I know I need to add the mysqli_real_escape_string - this was my next job after figuring this one out :ve
To verify the password you need to check with the stored password-hash from the database. There is no need to call password_hash() in login.php.
login.php
if (password_verify($pass, $row['password']))
Also there is no need to add a salt before hashing, the function password_hash() will add it automatically.
register.php
$password_save = password_hash($pass1, PASSWORD_DEFAULT, array('cost' => 10 ));