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
Related
This question already has answers here:
How to use PHP's password_hash to hash and verify passwords
(5 answers)
Closed 1 year ago.
Here's my code:
function login() {
//Declare variables
$username = $_POST["login"];
$password = $_POST["password"];
$client = $_POST["clients"];
//QueryDB
$servername = "localhost";
$SQLUsername = "XXXX";
$SQLPassword = "XXXX";
$dbname = "XXXX";
$conn = new mysqli($servername, $SQLUsername, $SQLPassword, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
//Set query then run it
$sql = "SELECT * FROM Users WHERE Username ='$username'";
$result = $conn->query($sql);
$row = $result->fetch_array(MYSQLI_ASSOC);
//Verify Password
if ($result->num_rows === 1) {
if (password_verify($password, $row['Password'])) {
//Build XML request
$XMLRequest = "<?xml version='1.0'?><Login><Client>".$client."</Client><LoginDetails><Username>".$username."</Username><Password>".$newhash."</Password></LoginDetails></Login>";
//Build URL
$ReplacedValues = array("NewType" => "login", "NewHash" => $XMLRequest);
$NewString = strtr($GLOBALS["params"], $ReplacedValues);
$NewUrl = $GLOBALS["link"].$NewString;
//Post to Server
header('Location: '.$NewUrl);
}
else {
echo "Password is wrong"."<br>";
echo $password."<br>";
echo $row['Password'];
}
} else {
echo "more then 1 row";
}
mysqli_close($conn);
}
My issue is that even if I hard code my password variable and Hash variable to their respective values the if condition returns false. Any idea why? The page does when it loads, loads the else condition to show me the user input password and the correct hash value from the DB. My DB is set to CHAR(255) for the password.
UPDATE**
Here is my C# discussed in the comments. This is not the complete code just up to the part of the insert statement for the DB. I am able to insert into the SQL server DB just fine.
public static string WebsiteRegister(XmlDocument XMLBody)
{
//Get SQL connection string
XmlNodeList XMLNodes = SQLConnectionMethods.EstablishSQLServerConnection("SQL");
string ConnectionString = XMLNodes.Item(0).ChildNodes[0].InnerText;
string UserName = XMLNodes.Item(0).ChildNodes[1].InnerText;
string Password = XMLNodes.Item(0).ChildNodes[2].InnerText;
//Open connnection
SqlConnection cnn = new SqlConnection(ConnectionString);
SQLConnectionMethods.OpenSQLServerConnection(cnn);
try
{
string username = XMLBody.SelectSingleNode("register/registerdetails/username").InnerText;
string pass = XMLBody.SelectSingleNode("register/registerdetails/password").InnerText;
string fname = XMLBody.SelectSingleNode("register/registerdetails/firstname").InnerText;
string lname = XMLBody.SelectSingleNode("register/registerdetails/lastname").InnerText;
string email = XMLBody.SelectSingleNode("register/registerdetails/email").InnerText;
string accountRef = XMLBody.SelectSingleNode("register/registerdetails/accountreference").InnerText;
string client = XMLBody.SelectSingleNode("register/client").InnerText;
//Build Query string
string queryString = $"Insert into [dbo].[UserAccounts] (AccountReference, FirstName, LastName, Email, Username, Pass, Client) values ('{accountRef}', '{fname}', '{lname}', '{email}', '{username}', '{pass}', '{client}')";
//Process request
using (SqlCommand myCommand = new SqlCommand(queryString, cnn))
{
string Result = (string)myCommand.ExecuteScalar();
I could not find any problem in your code here but since hashing and verifying is a process which depends on a lot of factors to be successful i would like to give you some tips so that you can check it yourself for any potential problems.
make sure you are not escaping/sanityzing the password before
hashing it. This way you're altering the stored password. Let
$password = $_POST['password'];
both when you create the account and
when you check if the password match at login.
make sure you are enclosing the hash variable in single quotes (') and not double quotes (").using double quotes makes PHP read each paired character with "$" as indivisual variables which will probably cause your code to break, USE SINGLE QUOTES INSTEAD.
Ensure the Password field in the database (that stores the hashed
password) is able to store up to 255 characters. From the documentation
it is recommended to store the result in a database column that can
expand beyond 60 characters (255 characters would be a good choice). If the field is narrower the hash will be truncated and you'll never
have a match.
As you get the user by username at login ensure that username is unique
as long as it is your primary key (in the table
definition). Good idea to check this also upon user registration and login (at php level).
echo both hash and entered
values and make sure they match the ones that were inserted and
generated during password_hash() if the database value was different
(the hash), make sure the type of its column is varchar(256), the
hash is usually 60 characters long but the hashing function is
frequently improved so that length may expand in the future.
if the entered value was different (the user password), make sure the
filtering isn't corrupting the password value.
also check if another variable has the same name as the one you're storing the password in.
If password_verify($password, password_hash($password, PASSWORD_DEFAULT))
"works", then the problem is that $row['Password'] does not contain what is expected - including not being generated correctly.
If it "doesn't work" then
another line is causing the observed behavior. Both of these
outcomes allow focusing on a refined problem set.
try changing the password from the another column that matched and copy it to your password column and enter that password to check.if it worked then there is problem with your stored password
try copying this hash to your password column
$2y$10$uDnEpQpkh9RO5tWwOrHtneLAuYMRDstRaKGZFyHubObDR0789OMO6
this is the hashed password for 123456 using bycrypt. select this password and try to verify it with password_verify(123456,$yourhashedfromdatabase) after selecting this from sql if it works then there is probably some manipulation after your data is entered and goes to the database. it it does not then check if the password that reaches the database is exactly as your typed by echoing it.
UPDATE: after you updated your code i see that while entering the password you use "Pass" column but while extracting you use "Password" column. this could be the issue
As of now I have this line of code
<?php
$pass = "12312312";
echo md5(crypt($pass))
?>
I know crypt can randomly generate salt but I don't have any idea how can I compare it on the user input.
What are the fields do I need for my log in table?
and also, what data will I insert to my table?
thanks!
You can store password in table as below
$pass = "12312312";
// store this in table with separate column
$salt = md5("any variable"); // may be email or username
// generate password
$password = sha1($salt.$pass);
// now store salt and password in table
And you can check password like
$pass = "User input";
// get the user from database based on user id or username
// and test the password stored in db with
if($passwordFromTable == sha1($saltFromTable.$pass))
{
// correct
}
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.
Alright, I'm trying to make a login page. It seems that all of the pages worked pretty good- until I added salts. I don't really understand them, but doing something as basic as I am shouldn't be to hard to figure out. Here's "loginusr.php":
<html>
<body>
<?php
//form action = index.php
session_start();
include("mainmenu.php");
$usrname = mysql_real_escape_string($_POST['usrname']);
$pass = $_POST['password'];
$salt = $pass;
$password = sha1($salt.$pass);
$con = mysql_connect("localhost", "root", "g00dfor#boy");
if(!$con)
{
die("Unable to establish connection with host. We apologize for any inconvienience.");
}
mysql_select_db("users", $con) or die("Can't connect to database.");
$select = "SELECT * FROM data WHERE usrname='$usrname' and password='$password'";
$query = mysql_query($select);
$verify = mysql_num_rows($query);
if($verify==1)
{
$_SESSION["valid_user"] = $usrname;
header("location:index.php");
}
else
{
echo "Wrong username or password. Please check that CAPS LOCK is off.";
echo "<br/>";
echo "Back to login";
}
mysql_close($con);
?>
</body>
</html>
I used the command echo $password; to show me if the password in the database matched with the script. They did. What am I doing wrong?
It seems like you've misunderstood salts, since you're setting $salt to be the password.
A salt should be a completely random string that's stored in a user record along with the password hash. A new unique salt should be generated for every user. So you need to add a new column to your database, called "password_salt" or similar.
Rather than trying to use the password in the SELECT query and see if you get any records, you actually need to just SELECT using the username/user_id in order to get the password hash and salt so that you can then use those to determine if the user entered the correct password.
When you sign up new users you should add the fields with values like this,
<?php
// This is registeruser.php
$salt = substr(sha1(uniqid(rand(), true)), 0, 20);
$pass = $_POST['password'];
$pass_to_store = hash("sha256", $salt.$pass);
// Then issue a DB query to store the $salt and $pass_to_store in the user record.
// Do not store $pass, you don't need it.
// e.g. INSERT INTO users ('username', 'password_salt', 'password_hash') VALUES (:username, :salt, :pass_to_store);
?>
Then to check the password is the same when logging in, you do something like this,
<?php
// This is loginuser.php
$user = // result from SQL query to retrieve user record
// e.g. SELECT password_hash, password_salt FROM users WHERE username='from_user'
$salt_from_db = $user['password_salt'];
$pass_from_db = $user['password_hash'];
if ($pass_from_db == hash("sha256", $salt_from_db.$_POST['password'])
{
// Password matches!
}
?>
Don't forget to sanitize user inputs and anything you're putting into your database. You might want to look into using prepared statements instead of having to remember to use mysql_real_escape_string all the time.
It looks like you're salting with the same password? Normally a salt would be a random key that is specific to your site that you prepend to the password input, which it looks like you're doing fine. Just make sure you're using that same salt for checking that you use when the password is created.
Also, to use sessions properly you need to have session_start before anything is output to the page:
<?php
session_start();
?>
<html>
<body>
...
A salt is a random value to prevent an attacker from just looking up the source of a hash in table generated based on common passwords. (Using the username as salt is obviously not a good idea as it only adds very little entropy).
So you need to store the salt in the database and read it from the database in order to calculate the salted password hash for comparison with the stored value.
You misspelled username a couple of times, is it misspelled in the database, too?
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.