password_verify() against MySQL doesn't work - php

I store the password, get through a form, into MySQL database via PDO, after having hashed it with password_hash()
(VARCHAR(512) field)
$options = array(
'cost' => 12
);
$password = password_hash($password, PASSWORD_BCRYPT, $options);
Suppose that
$pass = "123Azerty";
//and the hash is
$hash = "$2y$12$TzpGzy1cKM81pkEr/Mn0SOVA4wn0lr.7PnKFg4SU9Hto0EUiGGRMe";
When I get the password from the database and I verify it with password_verify() it returns always false
...
...
$returnedPWD = $row['password'];
if (password_verify($pass,$returnedPWD)){
echo "TRUE";
} else {
echo "FALSE";
}
...
...
At this point, I tried to do it "manually" in this way
$pass = "123Azerty";
$hash = "$2y$12$TzpGzy1cKM81pkEr/Mn0SOVA4wn0lr.7PnKFg4SU9Hto0EUiGGRMe";
if (password_verify($pass,$hash )){
echo "TRUE";
} else {
echo "FALSE";
}
And it always returned FALSE
BUT
when I changed
$hash = "$2y$12$TzpGzy1cKM81pkEr/Mn0SOVA4wn0lr.7PnKFg4SU9Hto0EUiGGRMe";
// into
$hash = '$2y$12$TzpGzy1cKM81pkEr/Mn0SOVA4wn0lr.7PnKFg4SU9Hto0EUiGGRMe';
it worked. Because the hash enclosed in single quotes, is not parsable.
On my understanding, it means that the hash taken from database, it is interpreted as parsable (double totes) than it doesn't work at all
then I tried to enclose the string out the db int strval():
...
...
$returnedPWD = strval($row['password']);
if (password_verify($pass,$returnedPWD)){
echo "TRUE";
} else {
echo "FALSE";
}
...
...
But it returns always FALSE
reading all the posts related the not functioning of password_verify(), I didn't come up to any valid solution, for me.
Please is there a way to make it work?
Thanks in advance
EDIT 1
I did try with other settings as PASSWORD_DEFAULT but no changes.
I also tried to base64 encode it upfront database storage, then decode it. But nothing changed
EDIT 2
I store the data using PDO with parameters
$query = "INSERT INTO `users` (username, password) VALUES (:username, :password)";
$params = array(
':username' => "$username",
':password' => "$password" // hashed one
);
EDIT 3
Table Structure
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(512) NOT NULL,
`enabled` int(1) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=13 ;
$username = trim($_POST["username"];
$password = trim($_POST["password"];
// Query to insert data
$query = "INSERT INTO `users` (username, password, enabled) VALUES (:username, :password, 1)";
// The very original setup I did use and didn't work out
$param_password = password_hash($password, PASSWORD_DEFAULT);
// Bind parameters
$params = array(
':username' => "$username",
':password' => "$param_password"
);
...
...
// insert into db
$sth = $sql->prepare($query);
// Call MySQL
try {
$sth->execute($params); // Execute the Query
} catch (PDOException $e) {
$mysql_error = 'MySQL connection failed: ' . "<br>" . $e->getMessage();
}
...
...
and here the fundamental lines on how I read from database
...
...
$username_login = trim($_POST["username"]);
$password_login = trim($_POST["password"]);
...
...
$query = "SELECT * FROM `users` WHERE username = :username";
$params = array(
':username' => $username_login
);
$sth = $sql->prepare($query);
try{
$sth->execute($params);
} catch (PDOException $e) {
$mysql_error = 'MySQL connection failed: ' . "<br>" . $e->getMessage();
}
while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
foreach ($row as $key => $value) {
global $$key;
$$key = $value;
}
}
...
...
if (password_verify($password_login, $password) and $enabled == 1){
// Password is correct, so start a new session
session_start();
// Store data in session variables
$_SESSION["logged"] = true;
$_SESSION["id"] = $id;
$_SESSION["uname"] = $username_login_;
// Redirect user to welcome page
header("location: index.php");
} else {
// Display an error message if password is not valid
$password_login__err = "The password you entered was not valid. Or you are not enabled";
}
...
...

Your code does not work because of "$password " - it has space at the end. It should be:
$query = "INSERT INTO `users` (username, password) VALUES (:username, :password)";
$params = array(
':username' => $username,
':password' => $password, // hashed one
);
Your manual test does not work because
$hash = "$2y$12$TzpGzy1cKM81pkEr/Mn0SOVA4wn0lr.7PnKFg4SU9Hto0EUiGGRMe";
has double quotes and it interpolates $2y, $12 and $Tz... as variables that leads to empty string. That's why single quotes works.
From your provided information I constructed sample code that does work: Check here

Related

Problems php and MySQL

I have 2 tables, user log that is where the people make a register with their email and password, and another table with the name user where the people make complete their own profile.
How can I make with an SQL query to insert the data that insert in the form?
Taking into account that table user makes reference with table user log with the id...
I mean
User log
Id
Email
Password
User
Id
User_id fk id reference userlog(id)
Name
Surname
This is the code wich i made the log in
<?php
session_start();
if (isset($_SESSION['usuario'])) {
header('Location: index.php');
}
$errores = '';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$email = filter_var(strtolower($_POST['email']), FILTER_SANITIZE_STRING);
$password = $_POST['password'];
$password = hash('sha512', $password);
try {
$conexion = new PDO('mysql:host=localhost;dbname=DATABASE, 'USER', 'PASS');
} catch (PDOException $e) {
echo "Error:" . $e->getMessage();;
}
$statement = $conexion->prepare('
SELECT * FROM userlog WHERE email = :email AND password = :password'
);
$statement->execute(array(
':email' => $email,
':password' => $password
));
$resultado = $statement->fetch();
if ($resultado !== false) {
$_SESSION['usuario'] = $resultado;
header('Location: index.php');
} else {
$errores .= '<li>Datos Incorrectos</li>';
}
}
I make a var_dump() to see what the array in $resultado bring, and it brign me the data of the table, but, when I want to use the data to fill an input it fails
If your data will be coming from POST method, please always use precautions to avoid SQL injection..
I will be using a very elementary example. You can enhance this one for your own use.
$servername = "localhost";
$username = "yourUser";
$password = "yourPass";
$dbname = "youtDB";
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$sql = "INSERT INTO Userlog (email, password)
VALUES ('some_email#example.com', 'some_safe_password')";
$conn->exec($sql);
$last_id = $conn->lastInsertId();
$userSql = "INSERT INTO Userlog (userId, name, lastName) VALUES ($last_id, 'some_name', 'some_lastName')";
$conn->exec($userSql);
}
catch(PDOException $e)
{
echo $sql . "<br>" . $e->getMessage();
}
You can pass your data by using post method.
try this code.
$sql = "INSERT INTO users (Userlog, Id, Email, Password)
VALUES ('".$_POST["Userlog"]."','".$_POST["Id"]."','".$_POST["Email"].",'".$_POST["Password"]."')";

PHP Select Statement - Errors

I am student and I work on a noteapp. I made 2 methods in my DB-Handler within my Login-Mask for users to Login or Register.
<?php
class DBHandler
{
var $hostname;
var $user;
var $pw;
var $db;
var $connection;
function connectToDB($hostname,$user,$pw,$db){
$this->hostname = $hostname;
$this->user = $user;
$this->pw = $pw;
$this->db = $db;
$this->connection = new mysqli($this->hostname,$this->user,$this->pw,$this->db);
if ($this->connection->connect_error) {
die('Failed to connect' . $this->connection->connect_error);
}
$this->ensureNotesTable();
$this->ensureUsersTable();
}
function ensureUsersTable(){
assert($this->connection);
$queryCreate = "CREATE TABLE IF NOT EXISTS users(id INT(5) PRIMARY KEY AUTO_INCREMENT, username VARCHAR(100) NOT NULL, password VARCHAR(100) NOT NULL)";
$this->connection->query($queryCreate);
}
function ensureNotesTable(){
assert($this->connection);
$queryCreate = "CREATE TABLE IF NOT EXISTS notesnew(id INT(5) PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, content VARCHAR(100) NOT NULL, userid INT(5) NOT NULL)";
$this->connection->query($queryCreate);
}
function ensureUsername($username,$password){
assert($this->connection);
echo $username;
$query = "SELECT * FROM users WHERE username = (?)";
$statement = $this->connection->prepare($query);
$statement->bind_param('s',$username);
$results = $statement->execute();
echo $this->connection->errno.'-'.$this->connection->error;
if(mysqli_num_rows($results) >= 1){
echo $this->connection->errno.'-'.$this->connection->error;
echo "Username already exists";
} echo "Username is free!";
$this->addUser($username,$password);
}
function addUser($username,$password){
assert($this->connection);
$queryCreate = "INSERT INTO users(username, password) VALUES (?,?)";
$statement = $this->connection->prepare($queryCreate);
$statement->bind_param('ss', $username, $password);
return $statement->execute();
echo "You have been registered!";
}
function getUserId($username){
assert($this->connection);
$queryCreate = "SELECT id FROM users WHERE username = $username";
$row = $this->connection->query($queryCreate);
$userid = mysqli_fetch_row($row);
return $userid[0];
}
function addNote($title, $text, $userID){
assert($this->connection);
$queryCreate = "INSERT INTO notesnew(title, content, userid) VALUES (?,?,?)";
$statement = $this->connection->prepare($queryCreate);
$statement->bind_param('ssi', $title,$text,$userID);
return $statement->execute();
}
}
Within ensureUsername I wanna check if the username which is used for the registration has already been picked by another user.
Within addUser I wanna do the Insert statement, to add the user to the database, if the username is free.
I tried about 3 hours today but it always gives me errors. I hate it! Maybe im just too stupid for it.
At the moment its saying:
Warning: mysqli::query() expects parameter 1 to be string, object
given in
C:\Users\ReallySorry\PhpstormProjects\NoteAppMongo\DBHandler.php on
line 57 0- Warning: mysqli_num_rows() expects parameter 1 to be
mysqli_result, null given in
C:\Users\ReallySorry\PhpstormProjects\NoteAppMongo\DBHandler.php on
line 60
Does anybody know what im doing wrong?
Thanks ...
The depressed student
Your problem is here:
$statement = $this->connection->prepare($query);
$statement->bind_param('s',$username);
$results = $this->connection->query($statement)
When you're using prepared statements (well done – so many people here don't!) you need to use the execute() method on your statement rather than calling query() on your connection. So, this should work:
$results = $statement->execute()
Some minor changes to the original -
function ensureUsername( $username=false, $password=false ){
$rv=false;/* Return Value */
if( !assert( $this->connection ) or !$username or !$password ) return $rv;
$db=$this->connection;/* shorthand for laziness */
$sql = "select `username` from `users` where `username`=?;";
$stmt = $db->prepare( $sql );
$stmt->bind_param('s', $username );
$res = $stmt->execute();
$stmt->store_result();
if( $res ){
$rv=( $stmt->num_rows > 0 ) ? 'Sorry, that Username already exists!' : $this->addUser( $username, $password );
}
$stmt->free_result();
$stmt->close();
echo $rv;
}
function addUser( $username, $password ){
$db=$this->connection;/* No need for assert now, if the script gets here the db conn must exist */
$sql = "insert into `users` (`username`, `password`) values (?,?);";
$stmt = $db->prepare( $sql );
$stmt->bind_param('ss', $username, $password );
return $stmt->execute() ? 'You have been registered!' : 'Sorry, there was a problem';
}

insert user id in another table during registration

I have a registration form that captures email and password. Once the form is submitted it will add an AUTO_INCREMENT userid, email, and password into my users table. During this same submit process I would like to add the ID that was created in my users table to a users_preferences table.
Here is what I currently have:
require("config.php");
if(!empty($_POST))
{
// Ensure that the user fills out fields
if(empty($_POST['username']))
{ die("Please enter a username."); }
if(!filter_var($_POST['username'], FILTER_VALIDATE_EMAIL))
{ die("Invalid E-Mail Address"); }
if(empty($_POST['password']))
{ die("Please enter a password."); }
// Check if the username is already taken
$query = "
SELECT
1
FROM users
WHERE
username = :username
";
$query_params = array( ':username' => $_POST['username'] );
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"); }
// Add row to database
$query = "
BEGIN;
INSERT INTO users (
username,
password,
salt
) VALUES (
:username,
:password,
:salt
) ;
INSERT INTO user_preferences (
user_id
) VALUES (
$user_id
);
COMMIT;
";
$user_id = mysql_insert_id();
// Security measures
$salt = dechex(mt_rand(0, 2147483647)) . dechex(mt_rand(0, 2147483647));
$password = hash('sha256', $_POST['password'] . $salt);
for($round = 0; $round < 65536; $round++){ $password = hash('sha256', $password . $salt); }
$query_params = array(
':username' => $_POST['username'],
':password' => $password,
':salt' => $salt
);
try {
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex){ die("Failed to run query: " . $ex->getMessage()); }
header("Location: index.php");
die("Redirecting to index.php");
}
The registration of the user will go through and add the data to the database, but no data is added to the user_preferences table. How do I get this to add the last user id to the second table?
The problem as I see it is that you are trying to put the value of $user_id into the query, yet it would only receive a value in the next command row (although you're not actually running the first query, you're just trying to fetch the last inserted id).
You should first run the INSERT INTO users... query, then retrieve the last inserted id, then run the second query (INSERT INTO user_preferences...).
Also assuming you're using PDO, last inserted id should be $db->lastInsertId() in your context.
** Update
Alright, without changing your code, just mostly refactoring it a tad bit, you should try something like this:
function checkDataValidity(){
if(empty($_POST['username'])){
throw new Exception("Please enter a username.");
}
if(!filter_var($_POST['username'], FILTER_VALIDATE_EMAIL)){
throw new Exception("Invalid E-Mail Address");
}
if(empty($_POST['password'])){
throw new Exception("Please enter a password.");
}
}
function doesUserExist($dbHandler){
$query = " SELECT 1 FROM users WHERE username = :username;";
$query_params = array( ':username' => $_POST['username'] );
$stmt = $dbHandler->prepare($query);
$result = $stmt->execute($query_params);
if ($stmt->rowCount() > 0){
throw new Exception('This email address is already registered');
}
}
function insertNewUser($dbHandler){
try{
$salt = dechex(mt_rand(0, 2147483647)) . dechex(mt_rand(0, 2147483647));
$password = hash('sha256', $_POST['password'] . $salt);
for($round = 0; $round < 65536; $round++){
$password = hash('sha256', $password . $salt);
}
$query_params = array(
':username' => $_POST['username'],
':password' => $password,
':salt' => $salt
);
$dbHandler->beginTransaction();
$query = "INSERT INTO users (username, password, salt) VALUES ( :username, :password, :salt );";
$stmt = $dbHandler->prepare($query);
$result = $stmt->execute($query_params);
$newUserId = $dbHandler->lastInsertId();
$dbHandler->commit();
}catch(Exception $dbException){
$dbHandler->rollback();
$newUserId = NULL;
}
return $newUserId;
}
function insertUserPreference($dbHandler, $userId){
$query_params = array(
':user_id' => $userId
);
try{
$dbHandler->beginTransaction();
$query = "INSERT INTO user_preferences ( user_id ) VALUES ( :user_id );";
$stmt = $dbHandler->prepare($query);
$result = $stmt->execute($query_params);
$dbHandler->commit();
}catch(Exception $dbException){
$dbHandler->rollback();
}
}
require("config.php");
if(!empty($_POST))
{
try{
checkDataValidity();
doesUserExist($db);
$newUserId = insertNewUser($db);
if (!is_null($newUserId)){
insertUserPreference($db, $newUserId);
}else{
throw new Exception('Error inserting user');
}
header("Location: index.php");
die("Redirecting to index.php");
} catch (Exception $e){
echo 'The following error occured: <br/>'.$e->getMessage();
}
}
Don't let the changes baffle you - I've only rearranged your code to be more easily readable. The above solves the original problem by moving the "user insert" into one function where we return the new ID if the insert was successful, otherwise null value, and we also move the second half of the query into its own function.

PHP crypt()-function within login script returns error-message

For learning purposes I am trying to implement a simple, PDO-based, single user login script with hashed passwords as an upgrade to MD5, which is stored in a MySQL database, following this tutorial. The script works, I am able to login and out with the correct login credentials, but every time I get the following error message:
Notice: Trying to get property of non-object in /var/www/login.php on line 23
line 23 in login.php is if ( crypt($password, $user->hash) == $user->hash ) { //
I already tried if( ! $user = $sth->fetch(PDO::FETCH_OBJ) ) {die('no rows returned');} which returns 'no rows returned', ie. no object is fetched. var_dump($user); returns bool(false).
This is how I hash the password:
$username = 'YourUsername';
$password = 'YourPassword';
// A higher "cost" is more secure but consumes more processing power
$cost = 10;
// Create a random salt
$salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.');
// Prefix information about the hash so PHP knows how to verify it later.
// "$2a$" Means we're using the Blowfish algorithm. The following two digits are the cost parameter.
echo $salt = sprintf("$2a$%02d$", $cost) . $salt;
// Value:
// $2a$10$IkCxeZMOEYCWFDsZ2zYvFA==
// Hash the password with the salt
echo $hash = crypt($password, $salt);
// Value:
// $2a$10$IkCxeZMOEYCWFDsZ2zYvF.gMDDP.ttroo0U9YDPlp6GiXxt02.JSy
$hash is then saved in table 'users' of the MySQL database:
CREATE TABLE users(
id INT AUTO_INCREMENT,
username varchar(65) UNIQUE NOT NULL,
hash varchar(123) NOT NULL,
first_name varchar(50) NOT NULL,
last_name varchar(50) NOT NULL,
email_address varchar(100) NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO users ( username, hash, first_name, last_name, email_address )
VALUES ( 'YourUsername', '$2a$10$IkCxeZMOEYCWFDsZ2zYvF.gMDDP.ttroo0U9YDPlp6GiXxt02.JSy', 'YourName', 'YourLastName', 'test#example.com' );
This is the login.php, where the error occurs on line 23:
<?php
include 'blog.php';
session_start();
$status = '';
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
$username = $_POST['username'];
$password = $_POST['password'];
$sth = $conn->prepare('SELECT hash FROM users WHERE username = :username LIMIT 1');
$sth->bindParam(':username', $username);
$sth->execute();
$user = $sth->fetch(PDO::FETCH_OBJ);
// Hashing the password with its hash as the salt returns the same hash
if ( crypt($password, $user->hash) == $user->hash ) {
$_SESSION['username'] = $username;
header("Location: admin/index.php");
}
else
{
$status="Your Login Name or Password is invalid.";
}
}
view_login("login/login", array("status" => $status));
PDO is pulled in through blog.php and db.php:
blog.php
<?php
require 'functions.php';
require 'db.php';
// Connect to the db
$conn = Blog\DB\connect($config);
if ( !$conn ) die('Problem connecting to the db.');
db.php
<?php namespace Blog\DB;
require 'config.php';
function connect($config)
{
try {
$conn = new \PDO($config['DB_HOST'],
$config['DB_USERNAME'],
$config['DB_PASSWORD']);
$conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
return $conn;
} catch(Exception $e) {
return false;
}
}
config.php
<?php
// Database configuration
$config = array(
'DB_HOST' => 'mysql:host=localhost;dbname=blog',
'DB_USERNAME' => 'YourUsername',
'DB_PASSWORD' => 'YourPassword'
);
I would appreciate your advise how to fix this error.
Secondly, do you consider this a safe login method considering a single user system without registration option?
Have you tried checking your return values?
// check your return value here
$sth = $conn->prepare('SELECT hash FROM users WHERE username = :username LIMIT 1');
// check your return value here
$sth->bindParam(':username', $username);
// check your return value here
$sth->execute();
// check your return value here
$user = $sth->fetch(PDO::FETCH_OBJ);
In summary, check your return values. eg:
if( ! $sth = $conn->prepare('SELECT hash FROM users WHERE username = :username LIMIT 1') ) {
die($conn->errorInfo());
}
if( ! $sth->bindParam(':username', $username) ) {
die($sth->errorInfo());
}
if( ! $sth->execute() ) {
die($sth->errorInfo());
}
if( ! $user = $sth->fetch(PDO::FETCH_OBJ) ) {
die('no rows retrurned');
}
Replace die() calls with whatever error reporting style you like.

Using a variable to direct traffic

I am mostly sure that my error is with the variable not being gotten from the table. However I can not see the error I am asking for that data at the same time I am asking for the username and password. The table consists of [username],[password],[company]. The goal is to have the user get directed based on the name in company after the username and password have been verified. I keep getting the echo at the end.
Here is the code
function RegisterUser($usename, $password, $company)
{
// hash the pwd
$hpwd = hash('sha256',$password);
$q ='insert into users values(username, password, company) values(?,?,?)';
$stmt = PDO::prepare($q);
$stmt->exectue(array( $username, $hpwd, $company));
}
// validate user and return the company if successfull
function ValidateUser($username, $password, &$company)
{
$hpwd = hash('sha256',$password);
$q ='select company from users where username=? AND password=?';
$stmt = PDO::prepare($q);
$stmt->exectue(array( $username, $hpwd));
if( ($company = $stmt->fetch(PDO::FETCH_COLUMN)) === false )
{
$company = header( 'Location: login.php' );
}
elseif($company == "monkeynones"){
header( 'Location: admin1.php' );
}
Your query is wrong:
$sql = "SELECT 'password' and 'company' from users where 'username' = '$username';";
should be
$sql = "SELECT `password`, `company` from `users` where `username` = '$username'";
Use backticks, not quotes, around identifiers. and is replaced by a comma, and the trailing semicolon in the query isn't required.
It is so important that new programmers learn to do username/password authentication properly I felt it necessary to write this longer post.
Firstly, as eicto pointed out, the mysql extension is both deprecated and should really not even be used ever.
So to the metal.
visit php.net and learn about PDO
Never store unencoded passwords.
here is what you should do:
set up PDO:
// you need to store $link somewhere. in a class preferrably
function InitPDO(&$link)
{
// havet the database handle all strings as UTF-8.
$options = array('PDO::MYSQL_ATTR_INIT_COMMAND' => 'set names utf8');
$link = new PDO ( 'mysql:host='.$config['dsn_host'].';dbname='.$config['dsn_db'], $config['username'], $config['password'], $options ) ;
// If there is an error executing database queries, have PDO to throw an exception.
$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}
upon registration of user.
function RegisterUser($username, $password, $company)
{
// hash the pwd
$hpwd = hash('sha256',$password);
$q ='insert into users values(username, password, company) values(?,?,?)';
$stmt = $link->prepare($q);
$stmt->execute(array( $username, $hpwd, $company));
}
// validate user and return the company if successfull
function ValidateUser($username, $password, &$company)
{
$hpwd = hash('sha256',$password);
$q ='select company from users where username=? AND password=?';
$stmt = $link->prepare($q);
$stmt->execute(array( $username, $hpwd));
if( ($company = $stmt->fetch(PDO::FETCH_COLUMN)) === false )
{
$company = 'invalid'; // because user auth failed';
}
//else all is good
}
example test usage.
// assumes there is a 'login.php' and a 'invalid.php' file
$link = null;
InitPDO( $link );
RegisterUser('tester','password','login');
VerifyUser('tester','password', $redir );
if( file_exists( $redir . '.php' ) )
{
header( 'Location: '. $redir . '.php' );
exit;
}
echo 'error. no valid page found to fullfill query';

Categories