jakeb jakeb - 2 months ago 9x
PHP Question

Convert PHP's password_verify to Ruby on Rails

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:


password_hash($user['salt'] . $password . $user['salt'], PASSWORD_DEFAULT);


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


Any ideas would be greatly appreciated


The format of a PHP password_hash password looks roughly like this:


The default Ruby Bcrypt method produces passwords of the form:


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
  # Unexpected type?

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.