|
1 | 1 | # frozen_string_literal: true |
2 | 2 |
|
3 | 3 | require 'bcrypt' |
| 4 | +require 'digest' |
4 | 5 |
|
5 | 6 | module Devise |
6 | 7 | module Encryptor |
7 | 8 | def self.digest(klass, password) |
8 | 9 | if klass.pepper.present? |
9 | 10 | password = "#{password}#{klass.pepper}" |
10 | 11 | end |
| 12 | + # This converts the password (of any length) into a fixed |
| 13 | + # 64-character hex string, safely under the 72-char limit |
| 14 | + password = Digest::SHA256.hexdigest(password) |
| 15 | + |
| 16 | + # BCrypt the pre-hashed string |
11 | 17 | ::BCrypt::Password.create(password, cost: klass.stretches).to_s |
12 | 18 | end |
13 | 19 |
|
| 20 | + # Compares a potential password with a stored hash. |
| 21 | + # |
| 22 | + # It attempts the new (SHA-256 -> BCrypt) method first. |
| 23 | + # If that fails, it falls back to the old (direct BCrypt) method |
| 24 | + # to support existing passwords that were not pre-hashed |
14 | 25 | def self.compare(klass, hashed_password, password) |
15 | 26 | return false if hashed_password.blank? |
16 | | - bcrypt = ::BCrypt::Password.new(hashed_password) |
| 27 | + |
| 28 | + begin |
| 29 | + bcrypt = ::BCrypt::Password.new(hashed_password) |
| 30 | + rescue ::BCrypt::Errors::InvalidHash |
| 31 | + return false |
| 32 | + end |
| 33 | + |
17 | 34 | if klass.pepper.present? |
18 | 35 | password = "#{password}#{klass.pepper}" |
19 | 36 | end |
20 | | - password = ::BCrypt::Engine.hash_secret(password, bcrypt.salt) |
21 | | - Devise.secure_compare(password, hashed_password) |
| 37 | + |
| 38 | + # This is for passwords created with the new `digest` method. |
| 39 | + pre_hashed_password = Digest::SHA256.hexdigest(password) |
| 40 | + new_style_hash = ::BCrypt::Engine.hash_secret(pre_hashed_password, bcrypt.salt) |
| 41 | + |
| 42 | + return true if Devise.secure_compare(new_style_hash, hashed_password) |
| 43 | + |
| 44 | + # This is for passwords created before this change |
| 45 | + # We re-run the original logic |
| 46 | + old_style_hash = ::BCrypt::Engine.hash_secret(password, bcrypt.salt) |
| 47 | + Devise.secure_compare(old_style_hash, hashed_password) |
22 | 48 | end |
23 | 49 | end |
24 | 50 | end |
0 commit comments