Reputation: 43
We're in the process of converting our site from an old PHP framework to Rails, and would really like for users to continue being able to login with their old password. On the old site, we're using password_hash and password_verify to hash and verify the passwords. However, on Rails I can't seem to get it to verify the old password.
Here is what we have in PHP:
Hash:
password_hash($user['salt'] . $password . $user['salt'], PASSWORD_DEFAULT);
Verify:
password_verify($user['salt'] . $password . $user['salt'], $user['password'])
On the new Rails framework we're using Devise and have built a custom migration script to move everything over and identify the correct password hashing method based on a password_version stored in the db, and this is what I'm using inside my User model:
def valid_password?(password)
if password_version == 'legacy'
hash = BCrypt::Password.new(encrypted_password)
hash_str = password_salt+password+password_salt
return hash.is_password? hash_str
end
super(password)
end
Any ideas would be greatly appreciated
Upvotes: 4
Views: 822
Reputation: 211610
The format of a PHP password_hash
password looks roughly like this:
$2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a
The default Ruby Bcrypt method produces passwords of the form:
$2a$10$GtKs1Kbsig8ULHZzO1h2TetZfhO4Fmlxphp8bVKnUlZCBYYClPohG
For a clean solution here you can always differentiate between the two by the $2y
or $2a
prefix. There's no need for a format column when it's already baked into the format.
For example:
case (encrypted_password[0,3])
when '$2y'
# Legacy PHP password
BCrypt::Password.new(encrypted_password.sub(/\A\$2y/, '$2a')).is_password?(salt + password + salt)
when '$2a'
# Ruby BCrypt password
BCrypt::Password.new(encrypted_password).is_password?(password)
else
# Unexpected type?
end
What you'll want to do on a successful verification of password is re-write the password to the database using Ruby's method to gradually replace all the old PHP formatted ones.
Upvotes: 5