-
Notifications
You must be signed in to change notification settings - Fork 5.5k
How To: Disallow previously used passwords
coneybeare edited this page Mar 2, 2011
·
6 revisions
This is achieved by storing records of previous password hashes, then checking them as a validation. I am using bcrypt on version 1.1.7 here, and rails 3. If you are using a different encryptor or version, then you might have to adjust the methods to suit your needs, but the concept is the same.
Create a new db model to hold the hashes
# db/migrations/<TIMESTAMP>_create_password_histories.rb
class CreatePasswordHistories < ActiveRecord::Migration
def self.up
create_table(:password_histories) do |t|
t.integer :user_id
t.string :encrypted_password
t.string :password_salt
t.timestamps
end
end
def self.down
drop_table :password_histories
end
end
# models/user.rb
include ActiveModel::Validations
has_many :password_histories
after_save :store_digest
validates :password, :unique_password => true
private
def store_digest
if encrypted_password_changed?
PasswordHistory.create(:user => self, :encrypted_password => encrypted_password, :password_salt => password_salt)
end
end
# models/unique_password_validator.rb
require 'bcrypt'
class UniquePasswordValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.password_histories.each do |ph|
hashed_value = BCrypt::Engine.hash_secret([value, Devise.pepper].join, ph.password_salt, Devise.stretches)
record.errors[attribute] << "has been used previously." and return if hashed_value == ph.encrypted_password
end
end
end
Thats it! If you are not using bcrypt, look into the Devise source code to figure out how to recreate the hashed_value
for your validation.