authentication with salt and bcrypt

554 views
Skip to first unread message

v.

unread,
Mar 9, 2015, 6:03:38 AM3/9/15
to f3-fra...@googlegroups.com
Hello,

I am trying to write my first fat free script.
Now I am trying to store hashed passwords in the database using bcrypt. I cannot seem to find examples on how to do this except for a code snippet in another post here.

My logic is to create a salt, use it to hash the password with BCrypt and store both values in the database.
During authentication the salt is retrieved from the database to recreate the hash and check the password against this.
Is that the correct procedure?

So far I have:
$salt = substr(md5(uniqid(rand(), true)), 0, 22);
$crypt
= \Bcrypt::instance();
$password
= $crypt->hash($this->f3->get('POST.password')[$salt]); //error here
$this
->f3->set('POST.salt', $salt);
$this
->f3->set('POST.password', $password);
$user
= new User($this->db);
$user
->add();

This returns an error which seems to be due to the fact that the salt is not a numeric value. (depending on the string I get 'Illegal string offset 'd4a37efee6d92da14c59b8'' or 'A non well formed numeric value encountered').
If I set $salt to an integer everything gets stored ok.

This seems weird to me...

How would I get the salt back from the db? Should I first query the database for the salt, check the password with this and then do a second query to see if the hash is correct? Or is it possible to check with one query only?

v.


ikkez

unread,
Mar 9, 2015, 6:09:37 AM3/9/15
to f3-fra...@googlegroups.com
i think that line should be:

$password = $crypt->hash($this->f3->get('POST.password').$salt);

as you probably want to concatenate the strings.

edit:


How would I get the salt back from the db? Should I first query the database for the salt, check the password with this and then do a second query to see if the hash is correct? Or is it possible to check with one query only?

You need to fetch both values from db, and use the ->verify method from bcrypt. the hash is different all the time, so you can't just match the hashes with a db query.

$valid = \Bcrypt::instance()->verify($f3->get('POST.password').$user->salt, $user->password);

v.

unread,
Mar 9, 2015, 6:15:15 AM3/9/15
to f3-fra...@googlegroups.com
derp....
I just copied this from another post. Obvisouly that was a mistake. Thank you for clearing this out.
What about the question with the 1 or 2 queries?


v.

unread,
Mar 9, 2015, 6:19:59 AM3/9/15
to f3-fra...@googlegroups.com
to be clear: i am using the sql mapper to get the data from the users table

Steve Cove

unread,
Mar 9, 2015, 6:20:46 AM3/9/15
to f3-fra...@googlegroups.com
You should not store the salt, the verify method takes the plain text password, and hashed password as parameters to verify.
If you want to generate your own salt,  then you pass it as a string:

$password = $crypt->hash($this->f3->get('POST.password'), $salt);

However, if you do not pass a salt to the method, it will generate a strong one its self:

if ($salt) {
 
...
 
}
 
else {
 $raw
=16;
 $iv
='';
 
if (extension_loaded('mcrypt'))
 $iv
=mcrypt_create_iv($raw,MCRYPT_DEV_URANDOM);
 
if (!$iv && extension_loaded('openssl'))
 $iv
=openssl_random_pseudo_bytes($raw);
 
if (!$iv)
 
for ($i=0;$i<$raw;$i++)
 $iv
.=chr(mt_rand(0,255));
 $salt
=str_replace('+','.',base64_encode($iv));
 
}


So you are probably better off using the generated salt

Steve Cove

unread,
Mar 9, 2015, 6:27:54 AM3/9/15
to f3-fra...@googlegroups.com
As to verifying a user with a mapper:

$user = new \DB\SQL\Mapper($this->db, 'users');
$user
->load(['username=?', $this->f3->get('POST.username']);


if($user->dry()){
   
die( 'username does not exisit');
}

if(!\Bcrypt::instance()->verify($this->f3->get('POST.password'), $user->password){
   
die('password incorrect';
}
//verified, good to go
echo
'Welcome back ' . $user->name;

v.

unread,
Mar 9, 2015, 8:18:01 AM3/9/15
to f3-fra...@googlegroups.com
Thank you very much Steve!

This works like a charm. I am not sure I entirely understand how the salt works and how it is retrieved.
Did I understand correctly that a strong salt is generated when doing this:
$crypt = \Bcrypt::instance();
$password
= $crypt->hash($this->f3->get('POST.password')); //no salt given -> salt = generated automatically
How is the same salt then recreated afterwards? If I understand correctly salt is random. How does the script know which salt was used for the first hashing, when the crypted pw was stored?

There were some errors with missing parantheses in the code snippets you posted. For future reference (for other noobs that are looking for this ;)) i have adapted them:
$user = new \DB\SQL\Mapper($this->db, 'users');

$user
->load( [ 'username=?', $this->f3->get('POST.username') ] );

if($user->dry()){die( 'username does not exist');}

if(! \Bcrypt::instance()->verify($this->f3->get('POST.password'), $user->password)){
   echo $this
->f3->get('POST.password').' - '.$user->password .'<br />';
   
die('password incorrect');
}

echo
'Welcome back ' . $user->username;


Steve Cove

unread,
Mar 9, 2015, 9:37:57 AM3/9/15
to f3-fra...@googlegroups.com

"Did I understand correctly that a strong salt is generated when doing this:"

Yes, that is correct

"How does the script know which salt was used for the first hashing"

The generated hash contains information about the salt used to create it.
For more information you can ready php's docs for crypt, which is what f3 uses internally:

v.

unread,
Mar 9, 2015, 9:49:47 AM3/9/15
to f3-fra...@googlegroups.com
Will do!
Thanks again for your kind and selfless help! I really appreciate it

Steve Cove

unread,
Mar 9, 2015, 10:31:47 AM3/9/15
to f3-fra...@googlegroups.com
No problem, glad i could help.

As a side note, my example allows the user to differentiate an incorrect password from an incorrect username.
Whilst this is useful for a genuine user, it can also help an attacker by letting them 1st find a genuine username, then concentrate on brute forcing the password.

It would be more secure to simply return a single error:

$user = new \DB\SQL\Mapper($this->db, 'users');

$user
->load(['username=?', $this->f3->get('POST.username']);
if$user->dry() || !\Bcrypt::instance()->verify($this->f3->get('POST.password'), $user->password){
    die( 'username or password incorrect' );
}
//verified, good to go

echo 
'Welcome back ' . $user->username;

You might also consider rate limiting your login route:


Or simply adding a delay after a failure:

if$user->dry() || !\Bcrypt::instance()->verify($this->f3->get('POST.password'), $user->password){
    sleep(2);
    die( 'username or password incorrect' );
}

This can be beaten with concurrent requests, but dealing with that is usually best done at the network level anyway (anti DOS);
Reply all
Reply to author
Forward
0 new messages