Major revamp of security. Passwords are first SHA1 hashed and then

that hash is bcrypted.  Bcrypted hashes are stored in a new column on
users.  This separate column is only to allow for rollbacks,
eventually the old SHA1 hash column will be removed.  Sensitive cookie
details are now encrypted to prevent user tampering and more stringent
checks on secret_token and session_secret_key are enforced.
This commit is contained in:
albert
2013-03-04 22:55:41 -05:00
parent bae5835cff
commit f52181db94
13 changed files with 108 additions and 68 deletions

View File

@@ -15,8 +15,8 @@ class SessionCreator
user.update_column(:last_logged_in_at, Time.now)
if remember.present?
cookies[:user_name] = {:expires => 1.year.from_now, :value => user.name}
cookies[:cookie_password_hash] = {:expires => 1.year.from_now, :value => user.cookie_password_hash}
cookies.permanent.signed[:user_name] = user.name
cookies.permanent.signed[:password_hash] = user.bcrypt_password_hash
end
session[:user_id] = user.id

View File

@@ -32,7 +32,7 @@ private
end
def load_cookie_user
CurrentUser.user = User.find_by_name(cookies[:user_name])
CurrentUser.user = User.find_by_name(cookies.signed[:user_name])
CurrentUser.ip_addr = request.remote_ip
end
@@ -41,7 +41,7 @@ private
end
def cookie_password_hash_valid?
cookies[:cookie_password_hash] && User.authenticate_cookie_hash(cookies[:user_name], cookies[:cookie_password_hash])
cookies[:password_hash] && User.authenticate_cookie_hash(cookies.signed[:user_name], cookies.signed[:password_hash])
end
def update_last_logged_in_at

View File

@@ -121,19 +121,24 @@ class User < ActiveRecord::Base
end
module PasswordMethods
def bcrypt_password
BCrypt::Password.new(bcrypt_password_hash)
end
def encrypt_password_on_create
self.password_hash = User.sha1(password)
self.password_hash = ""
self.bcrypt_password_hash = User.bcrypt(password)
end
def encrypt_password_on_update
return if password.blank?
return if old_password.blank?
if User.sha1(old_password) == password_hash
self.password_hash = User.sha1(password)
if bcrypt_password == User.sha1(old_password)
self.bcrypt_password_hash = User.bcrypt(password)
return true
else
errors[:old_password] << "is incorrect"
errors[:old_password] = "is incorrect"
return false
end
end
@@ -149,7 +154,7 @@ class User < ActiveRecord::Base
end
pass << rand(100).to_s
update_column(:password_hash, User.sha1(pass))
update_column(:bcrypt_password_hash, User.bcrypt(pass))
pass
end
@@ -168,29 +173,31 @@ class User < ActiveRecord::Base
end
def authenticate_hash(name, hash)
where(["lower(name) = ? AND password_hash = ?", name.downcase, hash]).first != nil
user = find_by_name(name)
if user && user.bcrypt_password == hash
user
else
nil
end
end
def authenticate_cookie_hash(name, hash)
user = User.find_by_name(name)
return nil if user.nil?
return hash == user.cookie_password_hash
user = find_by_name(name)
if user && user.bcrypt_password_hash == hash
user
else
nil
end
end
def bcrypt(pass)
BCrypt::Password.create(sha1(pass))
end
def sha1(pass)
Digest::SHA1.hexdigest("#{Danbooru.config.password_salt}--#{pass}--")
end
end
def cookie_password_hash
hash = password_hash
(name.size + 8).times do
hash = User.sha1(hash)
end
return hash
end
end
module FavoriteMethods
@@ -456,7 +463,7 @@ class User < ActiveRecord::Base
module ApiMethods
def hidden_attributes
super + [:password_hash, :email, :email_verification_key]
super + [:password_hash, :bcrypt_password_hash, :email, :email_verification_key]
end
def serializable_hash(options = {})