rails项目中使用pbkdf2存储密码

使用pbkdf2 存储密码

关键词 salt 计算次数可控 防止timing attack

class Admin < ActiveRecord::Base
  ##the table admins in db must has filed username and digest
  ##http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/PKCS5.html
  def digest=(password)
    ##strict_encode64 [a-zA-Z0-9+/ pad = ]http://www.rfc-base.org/txt/rfc-3548.txt
    salt = Base64.strict_encode64(OpenSSL::Random.random_bytes(16))
    iter = 20000
    key_len = 16
    dg = Base64.strict_encode64(OpenSSL::PKCS5.pbkdf2_hmac_sha1(password, salt, iter, key_len))
    super("#{dg}|#{salt}")
  end

  def check_password(password)
    right_digest, salt = self.digest.split('|')
    iter = 20000
    key_len = 16
    dg = Base64.strict_encode64(OpenSSL::PKCS5.pbkdf2_hmac_sha1(password, salt, iter, key_len))
    eql_time_cmp(right_digest, dg)
  end
  
  #avoid timing attacks
  def eql_time_cmp(a, b)
    unless a.length == b.length
      return false
    end
    cmp = b.bytes.to_a
    result = 0
    a.bytes.each_with_index {|c,i| #只要任一表达式的一位为 1,则结果的该位为 1。否则,结果的该位为 0。
      result |= c ^ cmp[i] #当且仅当只有一个表达式的某位上为 1 时,结果的该位才为 1。否则结果的该位为 0。
    }
    result == 0
  end

  class << self

    def create_administrator(username, password)
      admin = Admin.new(username: username)
      admin.digest = password
      admin.save
    end

    def check_administrator(username, password)
     admin = Admin.find_by(username:username)
     admin && admin.check_password(password)
    end
  end
end

timing attack

http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/PKCS5.html
在这里看到的
知乎上对 timing attack 的一些观点https://www.zhihu.com/question/20156213

这是看过的最好一篇关于密码存储的文章http://blog.jobbole.com/61872/

你可能感兴趣的:(rails项目中使用pbkdf2存储密码)