Authlogic: How to prevent reuse of previous password? - ruby-on-rails

how can you find out the user's past encrypted password to check if he is using the old password as the new one, in previous "Authlogic" versions it was easy to check, but in newer versions, I cannot find out the password hash, even if I know the old salt and the new password, is there a way to determine if the user is using the old password as the new one?

how can you .. check if [the user] is using the old password as the new one .. I cannot find out the password hash, even if I know the old salt and the new password ..
If you have always used the same crypto_provider, it should be fairly simple.
salt = current_user.password_salt
old_crypted_pw = current_user.crypted_password
new_plaintext_password = params[:new_password]
provider = User.crypto_provider
new_crypted_pw = provider.encrypt(new_plaintext_password, salt)
if new_crypted_pw == old_crypted_pw
raise 'Cannot reuse password'
# ...
Documentation of these methods can be found in authlogic/acts_as_authentic/password.rb.
If you are transitioning from one crypto provider to another (the transition_from_crypto_providers feature) then you must try to match against all User.crypto_providers.

Related

UserManager.AddPassword doesn't work as expected

I am using ASP.NET Identity for membership in MVC5 and have a page for the admin user to manage users and roles. I am trying to allow the admin to change the password for any other user if he needs to do so. I am using this code to perform the task:
userManager.RemovePassword(userId);
userManager.AddPassword(userId, newPassword);
Seems simple enough, but when I try to login with the new password I just created, I can't; I get the invalid password message.
I tried to investigate this issue further by trying to find out how it hashes passwords and discovered this:
string hashed = userManager.PasswordHasher.HashPassword("some password");
Firstly, I am assuming this password hasher is what AddPassword() is using internally.
Secondly, I noticed that each time I run HashPassword('some password'), I get a completely different hash returned to me.
I don't get it...
Alright, I found the problem, it's because of the following line of code in the AccountController:
var user = await UserManager.FindAsync(model.Email, model.Password);
What's happening here is that FindAsync requires a username, not an email and with the user I was testing, the username and email were different.
I wondered if maybe I changed the code in the Login action to pass in the email address and didn't remember... so I created a new MVC5 project and used Nuget to updated to the latest version... well, it looks like that's a bug! Yes, the default implementation actually passes in the email address to FindAsync. Obviously this is because when you register a new user, it sets up the username to be the email address (so they are the same and it doesn't matter). However, they didn't take into account the fact that someone may want to change their username!
Solved!

Should I accept a crypted password with AuthLogic?

Basically as the question asks.
The AuthLogic documentation for verify_password_method states:
The name of the method in your model used to verify the password. This should be an instance method. It should also be prepared to accept a raw password and a crytped password.
I'd like to support this, because it allows me to maintain our system's current masquerading behaviour without storing plain-text passwords like we have been in the past. However, it seems to me like allowing a simple string comparison to the crypted_password in the database is just as bad as storing the regular plain-text passwords.
Am I missing something here, or is there some other way to accept a crypted password in AuthLogic?
For reference, this is how I was going to write the valid_password? method:
def valid_password?(password, check_from_database = nil)
if password == self.crypted_password
true
else
super(password, check_from_database)
end
end
Ok, turns out that there's a much easier way to do this (although it seems horribly undocumented, and didn't turn up with a Google search of how to achieve this).
Authlogic::Session::Base.new(#user, true)
That line allows session creation without checking credentials. Obviously you should be careful with this since it assumes that the user has already identified themselves correctly - for my usage, since there is a check to ensure the current user is the admin user, it's safe.

Grails - different passwords for same salt in Spring Security

My web app is using Spring Security plugin for authentication and authorization. I'm building sort of an API, where I need to verify user password.
Spring Security is configured to use BCrypt with 5 logrounds and username property as salt:
grails.plugins.springsecurity.password.algorithm = 'brcypt'
grails.plugins.springsecurity.password.bcrypt.logrounds = 5
grails.plugins.springsecurity.dao.reflectionSaltSourceProperty = 'username' // password salting
Now, in my controller I'd like to verify user password and login. For this, I call springSecurityService.encodePassword(cmd.password, cmd.username)
where cmd is command object with my params. The thing is, on each request, password encoded with springSecurityService is different and never the same as user password in databse. I tried also with constant values in encodePassword call, something like this:
springSecurityService.encodePassword('foo', 'bar') and result is the same: on each request encoded password is different. This way I can't verify user password and get valid user instance from databse.
Any ideas how to solve this?
bcrypt generates a uniq salt each time, and includes it into result hash. Because of it springSecurityService.encodePasswod just ignores second argument, and reflectionSaltSourceProperty option as well (see sources). So, each time you'll get different hash for same input data.
You can use BCrypt class to validate password, like:
if (BCrypt.checkpw(candidate_password, stored_hash))
System.out.println("It matches");
else
System.out.println("It does not match");
See docs for BCrypt: http://static.springsource.org/autorepo/docs/spring-security/3.1.x/apidocs/org/springframework/security/crypto/bcrypt/BCrypt.html
Btw, as you're using Spring Security, it's already implemented in framework, so you can use passwordEncoder bean:
def passwrodEncoder
...
passwordEncoder.isPasswordValid(user.password, cmd.password, user.username) //user.username will be ignored

Decryptor for bcrypt

The title is pretty self explanatory. I'm not sure if this exists, as it would greatly compromise the security of bcrypt, but i'm using Devise in a rails app and forgot my password. However I can access the server and find the info. I can see the encrypted password and need to decrypt it.
I do not want alternate solutions to the problem, i just want a decryptor so I can get the password.
Why don't you just spin up a Rails console and re-assign your password manually?
u = User.find_by_username('myname')
u.password = u.password_confirmation = 'reset_password'
u.save
Usually something along those lines will work.

Encrypting Passwords to match stored encrypted passwords.

I started working on a project which had a lot of code already in place. It is a Ruby on Rail application that uses Devise for user authentication. One of the requirements of the application is that when a user changes their password, they are not allowed to use the same password as the last three passwords they previously used. To acomplish this, there is a table that contains a history of passwords for a given user. These passwords are copies of the encrypted passwords that existed prior to any password change on the user.
Here is where the problem comes in. We have a password change form that collects the new password for a given user. I need to be able to take the new password and encrypt it so that I can match the encrypted value of the new password against encrypted values of the old passwords in history.
Technical Stuff
Rails version 3.0.9
Devise version 1.3.4
Using standard BCrypt with Devise. bcrypt_ruby version 2.1.4
To do this we are overrriding the reset_password method supported by Devise. This allows us to introduce our own method, has_repeated_password in the user controller.
The version of has_repeated_password I started with is below:
def has_repeated_password?
return false if self.new_record? || self.version == 1
histories = self.versions.find(:all, :order => 'version DESC', :limit => 3)
histories.detect do |history|
history.encrypted_password == self.class.encryptor_class.digest(self.password, self.class.stretches, history.password_salt, self.class.pepper)
end
end
The problem here is that the encryptor class is never defined, causing an error every time this routine runs. Even through there are many examples that claim this works, I cannot get it to work when Devise is using the default encryption.
A second attempt at this is the following code:
def has_repeated_password?<br>
return false if self.new_record? || self.version == 1
histories = self.versions.find(:all, :order => 'version DESC', :limit => 3)
histories.detect do |history|
pwd = self.password_digest(self.password)
history.encrypted_password == pwd
end
end
In this case, I never get a password that matches any of the stored passwords, even though I have verified that the password in the database is what I expect.
I have been trying to dig through the Devise code to see what I can find there. I know that the autentication has to do this somehow when it matches passwords collected from users against the stored password.
Any help would be appreciated.
I think I found a solution to my own problem. The key sticking point of this was that I was trying to get an encrypted password that was not part of the user model (any more) tied to Devise. This solution does assume that Devise will be using Bcrypt as the standard encryption tool (can't remember which version of Devise made the move). Bcrypt/Devise actually buries the salt for the password in the encrypted password. If you have the salt and the pepper, you can get the same password to generate the same encrypted value.
So here is the updated code for the routine refernced above:
def has_repeated_password?
return false if self.new_record? || self.version == 1
histories = self.versions.find(:all, :order => 'version DESC', :limit => 3)
histories.detect do |history|
bcrypt = ::BCrypt::Password.new(history.encrypted_password)
password = ::BCrypt::Engine.hash_secret("#{self.password}#{self.class.pepper}", bcrypt.salt)
password == history.encrypted_password
end
end
The key here is that the Bcyrpt object has to be created with an existing encrypted password using the same salt that generated the original password. That is accomplished by giving it my stored historical encrypted password (history.encrypted_password). One of the other key elements is that both the history passwords and the proposed new password use the same pepper, which is managed by Devise. So by using the Engne.has_secret call with the intended new password, it can be compared with the history password.
I had to move the bcrypt code into here because all the password methods supported by Devise assume that you want to act on the user password of the current user object.

Resources