I'm trying to make phpass work by using this article: https://sunnyis.me/blog/secure-passwords
When I create a new user there is no problem. Everything uploads to the database and i get a salted hash that looks something like this:
$2a$08$5i8TUw7Ego09blkDF6Fv.OVGyKMZjLJ7HzSfRZpX62EbsrcxhLbKK
But the problem is the verification when trying to log in to the account.
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL ^ E_NOTICE);
require("passwordhash.php");
if (isset($_POST['submit'])){
if(!empty($_POST['email']) && !empty($_POST['pass'])) {
$email=($_POST['email']);
$password = $_POST["pass"];
/* Getting the correct matching email account for login */
$query="SELECT * FROM user WHERE email='$email'";
$result = $mysqli->query($query);
$numrows=mysqli_num_rows($result);
if($numrows!=0){
if (strlen($password) < 72) {
/* Here is the code for retreiving the hash from database using SELECT and then trying to match it with the $password earlier in the code */
$hasher = new PasswordHash(8, false);
$stored_hash = "*";
$query = "SELECT pass FROM user WHERE email='$email'";
if($row = $result->fetch_array()) {
$stored_hash = $row['pass'];
}
$check = $hasher->CheckPassword($password, $stored_hash);
/* at this if statement it never goes through */
if ($check) {
$_SESSION['email'] = $_POST['email'];
header('Location: '. $_SERVER["REQUEST_URI"]);
die;
}
/* It always ends up in this else statement*/
else {
$feedback="Invalid username or password! $stored_hash $password";
}
} else {
$feedback="Password must be 72 characters or less";
}
} else {
$feedback="Invalid username or password!";
}
} else {
$feedback="All fields are required!";
}
}
echo "$feedback";
?>
At the else statement where it always ends up I also tried to see what the variables I sent in to the function contained. $passwords contains whatever I write in as a password in the field and $stored_hash retrieves the fully hashed password from the database as intended.
This is the function:
function CheckPassword($password, $stored_hash)
{
$hash = $this->crypt_private($password, $stored_hash);
if ($hash[0] == '*')
$hash = crypt($password, $stored_hash);
return $hash == $stored_hash;
}
So my problem is that the something isn't working correctly since the variable $check returns an empty string. Is it possible that there is something wrong with my PHP version (5.3.23) or is there something wrong with the code?
Related
The following code should be straight forward and simple, the insert into the db on signup creates a hash, but later when I try to login with the same password the hash it is creating isn't matching up to what is in the database (I had print_r's throughout to verify). Can someone see if I'm just overlooking something dumb?
session_start();
require_once("login.php");
$error = "";
$email = "";
$password = "";
if (isset($_GET['logout'])) {
unset($_SESSION['id']);
setcookie('id', '', time() - 60*60);
$_COOKIE['id'] = "";
} else {
if (isset($_SESSION['id']) or isset($_COOKIE['id'])) {
header("Location: loggedinpage.php");
}
}
if (isset($_POST["submit"])) {
$link = mysqli_connect($hn, $un,$pw,$db);
if($link->connect_error) die("Fatal Errror.");
if (!$_POST["email"]) {
$error .="An email address is required<br>";
}
if (!$_POST["password"]) {
$error .="A password address is required<br>";
}
if ($error != "") {
$error= "<p>There were error(s) in your form:</p>".$error;
} else {
if ($_POST['signup'] == 1) {
$email = mysqli_real_escape_string($link, $_POST['email']);
$password = mysqli_real_escape_string($link,$_POST['password']);
$query = "SELECT id FROM `users` WHERE email = '".$email."' LIMIT 1";
$result=$link->query($query);
if (mysqli_num_rows($result) > 0) {
$error = "That email address is taken.";
} else {
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$query = "INSERT INTO `users`(`email`,`password`) VALUES ('".$email."', '".$hashedPassword."')";
if (!mysqli_query($link,$query)) {
$error = "<p>Could not sign you up, please try again later</p>";
} else {
$_SESSION['id'] = mysqli_insert_id($link);
if(isset($_POST['stayLoggedIn']) and $_POST['stayLoggedIn'] == 1) {
setcookie('id', mysqli_insert_id($link), time()+60*60*24);
}
header("Location: loggedinpage.php");
}
}
} else {
$email = mysqli_real_escape_string($link, $_POST['email']);
$password = mysqli_real_escape_string($link, $_POST['password']);
$hashedPassword = password_hash($password,PASSWORD_DEFAULT);
$query = "SELECT * FROM users WHERE email = '".$email."' LIMIT 1";
$result = $link->query($query);
if ($result->num_rows > 0) {
$row = $result->fetch_array(MYSQLI_ASSOC);
if ($email == $row['email'] and password_verify($password,$row['password'])) {
if (isset($_POST['stayLoggedIn']) and $_POST['stayLoggedIn'] == 1) {
setcookie('id', $row['id'], time()+60*60*24);
header("Location: loggedinpage.php");
}
} else {
$error = "Incorrect Username/Password combination";
}
}
}
}
}
Although it's tucked away at the end of a paragraph, PHP documentation does say that "it is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice)."
The current default algorithm, bcrypt, generates hashes that are 60 characters long. If your database column cannot hold at least this many characters, your hashes will be truncated and verification will fail.
You've got a few other problems as well:
You're modifying the password before generating the hash (with mysqli_real_escape_string())
You're not using prepared statements
You appear to be relying on cookies for authentication. Cookies are user-generated data, they are not to be trusted! This is why PHP provides session support, because the data is stored on the server.
You should not be checking for an existing email address using a query, instead you should have a unique index set on the email column in the database.
try
if(password_verify($password, (string)$row->password)){
//Your Code
}
because of password_verify function return Boolean only true or false
And
$hashedPassword = password_hash($password,PASSWORD_DEFAULT);
Only add once when you Insert to Sql (new user)
I'm trying to make a login system (already have registration system complete) with password_hash() and password_verify() but it isn't working for me. I've been trying to find an answer this whole morning and yet can't seem to make it work, I've watched codecourse tutorials, read blog posts and nothing. The tutorial I've been following the most is this one.
<!-- login -->
<?php
if($_SERVER['REQUEST_METHOD'] == "POST") {
$errors = array();
error_reporting(E_ALL);
ini_set('display_errors', 1);
//Basic validation
if(empty($_POST['username'])){
$errors[] = "Please enter your username";
}else{
$username = $mysqli->real_escape_string($_POST['username']);
}
if(empty($_POST['password'])){
$errors[] = "Please enter your password";
}else{
$password = trim($_POST['password']);
}
if (empty($errors)) {
$sql = "SELECT * FROM users WHERE username = '$username'";
$result = $mysqli->query($sql);
if ($result->num_rows === 1) {
$row = $result->fetch_array(MYSQLI_ASSOC);
if(password_verify($password, $row['password'])) {
echo 'test';
$_SESSION['user']['user_id'] = $row['user'];
header("Location: google.com");
exit();
}else{
$errors[] = "The username or password do not match";
}
}else{
$errors[] = "The username or password do not match";
}
}
}
?>
<!-- register -->
<?php
if($_SERVER['REQUEST_METHOD'] == "POST") {
$username = mysqli_real_escape_string($conn, $_POST['username']);
$password = $_POST['password'];
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$confirm_password = mysqli_real_escape_string($conn, $password);
$ip = $_SERVER['REMOTE_ADDR'];
if(empty($username) || empty($password) || empty($confirm_password)) {
$error = "Fill in the missing fields";
} else {
$sql = "INSERT INTO users VALUES('', '$username', '$hashed_password', '$ip', 'User')";
if($conn->query($sql) === TRUE) {
$error = "Your account has been created.";
} else {
$error = "Your account has not been created, please try again later.";
}
}
}
?>
The end result product is supposed to login in successfully and redirect, I'm using PHP 5.6 and running on localhost XAMPP.
You'll never get a match because you're using
$password =mysqli_real_escape_string($conn, $_POST['password']);
to store the password and then not using it on verification. Instead you use
$password = trim($_POST['password']);
Make sure you don't escape passwords or use any other cleansing mechanism on them before hashing. Doing so changes the password and causes unnecessary additional coding. The password_hash() function can generate some very lengthy text (the current default is 60 characters), so make sure the field in your database is large enough to accommodate the hash. Setting the field larger now will allow for the length needed. The PHP team is adding more algorithms to the method which means the hash can and will grow.
I followed a tutorial on youtube on how to encrypt users password using the encrypt blowfish function. I have implemented it properly into my registration script, with it successfully registering an account and sending the encrypted password to the database. My issue though, is retrieving that encrypted password when trying to log a user in. When I try and login to an existing user, it gets down to that last else statement saying it doesn't exist, meaning the hashed password isn't being recognized.
Code for encrypt password function:
public function encryptPass($password, $rounds = 11)
{
$salt = "";
// creates array of capital letters A-Z & lowercase as well as #'s 0-9
$saltChars = array_merge(range('A', 'Z'), range('a', 'z'), range(0,9));
for($i = 0; $i < 22; $i++)
{
// randomize the array
$salt .= $saltChars[array_rand($saltChars)];
}
return crypt($password, sprintf('$2y$%02d$', $rounds) . $salt);
}
Code used to register an account:
/// REGISTER ACCOUNT ///
if(isset($_POST['register']))
{
// clean up the fields
$username = mysql_real_escape_string(trim($_POST['username']));
$emailid = mysql_real_escape_string(trim($_POST['emailid']));
$password = mysql_real_escape_string(trim($_POST['password']));
$confirmPassword = mysql_real_escape_string(trim($_POST['confirm_password']));
if($password == $confirmPassword)
{
$iUe = $dbMan->ifUsernameExist($username);
$iEe = $dbMan->ifEmailExist($emailid);
// if username and email don't already exist, continue with registration
if(!$iUe && !$iEe)
{
// encrypt the users password
$hashedPassword = $dbMan->encryptPass($password);
echo "$password <br> \n";
// register the account
$register = $dbMan->UserRegister($username, $emailid, $hashedPassword);
// if registration was succesful
if($register)
{
echo "<script>alert('Registration Successful')</script>";
}
else
{
echo "<script>alert('Registration Not Successful')</script>";
}
}
else
{
echo "<script>alert(' That email or username already exists! ')</script>";
}
}
else
{
echo "<script>alert(' Passwords do not match! ')</script>";
}
}
Code used for the login:
/// LOGIN ACCOUNT ///
if(isset($_POST['login']))
{
// 'convert' post variables to session variables
$_SESSION['username'] = $_POST['username'];
$_SESSION['password'] = $_POST['password'];
// clean em up, get rid of any white spaces or sql injection special chars
$username = mysql_real_escape_string(trim($_SESSION['username']));
$password = mysql_real_escape_string($dbMan->encryptPass(trim($_SESSION['password'])));
echo "$password<br>\n";
$user = $dbMan->Login($username, $password);
// if theres an acccount with that username/pw in the db
if ($user)
{
// login successful
header("location:index.php");
}
else
{
// Registration Failed
echo "<script>alert(' The email or password do not match! ')</script>";
}
}
Code for dbManager:
<?php
require_once 'dbConnect.php';
//session_start();
class dbManager
{
function __construct()
{
// connecting to database
$db = new dbConnect();
}
// destructor
function __destruct()
{
}
public function UserRegister($username, $emailid, $password)
{
$query = mysql_query("INSERT INTO users(username, emailid, password) values('".$username."','".$emailid."','".$password."')") or die(mysql_error());
return $query;
}
public function Login($username, $password)
{
$query = mysql_query("SELECT * FROM users WHERE username = '".$username."' AND password = '".$password."'");
$user_data = mysql_fetch_array($query);
//print_r($user_data);
$num_rows = mysql_num_rows($query);
if ($num_rows == 1)
{
$_SESSION['login'] = true;
$_SESSION['uid'] = $user_data['id'];
$_SESSION['username'] = $user_data['username'];
$_SESSION['emailid'] = $user_data['emailid'];
return TRUE;
}
else
{
return FALSE;
}
}
// check if username exists in db
public function ifUsernameExist($username)
{
$qr = mysql_query("SELECT * FROM users WHERE username = '".$username."'");
echo $row = mysql_num_rows($qr);
if($row > 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
// check if email exists in db
public function ifEmailExist($emailid)
{
$qr = mysql_query("SELECT * FROM users WHERE emailid = '".$emailid."'");
echo $row = mysql_num_rows($qr);
if($row > 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
// encrypt password
public function encryptPass($password, $rounds = 11)
{
$salt = "";
// creates array of capital letters A-Z & lowercase as well as #'s 0-9
$saltChars = array_merge(range('A', 'Z'), range('a', 'z'), range(0,9));
for($i = 0; $i < 22; $i++)
{
// randomize the array
$salt .= $saltChars[array_rand($saltChars)];
}
return crypt($password, sprintf('$2y$%02d$', $rounds) . $salt);
}
}
?>
Note: both the login and register 'methods' are in the same php file including the form markup. The encrypted function is located in a different file called dbManager.
Hopefully I provide enough information for someone to point me in the right direction. Any help is appreciated!
Thanks, Dev.
You need to pass your cleartext password to encrypt to compare it within the database.
change
$password = trim(mysql_real_escape_string($_SESSION['password']));
to
$password = $dbMan->encryptPass(trim(mysql_real_escape_string($_SESSION['password'])));
In your login action.
Ideally you would run the $dbMan->encryptPass before doing mysql_real_escape_string both on INSERTand SELECT.
$password = mysql_real_escape_string($dbMan->encryptPass(trim($_SESSION['password'])));
The salts have to be the same for encrypt and decrypt, seeing as you are using array_rand the salts are different on each pass. You have to store the salt someplace else. If you remove the salt or set it to a constant it will work now.
I am using PHPASS to store password encrypted and compare when login.
here is the code
ob_start();
$userName = $password = "";
$userNameErr = $passwordErr = $loginErr = "";
$hasher = new PasswordHash(8, false);
if (isset($_POST['subEmployee'])) {
if (empty($_POST['user_name'])) {
$userNameErr = "User name is required";
} else {
$userName = check_input($_POST['user_name']);
if (!preg_match("/^[0-9_a-zA-Z]*$/", $userName)) {
$userNameErr = "Only letters, numbers and '_' allowed";
}
}
if (empty($_POST['password'])) {
$passwordErr = "Password is required";
}else{
$password = check_input($_POST['password']);
}
$active = 1;
$loginUser = $db->prepare("SELECT password FROM users WHERE user_name=? AND activity=?");
$loginUser->bind_param('si', $userName, $active);
if ($loginUser->execute()) {
$results = $loginUser->get_result();
if ($results->num_rows == 1) {
$row = $results->fetch_object();
$stored_hash = "*";
$stored_hash = $row->password;
$check = $hasher->CheckPassword($password, $stored_hash);
if ($check) {
$_SESSION['name'] = $row->first_name;
$_SESSION['userId'] = $row->id;
$_SESSION['user'] = 1;
print_r($_SESSION);
header("Location:?pid=4");
} elseif (!empty($_POST['user_name']) && !empty($_POST['password'])) {
$loginErr = "'Invalid Login Information'";
}
}
}
}
so far it always give the same message 'Invalid Login Information' I have made the registration form that store my password like this.
$hasher = new PasswordHash(8, false);
$hash = md5(rand(0, 1000));
if (empty($_POST['password'])) {
$error ['passwordErr'] = "Password is required";
} elseif (strlen($_POST['password']) < 8) {
$error ['passwordErr'] = "<span class='notAllowed'>Chose password with at last eight characters</span>";
} elseif (strlen($_POST['password']) > 72) {
$error ['passwordErr'] = "<span class='notAllowed'>Password max 72 characters</span>";
} elseif ($_POST['password'] !== $_POST['confirm']) {
$error ['passwordErr'] = "Password don't matching";
} else {
$password = $hasher->HashPassword($password);
}
when I checked my database the password seems hashed to me and the user name is there and everything is alright
but still getting this message as 'Invalid Login Information'.
does this two lines is right
$loginUser = $db->prepare("SELECT password FROM users WHERE user_name=? AND activity=?");
$loginUser->bind_param('si', $userName, $active);
does the login code OK.
I try this too
Update
I updated my code
if (isset($_POST['subEmployee'])) {
$error=array();
$hash_cost_log2 = 8;
$hash_portable = FALSE;
$hasher = new PasswordHash($hash_cost_log2, $hash_portable);
if (empty($_POST['user_name'])) {
$userNameErr = "User name is required";
} else {
$userName = check_input($_POST['user_name']);
if (!preg_match("/^[0-9_a-zA-Z]*$/", $userName)) {
$userNameErr = "Only letters, numbers and '_' allowed";
}
}
if (empty($_POST['password'])) {
$passwordErr = "Password is required";
} else {
$password = $_POST['password'];
}
$active = 1;
$loginUser = $db->prepare("SELECT password FROM hired_person_info WHERE user_name=? AND activity=?");
$loginUser->bind_param('si', $userName, $active);
if ($loginUser->execute()) {
$results = $loginUser->get_result();
if ($results->num_rows == 1) {
$row = $results->fetch_object();
$stored_hash = "*";
$stored_hash = $row->password;
$check = $hasher->CheckPassword($password, $stored_hash);
if ($check) {
$_SESSION['name'] = $row->first_name;
$_SESSION['userId'] = $row->id;
$_SESSION['user'] = 1;
print_r($_SESSION);
header("Location:?pid=4");
} elseif (!empty($_POST['user_name']) && !empty($_POST['password'])) {
$loginErr = "'Invalid Login Information'";
}
} else {
$loginErr = "'We didn't find any users'";
}
}
}
add this from the manual of PHPass
$hash_cost_log2 = 8;
$hash_portable = FALSE;
$hasher = new PasswordHash($hash_cost_log2, $hash_portable);
still no luck can somebody tell me where am mistaking here
Edit
this is my check_input() code
function check_input($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
and I am using PHP 5.3.29
Thanks
These are some points i would check:
1) In your registration handler you check directly the POST variable, but for hashing you take a variable $password, i would access the input always in the same way, for example:
$password = $hasher->HashPassword($_POST['password']);
2) The function check_input() is not recommended for passwords, since you calculate a hash-value and this hash-value is "safe" anyway. Even for other user input, one should validate it as you did, but escaping should be done as late as possible, and only for the particular output. So the function htmlspecialchars() should not be called for user input, but always before outputting to HTML.
3) In your login handler you access the password once with the POST variable and once with the variable $password. The variable $password is set only in an if statement, so if the input is empty you fill the error but you continue with an uninitialized $password variable. Either fill the variable just at the beginning, or always use the POST variable.
4) Since you are using PHP 5.3.29 you can use the new function password_hash() with the compatibility pack. I do not think that the PHPass library is the problem here, nevertheless here is an example for the new function.
// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);
5) Another often made mistake is, that the database field for storing the hash-value is too short, it needs a length of varchar(60). Maybe you could provide one of your password-hashes (of course only an example)?
This library requires PHP >= 5.3.7 OR a version that has the $2y fix backported into it (such as RedHat provides).
I have successfully used Phpass to hash registered users passwords and store them in a database, now i am stuck on the login how to check the sumbitted username and password, checking the username exists in the database then checking the hashed password against the one given.
Any help much appreciated!!! Thankyou!
This is my code:
<?php
// Inialize session
session_start();
// Include database connection settings
include('config.inc');
require("PasswordHash.php");
$hasher = new PasswordHash(8, false);
$username = $_POST['username'];
$password = $_POST['password'];
// Passwords should never be longer than 72 characters to prevent DoS attacks
if (strlen($password) > 72) { die("Password must be 72 characters or less"); }
$query = "SELECT * FROM user WHERE username = '$username'";
$query = mysql_query($query);
$numrows = mysql_num_rows($query);
if ($numrows = 1) {
$res = mysql_query("SELECT password FROM user WHERE username = '$username'");
$row = mysql_fetch_array($res);
$hash = $row['password'];
$password = $_POST['password'];
if ($hasher->CheckPassword($password, $hash)) { //$hash is the hash retrieved from the DB
$what = 'Authentication succeeded';
} else {
$what = 'Authentication failed';
}
} else {
echo "No Such User";
include 'login.php';
exit();
}
echo "$what\n";
echo "<br />";
echo "$hash";
?>
THIS IS MY WORKING CODE FOR BENEFIT OF OTHERS:
<?php
// Inialize session
session_start();
// Include database connection settings
include('config.inc');
require("PasswordHash.php");
$hasher = new PasswordHash(8, false);
$username = $_POST['username'];
$password = $_POST['password'];
// Passwords should never be longer than 72 characters to prevent DoS attacks
if (strlen($password) > 72) { die("Password must be 72 characters or less"); }
$query = "SELECT * FROM user WHERE username = '$username'";
$query = mysql_query($query);
$numrows = mysql_num_rows($query);
if ($numrows = 1) {
$res = mysql_query("SELECT * FROM user WHERE username = '$username'");
$row = mysql_fetch_array($res);
$hash = $row['password'];
$password = $_POST['password'];
if ($hasher->CheckPassword($password, $hash)) { //$hash is the hash retrieved from the DB
$what = 'Authentication succeeded';
} else {
$what = 'Authentication failed';
}
} else {
echo "No Such User";
include 'login.php';
exit();
}
echo "$what\n";
echo "<br />";
echo "$hash";
?>
Here's how phpass works: When you save the user's password (when they create it) you hash it before saving, like so:
$hash_iterations = 30;
$portable_hashes = FALSE;
$hasher = new PasswordHash($hash_iterations, $portable_hashes);
$hash_value = $hasher->HashPassword($actual_password);
Then save $hash_value in the database as the user's password. When you go to validate the user, look up the user by username. If found, compare the actual password from the database (stored hash) with a hash of what the user entered:
// $stored_hash is the value you saved in the database for this user's password
// $user_input is the POST data from the user with the actual password
$valid_password = $hasher->CheckPassword($user_input, $stored_hash);
Make sure to initialize the PasswordHash class the same way each time, with the same values for $hash_iterations and $portable_hashes, or the comparison won't work correctly.