I have a PHP application I'm converting to Grails. The PHP application used a salt-mechanism to encode the passwords of all its users.
When moving the salt and salted password into the (custom) user database in Grails, I am unable to log in while in my Grails application.
Of course, I'm using the Spring Security Core plugin, and I've added the salt to the User domain class as specified in this tutorial, which I found here: Grails with Spring Security Plugin and Salted Passwords
After running through the tutorial, I am able to add a user and successfully log in with that user:
[BootStrap.groovy]:
new User( username:"user", email:"user#place.com", password:"password", enabled:true).save(flush: true)
(you might also notice the addition of email, which I added using this tutorial)
But I am unable to login using any of the users that were transferred over from the PHP project. If it's any help, here's how their passwords were encoded:
$password = "password";
$salt = bin2hex( openssl_random_pseudo_bytes( 32 ) );
$passwordSalted = hash( "sha256", $salt . $password );
Looks like Burt nailed it with his suggestion here: http://grails.1312388.n4.nabble.com/Spring-Security-Core-plugin-and-multiple-salt-sources-tc3638236.html#a3646256
Basically, I already had my own salt provided from the tutorials I mentioned above, I just needed to combine it with the password using a custom password encoder.
public class CustomPasswordEncoder extends MessageDigestPasswordEncoder {
public CustomPasswordEncoder() {
super("SHA-256");
}
#Override
protected String mergePasswordAndSalt(String password, Object salt, boolean strict) {
if (password == null) {
password = "";
}
if (salt == null || "".equals(salt)) {
return password;
}
return salt + password;
}
}
Related
I've been spending a few days troubleshooting a failure of certain passwords to validate in Laravel 9. The password testperson resolves to the hash $2y$10$5xc/wAmNCKV.YhpWOfyNoetCj/r3Fs5TyAskgZuIF/LEItWfm7rPW. A direct query on the corresponding database table confirms that this is the correct hash. Yet Laravel's authentication infrastructure rejects this password and denies authentication.
This is not universal. I have multiple passwords that are resolving correctly. For example, the password eo resolves to $2y$10$uNWYvMVmagIwQ2eXnVKLCOAK1QFQdcRtxbvlghf.Xpg0U1w.N./N2, and Laravel authenticates that password. The same mechanism creates both of these user records, though they have different permissions (indicated by boolean values on the record).
I tracked down the bug to the function password_verify, which was identified as returning false negatives in this Stack Overflow question and this Treehouse thread.
Specifically, here is the stack in Laravel that gets down to this failure point:
The login route calls \Illuminate\Foundation\Auth\AuthenticatesUsers::login via the controller class.
The login method calls \Illuminate\Foundation\Auth\AuthenticatesUsers::attemptLogin.
The attemptLogin method calls the attempt method of the controller's guard object.
\Illuminate\Auth\SessionGuard::attempt calls \Illuminate\Auth\SessionGuard::hasValidCredentials.
\Illuminate\Auth\SessionGuard::hasValidCredentials calls the validateCredentials method on the guard's provider object.
Illuminate\Auth\EloquentUserProvider::validateCredentials calls the check method on its hasher object.
Illuminate\Hashing\HashManager::check calls the check method on its driver.
Illuminate\Hashing\BcryptHasher::check calls Illuminate\Hashing\AbstractHasher::check.
Illuminate\Hashing\AbstractHasher::check calls password_verify.
After unwinding this entire stack, I ran the following code in the login method of the login controller:
$provider = $this->guard()->getProvider();
$credentials = $this->credentials($request);
$user = $provider->retrieveByCredentials($credentials);
$password_unhashed = $request['password'];
$password_hashed = $user->getAuthPassword();
$password_verify = password_verify($password_unhashed, $password_hashed);
logger('attemping login', compact('password_verify','password_unhashed','password_hashed'));
That dumps this context:
{
"password_verify": false,
"password_unhashed": "testperson",
"password_hashed": "$2y$10$5xc/wAmNCKV.YhpWOfyNoetCj/r3Fs5TyAskgZuIF/LEItWfm7rPW"
}
And if I put that password into a SELECT users WHERE password= query, I get the user that I'm expecting.
What's going on here? And how do I get around this?
I think your assertion that the hash you provided is a hash of 'testperson' is in fact false. Since hashing is one-way, I can't tell you what the hash you showed is derived from. NOTE: This runs on PHP 7.4, but I don't think it will work on PHP 8 and beyond because of the deprecation of the salt option in password_hash().
<?php
//$testhash = '$2y$10$5xc/wAmNCKV.YhpWOfyNoetCj/r3Fs5TyAskgZuIF/LEItWfm7rPW';
$testhash = '$2y$10$uNWYvMVmagIwQ2eXnVKLCOAK1QFQdcRtxbvlghf.Xpg0U1w.N./N2';
//$password = "testperson";
$password = "eo";
$options = array("cost" => 10, "salt" => substr($testhash, 7, 22));
$pwhash = password_hash($password, PASSWORD_BCRYPT, $options);
echo $pwhash."\n";
$salt = substr($pwhash, 0, 29);
echo $salt."\n";
$cryptpw = crypt($password, $salt);
echo $cryptpw."\n";
if (password_verify($password, $cryptpw)) {
echo("Verified.\n");
} else {
echo("NOT Verified.\n");
}
if (password_needs_rehash($cryptpw, PASSWORD_BCRYPT, $options)) {
echo("Needs rehash.\n");
} else {
echo("Doesn't need rehash.\n");
}
/*
testperson results...
$2y$10$5xc/wAmNCKV.YhpWOfyNoeVNPMEcYrxepQeFAssFoAaIYs4WLmgZO
$2y$10$5xc/wAmNCKV.YhpWOfyNoe
$2y$10$5xc/wAmNCKV.YhpWOfyNoeVNPMEcYrxepQeFAssFoAaIYs4WLmgZO
Verified.
Doesn't need rehash.
eo results...
$2y$10$uNWYvMVmagIwQ2eXnVKLCOAK1QFQdcRtxbvlghf.Xpg0U1w.N./N2
$2y$10$uNWYvMVmagIwQ2eXnVKLCO
$2y$10$uNWYvMVmagIwQ2eXnVKLCOAK1QFQdcRtxbvlghf.Xpg0U1w.N./N2
Verified.
Doesn't need rehash.
*/
?>
I have a call to Hash::make in the observer for the user class. I discovered that it was running even though it wasn't supposed to, resulting in a duplicate hash.
I have set the encryption key in config.php file
$config['encryption_key'] = 'az2x#_.#!`~$aezxqy+=#%^&';
I want to use this key in password when registering users into the website, right now i am using this method
'password' => hash('sha256', $password . 'az2x#_.#!`~$aezxqy+=#%^&')
Is there any way i can set the above code like
'password' => hash('sha256', $password . $config['encryption_key']),
when i do it gives error of undefined $config variable.
I strongly suggest using the built-in php API for password salt/hash and not the method you are using, the encryption key is meant to be used for the encryption class...
$passwordHash = password_hash($password, PASSWORD_BCRYPT);
You use this salted and hashed password stored in the database.
Then verify it when getting it from the DB using:
password_verify($password, $result->password)
This returns a BOOLEAN so you use it in an if statement:
if ( password_verify($password, $result->password) ) {
// do login stuff
} else {
// handle login failure
}
Else, you can use the encryption key by the following (strongly suggest against it):
$this->config->item('encryption_key');
If this doesn't work you are probably writing a helper or something, so you'll need to call the CI instance to get access to the config array.
I tried to reset password of wordpress admin panel.
I type some password and select function MD5.
It shows exact md5 conversion of the given string.
After log-in in admin panel of wordpress, if we browse the table it converts the password in some other string.
Can any one guide me about the password technique ?
Thanks
Quoting from the Wordpress docs
Creates a hash of a plain text password. Unless the global $wp_hasher is set, the default implementation uses PasswordHash, which adds salt to the password and hashes it with 8 passes of MD5. MD5 is used by default because it's supported on all platforms. You can configure PasswordHash to use Blowfish or extended DES (if available) instead of MD5 with the $portable_hashes constructor argument or property (see examples).
It uses the wp_hash_password function.
function wp_hash_password($password) {
global $wp_hasher;
if ( empty($wp_hasher) ) {
require_once( ABSPATH . WPINC . '/class-phpass.php');
// By default, use the portable hash from phpass
$wp_hasher = new PasswordHash(8, true);
}
return $wp_hasher->HashPassword( trim( $password ) );
}
The source coude of the "class-phpass.php" file can be found on the site: https://core.trac.wordpress.org/browser/tags/4.3.1/src/wp-includes/class-phpass.php
I add a user to the database.
I encode their password using bcrypt encryption:
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($user->getPassword(),$user->getSalt());
$postData->setPassword($password);
This all persists to the database just fine.
On my User Entity my getSalt() method returns null:
public function getSalt(){
return null;
}
Now when I try to authenticate the symfony logs tells me that I am entering bad credentials.
However...
If i use this site to encrypt my password with bcrypt:
http://bcrypthashgenerator.apphb.com/
Then enter the result into the database manually and try and authenticate with the users password i have updated with the results from the above link,
It works fine.
So there is something I have not set, that when I am encrypting the password that symfony cannot authenticate against it.
I have tried the setting a salt on the getSalt(), setting it to an empty string.
EDIT
I am using php 5.5.9
I have also tried using the BCryptPasswordEncoder($cost) class,
setting the cost on the __construct the same as what's in my security.yml
and it still does not authenticate.
EDIT
I have also tried installing this class via composer to as suggested here.
EDIT
If I use the php method:
password_hash('testertester',PASSWORD_BCRYPT,array('cost'=>13));
It authenticates perfectly.
So its something to do deeper in Symfony. I'll keep digging.
Still no luck :-/
I am scouring the web to no avail so far.
Any help would be greatly appreciated.
Ad
I was passing in the existing entity object not the submitted data object!!
$postData = $form->getdata();
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($user->getPassword(),$user->getSalt());
$postData->setPassword($password);
Note this line here:
$user->getPassword() needed to be $postData->getPassword()
$password = $encoder->encodePassword($postData->getPassword(),$user->getSalt());
You live and learn ay!
I am using bcrypt to hash my passwords and it seems that symfony2 authentication system is not production the same hash as php's native crypt function. Bellow is the salt that I am generating for my user password:
$salt = '$2y$13$' . substr(md5(uniqid(rand(), true)),0,21) . '$';
$this->setPassword('test',$salt);
In my security.yml file I am simply doing:
encoders:
Blogger\BlogBundle\Entity\User:
algorithm: bcrypt
iterations: 13
Is there any reason why the two encoding methods would generate different hashes? The library I am using is ircmaxell/password-compat.
Best way to use this within Symfony2 is to use get the encoder.
use \Blogger\BlogBundle\Entity\User;
$user = new User();
$encoderFactory = $this->get('security.encoder_factory');
$encoder = $encoderFactory->getEncoder($user);
$salt = 'salt'; // this should be different for every user
$password = $encoder->encodePassword('password', $salt);
$user->setSalt($salt);
$user->setPassword($password);
If you are using FOSUserBundle, you should use:
use \Blogger\BlogBundle\Entity\User;
$userManager = $this->get('fos_user_manager');
$password = 'password';
$user = new User();
$user->setPlainPassword($password);
$userManager->updateUser($user, true); // second argument tells user manager to flush
After reviewing the source code for Symfony2.3 implementation of bcrypt, they use a function called hash_algorithm() and it seems to yield different results than crypt(). Both use $2y$ versions of bcrypt and I had set the cost for both algorithms to 13 ... however it is more consistent to do the following for setting passwords instead:
$user->setPassword(password_hash($user->getPassword(), PASSWORD_BCRYPT, array('cost' => 13)));
That line of code seemed to fix my problem. The best part is that I don't even have to generate my salt any more.