I have the following code that checks for both the username and password but I would like to change it so that it checks to see if the username or password is valid.
I was thinking of making two different functions checkUsername and checkPassword as functions and have two call backs for each input.
I just would like to know if I am on the right track or if my controller and model can be altered how they are?
Model:
function check_login($username,$password) {
$query = $this->db->query("SELECT id, first_name, last_name, email, password FROM users WHERE email = ? and password = ?", array($username, md5($password))); // Result
return ($query->num_rows() == 1) ? $query->row() : FALSE;
}
}
Controller:
function _checkUsernamePassword() {
// adding the _ makes the function 'private' so it can't be called from the URI.
extract($_POST); // Gets data from form and creates vars
$user = $this->login_model->check_login($username,$password);
if(! $user){ // != If username or password are not correct
$this->session->set_flashdata('login_error',TRUE); //does not add the non valid login to the session
$this->form_validation->set_message('_checkUsernamePassword', 'Sorry %s is not correct.');
return FALSE;
} else {
$this->session->set_userdata('logged_in',TRUE);
$this->session->set_userdata('user_id',$user->id);
$this->session->set_userdata('user_name',$user->first_name);
$this->session->set_userdata('user_email',$user->email);
return TRUE;
}
Will you ever have a scenario where you want to determine if a user name exists on its own? If not, I see no reason to add the extra functions. It's generally considered a bad practice to let the client know that a username was valid if an invalid password was submitted. It lets a malicious person know that they've solved 50% of the puzzle, freeing them to crack the password only. Further, I can't imagine a scenario where you'd want to check if a password exists on its own.
One thing I would note from your code is that md5 has been broken and sha1 is really a better option for storing passwords these days. Otherwise, without poring over the code looking for tiny improvements I'd say that you've used prepared statements with your inputs and everything else looks okay, so you're probably fine.
Easiest option: change the model, so you can specify whether to check for one or both.
function check_login($username,$password, $match_both = true) {
$query = $this->db->query("SELECT id, first_name, last_name, email, password FROM users WHERE email = ? " . ($match_both ? 'AND' : 'OR') . " password = ?", array($username, md5($password))); // Result
return ($query->num_rows() == 1) ? $query->row() : FALSE;
}
Yes better to check for user name first then go for passwrd check.
that way it will be more secure.
and use mysql_real_escape_string() to esacape special characters in the inputted username and password. else theere will be a way for sql injection.
Related
Should I validate a username and pass word by searching for both in the SQL table Or Should I find the username then match the pass word with a PHP if statement?
SELECT * FROM table WHERE username = $username AND password =$password
SELECT * FROM table WHERE username = $username
...
if ($row[password] == $password)
{
do stuff
}
Which method Is more secure and efficient?
The thing is… You are supposed to store salted, hashed passwords in the database. Since these are individually salted per user/password, you cannot look them up directly with password = ?, because you don't know the salt and therefore cannot calculate the matching hash in advance. If you're doing this properly, you must fetch the user record by username first, then validate the password hash using the retrieved salt/hash. Pseudocode:
$user = fetch_from_database($_POST['username']);
if (!$user) {
throw new Exception("User doesn't exist");
}
if (!password_verify($_POST['password'], $user['password_hash'])) {
throw new Exception('Invalid password');
}
echo 'Welcome ', $user['name'];
See http://php.net/password_hash, http://php.net/password_verify.
From the 2 listed above, the second one is more secure, because first one is more tilted towards SQL injection.
SELECT * FROM table WHERE username = $username AND password =$password
In this code if the value of username and password entered is something like "a or ('a'='a')"
the code will be modified to
SELECT * FROM table WHERE username = a or ('a' = 'a') AND password = a or ('a' = 'a')
Which means a clear code for listing all your data.
Whereas in the second case , IF condition will consider the value as a single string only. So second is the best among the 2 u mentioned..
Hope this helps
So in my function I have the connection, the query which will result in 1 row and 1 column displayed. I then want to run the password_verify() This is where I am struggling. As the first parameter I have placed $pass which is user entered but then I need to get the result from the database to place in the 2nd parameter.
How would I do this?
function login($user, $pass){
$conn = connect();
$query = "SELECT password FROM account WHERE username = '$user' AND password = '$pass'";
$result = mysqli_query($conn, $query);
if (mysqli_num_rows($result)=== 1){
password_verify($pass, "$row[password]");
session_start();
If you do everything right your password field in a account table stores hashed password.
And argument $pass of a function is a plain password, I suppose.
So, your query
SELECT password FROM account WHERE username = '$user' AND password = '$pass'
will NEVER find any user, as you try to find user by plain password.
In addition - your $row variable is not defined.
What's the solution:
function login($user, $pass){
$conn = connect();
// do not add password to query
$query = "SELECT password FROM account WHERE username = '$user'";
$result = mysqli_query($conn, $query);
if (mysqli_num_rows($result) === 1){
// define $row
$row = mysqli_fetch_assoc($result);
// set proper quotes and compare password
// from db with password input by user
if (password_verify($pass, $row["password"])) {
// do something if password is correct
session_start();
And of course, instead of passing values directly to query, start using prepared statements.
When someone registers on your website, you have to use de built-in PHP function password_hash(). Also, I suggest naming it "password_hash" in your database and not "password" as to avoid confusion.
When someone tries to log in on your website, you have to use the built-in PHP password_verify() to compare the hashed password with the password.
// Login function
function login($user, $pass)
{
// Search the user by username
$conn = connect();
$query = "SELECT password_hashed FROM account WHERE username = '$user'";
$result = mysqli_query($conn, $query);
if (mysqli_fetch_row($result))
{
// We found the user, check if password is correct
if(password_verify($pass, $result["password_hashed"]))
{
return true;
}
else
{
return false;
}
}
else
{
// We didn't find the user
return false;
}
}
-Why should you hash the password when you store it into the database? Because when you store the password without hashing, the password in the database is exactly the same like what the user types in when registering. So when a hacker gets into the database, he sees the passwords of every user. If you hash the password, PHP makes the password into something completely else, that way when a hacker gets into the database he doesn't see the password but something completely else.
-The password_verify function is definitely the way to go when a user logs in. Just don't forget to add extra security when you enter data you receive from a user (through $_POST, $_GET, $_SESSION, ...) because when a user types in the follow name: my name" DROP TABLES account; they will delete all account informations.
I'm coding a PHP script and I need a login. So I want to check the input against a database with username and password. Is the best way to do it by doing a query where I compare if the post data is the same (SQL function 'like') as in the database? After that I count the mysql rows. If it's zero, I deny the login. If it's one, I allow the login.
Is that the common and correct way of doing it or are there better ways? I want to have the best modern way. That's the reason why I'm using HTML5 too.
Some part of my code. Setting up cookies for the future cookie log-in and also setting attempts in the session to show the captcha if attempts exceeded certain number of logins.
(Hope you can understand it a bit)
Signin call
if($this->check_signin_errors()) {
if($this->signin($this->user_email, sha1($this->user_pass))) { //$user_pass hashed
header("Location: /account/");
exit();
} else {
$this->generate_captcha();
}
return true;
}
Signin function
private function signin($user_email, $user_pass) {
global $con;
$query = mysqli_query($con, "SELECT *, COUNT(id) FROM `accounts` WHERE `user_email` = '".mysqli_real_escape_string($con, $user_email)."' AND `user_pass` = '".mysqli_real_escape_string($con, $user_pass)."' AND access_level >= '0'");
$result = mysqli_fetch_assoc($query);
if($result['COUNT(id)'] > 0) {
$_SESSION['account_logged'] = true;
$_SESSION['user_id'] = $result['id'];
$_SESSION['user_email'] = $result['user_email'];
$_SESSION['user_pass'] = $result['user_pass'];
$_SESSION['access_key'] = $result['access_key'];
$_SESSION['access_level'] = $result['access_level'];
$this->set_cookie('cookie_name', '1/'.$_SESSION['user_email'].'/'.$_SESSION['user_pass'], 7776000);
$_SESSION['attempt'] = 1;
return true;
}
$_SESSION['account_logged'] = false;
$_SESSION['attempt'] = $_SESSION['attempt'] + 1;
$this->throw_error(5);
return false;
}
The most current approach would be to use the PHP 5.5 password hashing functions.
And because not everyone uses 5.5 yet, there is a library that implements it for earlier PHP versions.
Because hashing passwords uses a dynamic "salt", a random string, generated for each user individually, when logging in you actually need to read the user database to get the current hash, because when you want to compare the login password, you need the hash as second input to the password_verify() function.
So actually you try if you find the user in the database (do not use "LIKE" here, this is for searching with placeholders - your user should be able to type his username correctly). If none is found: no login. If two are found: You should add a unique index to the username, otherwise you'll have two users with the same name.
If one entry is found, you read it, compare the hashes, and then allow him further.
I have this user login process page. at this point the user has entered the info and all of this works BUT I cannot figure out how to pull the encrypted password out of the DB. I need to extract with the PASSWORD() function and do not know how. I know this is not the best way to do it but its what the assignment calls for. I have the problem section commented out I think thats what needs fixing.
//sets $query to read usnername and passowd from table
$query = "SELECT username,password,first_name,last_name FROM jubreyLogin WHERE username
= '$userName' AND password=password('$userPassword')";
$result = mysql_query($query,$db);
if(mysql_error())
{
echo $query;
echo mysql_error();
}
//reads data from table sets as an array
//checks to see if user is already registered
while($row=mysql_fetch_array($result))
{
if($userName == $row['username'] /*&& $userPassword == ($row['password'])*/)
{
$login = 'Y';
$welcome = "Welcome" . " " .$row['first_name']. " " .$row['last_name'];
$userName = $row['username'];
}
}
if ($login='Y')
{
setcookie('name',$welcome,time()+60*60*24*30);
setcookie('login',"Y",time()+60*60*24*30);
$_SESSION['username_login'] = $userName;
header('Location: welcome.php');
}
Here is the modified code that I should of posted first I need it to check user entered password in this case $userPassword with the encrypted password if its a match it will send the user into the next page of the site.
You don't need to see the password in clear text ( you can't even if you wanted to). As you are checking the record both on password and username you don't need the check in your if() statement. If there is any row found, that means the username/password combination was succesfful and the user can be deemed as logged in.
Edit:
The updated code doesn't really make any difference to the actual logic. The logic stays the same, you query the database with username AND encrypted password, if there is a match that means the user has the right to login, so you proceed with setting the cookies/session data and redirect. Although I do not really see the need for the login cookie and the welcome cookie cause you could simply put in both username, fname and lname in the session. If the session on the following pages contains username that means the user has logged in.
The code can go something like this:
//sets $query to read usnername and passowd from table
$query = "SELECT username,first_name,last_name FROM jubreyLogin WHERE username = '$userName' AND password=password('$userPassword')";
$result = mysql_query($query,$db);
if(mysql_error())
{
echo $query;
echo mysql_error();
}
// were any rows returned?
if(mysql_num_rows($result)){
list($userName, $firstName , $lastName) = mysql_fetch_row($result);
$welcome = "Welcome" . " " .$firstName. " " .$lastName;
setcookie('name',$welcome,time()+60*60*24*30);
setcookie('login',"Y",time()+60*60*24*30);
$_SESSION['username_login'] = $userName;
header('Location: welcome.php');
}
You should not be encrypting your passwords, you should be hashing them. Try using a library such as phpass to be on the safe side. What you will need to do is hash the passwords and store the hashed value in the database. When a user logs in, you will hash the password they provide and compare that with the hashed value in the database. If the hashes match, the password provided is correct. If not, you send an error to the user. You never need to be able to obtain the password in plain text in order to validate a user.
Also, make sure that you are either escaping your variables using mysql_real_escape_string() or prepared statements or your script will be vulnerable to SQL injection.
In a login system, how can you tell if the user has entered the password incorrectly? Do you perform two SQL queries, one to find the username, and then one to find the username and matching (salted+hashed etc) password? I'm asking this because If the user entered the password incorrectly, I want to update the failed_login_attempts column I have.
If you perform two queries wouldn't that increase overhead?
If you did a query like this, how would you tell if the password entered was correct or not, or whether the username doesn't exist:
SELECT * FROM author
WHERE username = '$username'
AND password = '$password'
LIMIT 1
( ^ NB: I'm keeping it simple, will use hash and salt, and will sanitize input in real one.)
Something like this:
$user = perform_Query() // get username and password?
if ($user['username'] == $username && $user['password'] == $password)
{
return $user;
}
elseif($user['username'] == $username && $user['password'] !== $password)
{ // here the password doesn't match
// update failed_login_attemps += 1
}
You're overthinking it. Only one query is required:
SELECT * FROM author WHERE username = '$username';
Then do:
if ($user['password'] == saltedHash($password,$user['salt'])) {
return "successful";
}
else {
return "failed";
}
Username must be unique. Otherwise this won't work. I would advise against making username non-unique because it causes a lot of other headaches apart from this.
If you perform two queries wouldn't that increase overhead?
I'd say it doesn't matter really. Many complex web frameworks issue dozens or hundreds of queries per request. One more or less won't change things much.
I think it's really up to preference. Fetching the whole user row, and then checking the password on PHP side makes the most sense as far as I can see, because you then already have the ID you need to update the failed_logins column.
What we do is perform one query to find the user (based on userid) and we select the UserId, PwSalt, and PwHash.
If no user is found then we know it is an invalid username.
If the user is found, we hash the password and compare it to the pwHash from the query. If the hash doesn't match we update the failed login attempts.
In your code, $user will be empty if username or password is incorrect
SELECT password = '$password' AS is_legit, *
FROM author
WHERE username = '$username'
LIMIT 1
$user = perform_Query() // get username and password?
// $user will be empty if the username is incorrect
$user_exists = $user.length > 0;
// to make sure we don't address a
// non-existent array element
if($user_exists && $user['password'] == $password){
return $user;
}
elseif($user_exists && $user['password'] !== $password)
{ // here the password doesn't match, but the user does
// update failed_login_attemps += 1
// be sure to let the user know that the penetration
// attempt is halfway complete
}
else{
// F4il
}
The way (and many others) create login systems is like so:
On Registration
create a unique hash and store along with username,password
On Login
Pull username,password,hash from database
Use the clause WHERE username = '$username'
If theres 1 row, the username is correct
build a compiled hash with hash($post_pass,$user_hash) and compare with $user_pass
Also if you return anything at 1 point of your method anything after would not be run, so
if ($user['username'] == $username && $user['password'] == $password)
{
return $user;
}
elseif($user['username'] == $username && $user['password'] !== $password)
{ // here the password doesn't match
// update failed_login_attemps += 1
}
can be modified to
if ($user['username'] == $username && $user['password'] == $password)
{
return $user;
}
return false
because if ($user['username'] == $username && $user['password'] == $password) is met, then the return would be executed there for the false would not be executed.
Hope this helps.