Ruby on Rails: почему зашифрованный пароль пользователя изменяется в БД, когда я использую метод «toggle» в атрибуте admin? - PullRequest
2 голосов
/ 28 марта 2011

Я только что закончил учебник Hartl по Rails, и я использую его логику аутентификации учетной записи в моем первом приложении rails.Однако, когда я создаю новую учетную запись пользователя и устанавливаю ее как учетную запись администратора, переключая атрибут admin в консоли (например, User.find (5) .toggle! (: Admin)), зашифрованный пароль, который хранится в БДмодифицируется.Зачем?

Вот логика модели пользователя ...

class User < ActiveRecord::Base

  #virtual attributes
  attr_accessor :password           

  #attrs modifiable by the outside world
  attr_accessible :name, :email, :password, :password_confirmation    

  email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

  #validations
  validates :name,  :presence => true,
                    :length   => { :maximum => 50 }
  validates :email, :presence => true,
                    :format   => { :with => email_regex },
                    :uniqueness => true
  validates :password,  :presence     => true,
                        :confirmation => true,
                        :length       => { :within => 6..40 }

  #class method that authenticates a user, used to create a session cookie
  def self.authenticate(email, submitted_password)
    user = find_by_email(email)
    return nil if user.nil?
    return user if user.has_password?(submitted_password)
  end

  #used to authenticate a signed user from a signed cookie 
  def self.authenticate_with_salt(id, cookie_salt)
    user = find_by_id(id)
    return nil if user.nil?
    return user if user.salt == cookie_salt
  end

  #callback that occurs before a record is successfully saved (meaning it has a valud password)                      
  before_save :encrypt_password

  def has_password?(submitted_password)
    encrypted_password == encrypt(submitted_password)
  end

  private

    #self keyword is required when assigning to a instance attribute
    def encrypt_password
      self.salt = make_salt if new_record?
      self.encrypted_password = encrypt(password)
    end

    def encrypt(string)
      secure_hash("#{salt}--#{string}")
    end

    def make_salt
      secure_hash("#{Time.now.utc}--#{password}")      
    end

    def secure_hash(string)
      Digest::SHA2.hexdigest(string)
    end
end

и вот как выглядит поведение ...

ruby-1.9.2-p180 :018 > User.last
 => #<User id: 12, name: "Test User A", email: "testusera@website.com", created_at: "2011-03-28 17:47:42", updated_at: "2011-03-28 17:47:42", salt: "23ca99e9848336a078e05ce9a8d904f9dfdff30dc7a38586f22...", admin: false, monthly_score: nil, encrypted_password: "50d17e6d6b581cfcfe84b61feb318705978cdf6c435626d10aa..."> 

ruby-1.9.2-p180 :019 > User.last.toggle!(:admin)
 => true 

ruby-1.9.2-p180 :020 > User.last
 => #<User id: 12, name: "Test User A", email: "testusera@website.com", created_at: "2011-03-28 17:47:42", updated_at: "2011-03-28 17:49:06", salt: "23ca99e9848336a078e05ce9a8d904f9dfdff30dc7a38586f22...", admin: true, monthly_score: nil, encrypted_password: "5d6e17f7aa73925a0099da45807f5994fa8c368a5a12d187a7d...">

Большое спасибо за вашу помощь!

Ответы [ 3 ]

5 голосов
/ 28 марта 2011

попробуйте изменить ваш метод before_save:

def encrypt_password
  if password.present?
    self.salt = make_salt if new_record?
    self.encrypted_password = encrypt(password)
  end
end

UPD . или вы можете сделать его немного короче

def encrypt_password
  self.salt = make_salt if new_record?
  self.encrypted_password = encrypt(password) if password.present?
end
0 голосов
/ 31 октября 2016

В вашем коде происходит что-то странное. Соль должна быть независимой от пароля, но ваш метод Хартла make_salt говорит:

def make_salt
  secure_hash("#{Time.now.utc}--#{password}")      
end

Эта может была источником вашей nil проблемы, так как вы обращались к password внутри make_salt; в любом случае это плохая криптография, так как она сводится к использованию Time.now в качестве «случайной» соли, которая намного легче взломать (для чего создаются радужные таблицы).

Вместо этого вы должны использовать хороший генератор случайных чисел, например, Встроенный в Ruby SecureRandom:

def make_salt
  SecureRandom.hex(64)
end

Почему такая длинная соль? Согласно https://crackstation.net/hashing-security.htm, «Чтобы злоумышленник не смог создать таблицу поиска для каждой возможной соли, соль должна быть длинной. Хорошее правило - использовать соль того же размера, что и результат хэш-функции. Например, вывод SHA256 составляет 256 бит (32 байта), поэтому соль должна быть не менее 32 случайных байтов. " Я не хочу использовать SecureRandom.random_bytes(32), чтобы избежать потенциальных проблем кодирования строк в базе данных с не-ascii-символами, а 64 случайных шестнадцатеричных символа составляют 32 случайных байта, которые, как я думаю , считают такой же энтропией.

0 голосов
/ 28 марта 2011

Не уверен, что конкретно происходит.Возможно, пароль снова шифруется с использованием другой соли (которая зависит от времени).Попробуйте добавить debugger в первую строку encrypt_password, а затем запустите тот же код из консоли, чтобы узнать, зашифровывается ли пароль при запуске toggle!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...