After successfully verifying username exists in the database, I need to verify whether the password matches as well. Therefore, I need to retrieve the hashed password from the database, right? Or do I have other choices?
I'm using PHP 5.5 API bcrypt for hashing. My code would give me a 500 internal server error when I get the password from database. How do I do this correctly?
Here's my php code:
// If found username, check password, else respond invalid username
if($countRows!==0){
// Get hash from database
// Prepare statement
$stmt = $conn->prepare('SELECT password FROM users WHERE username = ?');
// Bind
$stmt->bind_param('s', $ps_username);
// Set Parameters
$ps_username = $username;
// Execute
$hash = $stmt->execute();
// Check password
if (!password_verify($password, $hash)) {
if($error != ""){
$error .= '<br>';
}
$error .= 'The user or password you entered do not match, please try again.';
}
else {
echo 'OK';
// Session start
// Redirect user to profile/homepage
}
}
And can someone recommend something that I can learn SQL commands? I can't find a good place for that.
execute() does not return any column data. It returns a boolean (true/false). This is where your code block first fails:
$hash = $stmt->execute();
You can view examples on how to fetch data from the result set here: http://php.net/manual/en/mysqli-stmt.fetch.php
An example being:
$stmt->execute();
$stmt->bind_result($hash);
$stmt->fetch();
In response to:
And can someone recommend something that I can learn SQL commands?
This is pretty much off topic for Stackoverflow but the PHP manual for mysqli can show you how to use the mysqli API fairly well with plenty of examples. If you want to learn the Structured Query Language itself, then there are plenty of external resources for that, including MySQL's documentation.
Related
I am trying to create a login system and hence I encrypted password during registration with the password_hash($password,PASSWORD_BCRYPT) function. However I am having difficulties while comparing the login password provided by the user with the bcrypted password stored in the database.
Here is the code without the security functions I tried while trying to compare the login password with the registered password. Any help would be greatly appreciated.
$loginpassword=$_POST['password'];
$con=mysqli_connect($ip,$username,$dbpass,$dbname);
$regpassword="SELECT password FROM customerdb WHERE username='$username'";
$result=mysqli_query($con,$regpassword);
$value=mysqli_fetch_fields($result);
if(password_verify($loginpassword,$value))
{
session_start();
header(.........);
exit();
}
P.S. I am using php 5.4. Hence I included the password_compat from https://github.com/ircmaxell/password_compat
The mysqli_fetch_fields method returns an array of metadata about the columns in the database, and not the values.
To get the password value, use mysqli_fetch_array. You also need to check a row was returned from the database.
$row = mysqli_fetch_array($result, MYSQLI_NUM);
if ($row) {
$value = $row[0];
// Verify...
} else {
// Invalid username
}
You should read up on SQL injection because your query is vulnerable to attack.
I´m not very familiar with security, therefore I rely on what I find on the internet. I found a site of someone who explains a bit what he does and how his method works. People may copy-paste it to ease things up.
Though I do understand quite a lot, I couldn't come up with it myself (I'm pretty new to PHP/XHTML, etc.)
The website: How to store safely with PHP and MySQL
He uses PDO in his tutorial. And I am able to store the information in the database. But when I try to use the script in which he provides the code for actually logging in, though it seems it contains errors.
I've worked everything out and everything works fine, but the comparison of the hashed password with the inserted password (with the hash, etc.) does not work properly.
What is going on here?
Thanks in advance!
EDIT
People have been asking for the code so, here it is:
session_start();
require('config.php');
// Setting up a connection
$MyConnection = new PDO('mysql:host=*;dbname=*', $dbuser, $pass);
// Retrieving information from form.
$username = $_POST['username'];
$password = $_POST['password'];
$sth = $MyConnection->prepare("SELECT * FROM AMP_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) {
echo 'You are now logged in. If we actually used sessions this time.';
}
I will add a $_SESSION['name'] = $username, once the code starts to work. Until now I simply echo out if it worked out or not. And it doesn't show anything, so it doesn't work.
SECOND EDIT
Just as a quick update, the script provided by me, is the WHOLE script. Nothing is let out. (Except names of databases, etc.)
Therefore I wonder if the problem may be that I don't use the hashing script of the saving the passwords into the database. Though I have put it in, it still doesn't respond. Am I still doing something wrong?
if($_POST):
$name = $_POST['username'];
$pass = crypt($_POST['password'], '$2a$07$Hd893nD39Jdjd48Jdh3nD$');
$conn = new PDO('mysql:host=*; dbname=*', 'root', '');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $conn->prepare('SELECT * FROM user WHERE name = ? AND password = ?');
$stmt->execute(array($name, $pass));
if($stmt->rowCount() === 0){
echo 'Your Username / Password is incorrect. Please try again';
}else{
echo 'login success';
}
endif;
Maybe you have to check the length of the field that you store the password on the database... If the length is small then the hashed password will not stored as whole.. you will store a part of it!
I modified it to run in mysqli and it works fine:
$getAuth=$dbConAU->prepare("SELECT Password FROM Users WHERE UserName=? LIMIT 1");
$getAuth->bind_param("s",$UserName);
$getAuth->execute();
$getAuth->bind_result($hash);
$getAuth->fetch();
$getAuth->close();
if (crypt($Password, $hash) == $hash) {
return "OK";
}
else { return "Not OK"; }
I know this thread is a few months old but someone might find this
SunnyTuts php pdo login and registration tutorial
tutorial helpful. I found both this thread and the tutorial while looking for a secure way to allow users to login. Being new to php and web design I found it a small bit hard to follow but I'm sure it will seem like a piece of cake to some of you....
For example someone tries to log in with the name "Bob" but there is no Bob in the database. Should the user be notified there is no Bob or should should the program simply say "authentication failed" (I noticed Gmail does this)? This is partly a usability question and partly an efficiency question. As it is now the program queries the database to see if the given username exists and if it does then it queries the database again to find the password hash for the same username (redundant).
//$link is the link to the database storing passwords/usernames
if(userNameExists($uName, $link))
{
if(passwordCorrect($uName, $pass, $link))
echo 'log in successful!';
else
echo 'can\'t log in';
}
else
{
echo 'username doesn\'t exist!';
}
/*This function checks to see if the username exists
INPUT: the userName to check for and a link to the database
OUTPUT: true if username exists
*/
function userNameExists($userName, $link)
{
$result = mysqli_query($link, 'SELECT `userid`
FROM `login`
WHERE `userid` = \''.$userName.'\' LIMIT 1');//need to look into uses of backticks, single quotes, double quotes
return mysqli_num_rows($result) == 1;
}
/*This function checks the password for a given username
INPUT: the userName and password the user entered, and a link to the database
OUTPUT: true if the given password matches the one in the database
*/
function passwordCorrect($userName, $givenPassword, $link)
{
$result = mysqli_query($link, 'SELECT `password`
FROM `login`
WHERE userid = \''.$userName.'\' LIMIT 1');
$retrievedPassword = mysqli_fetch_array($result);
if(password_verify($givenPassword, $retrievedPassword['password']))
return true;
else
return false;
}
Should I instead only use passwordCorrect() and if mysqli_query() returns false this implies the username does not exist (admittedly I don't like these solutions because it could mean something else has gone wrong, doesn't it?)?
You should not provide details why the login failed (user does not exists or password is wrong), as this increases security. Unless usernames are visible when one is not logged in (which is actually insecure and should not be the case..!)
Advantage is that you indeed can use a single query to get the hash for the given username. If you do not get a result, the username is wrong (and the login failed), otherwise you can (directly) check the hash (to see if the password is wrong).
Roughly speaking there are two basic points to this story that you can consider
Providing specific information may improve usability because the user then knows what piece of information he provided was erroneous
But providing specific information also allows for potential abuse. If you specifically return if a username did not exist, the opposite might alert a hacker that a username does exist and he may use that information to more effectively use brute force techniques to crack logins
What best suits your situation will always be a trade off but often it is advised to omit specific information for the aforementioned security reasons
EDITThanks to the comments below it has been figured out that the problem lies with the md5, without everything works as it should.
But how do i implent the md5 then?
I am having some troubles with the following code below to login.
The database and register system are already working.
The problem lies that it does not find any result at all in the query.
IF the count is > 0 it should redirect the user to a secured page.
But this only works if i write count >= 0, but this should be > 0 , only if the user name and password is found he should be directed to the secure (startpage) of the site after login.
For example root (username) root (password) already exists but i cannot seem to properly login with it.
<?php
session_start();
if (!empty($_POST["send"]))
{
$username = ($_POST["username"]);
$password = (md5($_POST["password"]));
$count = 0;
$con = mysql_connect("localhost" , "root", "");
mysql_select_db("testdb", $con);
$result = mysql_query("SELECT name, password FROM user WHERE name = '".$username."' AND password = '".$password."' ")
or die("Error select statement");
$count = mysql_num_rows($result);
if($count > 0) // always goes the to else, only works with >=0 but then the data is not found in the database, hence incorrect
{
$row = mysql_fetch_array($result);
$_SESSION["username"] = $row["name"];
header("Location: StartPage.php");
}
else
{
echo "Wrong login data, please try again";
}
mysql_close($con);
}
?>
The best thing you can do in such situations is trying to find out where the problem lies.
So, you could proceed by steps and do the following:
1) start your script with a print_r($_POST), to see what variables are passed by post (by absurd, the problem might even be related to the 'send' guard parameter you have ..IE a form being sent through get)
2) Assign your query to a variable (...and don't forget to escape parameters!) and print it to screen; and then exec it on mysql (or phpmyadmin) to see what results they give.
As a side note, as someone already pointed out, this code might be subject to SQL-injection, so you might consider using prepared statements; see here: quick intro
Your login code is good.
But you also need to use md5() function BEFORE storing the password in the database. So when you are inserting the user record in the DB , apply the md5() to the password , save in the DB. Now when you will try to find the record on login, it will match correctly.
You should rewrite this with mysqli or PDO and using a newer hash function as well as a salt. MD5 is very widely used and is a target for crackers.
Can anyone see anything wrong with this login script:
public function login($username, $pass, $remember) {
// check username and password with db
// else throw exception
$connect = new connect();
$conn = $connect->login_connect();
// check username and password
$result = $conn->query("select * from login where
username='".$username."' and
password=sha1('".$pass."')");
if (!$result) {
throw new depException('Incorrect username and password combination. Please try again.');
} else {
echo $username, $pass;
}
To explain:
At the moment the script is allowing anything through. In other words the query is returning true for any username and password that are passed to it.
I've put the echo statement just as a check - obviously the script would continue in normal circumstances!
I know that the connect class and login_connect method are working because I use them in a register script that is working fine. depException is just an extension of the Exception class.
The function login() is part of the same class that contains register() that is working fine.
I know that the two variables ($username and $pass) are getting to the function because the echo statement is outputting them accurately. (The $remember variable is not needed for this part of the script. It is used later for a remember me process).
I'm stumped. Please help!
UPDATE
Thanks for those responses. I was getting confused with what the query was returning. The complete script does check for how many rows are returned and this is where the checking should have been done. Everything is now working EXCEPT for my remember me function. Perhaps someone could help with that?!?! Here is the full script:
public function login($username, $pass, $remember) {
// check username and password with db
// else throw exception
$connect = new connect();
$conn = $connect->login_connect();
// check username and password
$result = $conn->query("select * from login where
username='".$username."' and
password=sha1('".$pass."')");
if (!$result) {
throw new depException('Incorrect username and password combination. Please try again.');
}
if ($result->num_rows>0) {
$row = $result->fetch_assoc();
//assign id to session
$_SESSION['user_id'] = $row[user_id];
// assign username as a session variable
$_SESSION['username'] = $username;
// start rememberMe
$cookie_name = 'db_auth';
$cookie_time = (3600 * 24 * 30);*/ // 30 days
// check to see if user checked box
if ($remember) {
setcookie ($cookie_name, 'username='.$username, time()+$cookie_time);
}
// If all goes well redirect user to their homepage.
header('Location: http://localhost/v6/home/index.php');
} else {
throw new depException('Could not log you in.);
}
}
Thanks very much for your help.
UPDATE 2!
Thanks to your help I've got the main part of this script working. However, the remember me bit at the end still doesn't want to work.
Could someone give me a hand to sort it out?
$username, $pass and $remember are all short variable names that I assigned before passing them to the function to save writing $_POST['username'] etc. everytime. $remember refers to a checkbox.
What does $conn->query() return, a MySQL resource object like mysql_query() does? If so then it'll always compare "true". mysql_query() only returns FALSE if the query completely fails, like it has a syntax error or a table doesn't exist.
To check if you got any results you need to try to fetch a row from the result set and see if you get anything, via whatever your equivalent of mysql_fetch_row() is.
Important: Your script is vulnerable to SQL injection attacks, or even just odd usernames like o'neil with an apostrophe. You should escape all variables in a query with mysql_real_escape_string() (or equivalent) to make sure your query doesn't get messed up by special characters. Or, even better, use prepared statements which look like
select * from login where username=? and password=sha1(?)
Re: UPDATE
Variables from a form are available via either $_GET or $_POST, depending on which method was used to submit the form. Try if (isset($_POST['remember'])) to see if that check box was checked.
Important: I see that you tried to use a bare $remember to see if the check box was checked. That suggests to me that you are trying to take advantage of the register_globals feature in PHP which makes your GET and POST variables accessible via regular variable names. If that is the case you should heed the warning in the PHP manual!
WARNING
[register_globals] has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 6.0.0. Relying on this feature is highly discouraged.
Use $_GET and $_POST instead. I could tell you how to make if ($remember) work, actually, but given the inherent evil-ness of register_globals I'm not gonna! ;-)
Your query is open for sql-injections...
SELECT * FROM users WHERE
username = '' OR 'a' = 'a'
AND password =
sha1('guessAnyPassword')
I'd also check your result, and base the action on how many records were returned.
if (mysql_num_rows($result) > 0)
In php most queries only return False if there was an error executing them. Your query is returning a value, probably an empty array of values. This is not a false value as far as your if statement is concerned.
Check how many rows are returned. The function to do this will depend on your abstraction layer (connect class etc...)