最好的灵活rails密码安全实现

时间:2014-02-26 20:24:38

标签: ruby-on-rails ruby security gem passwords

我需要实现具有各种灵活要求的密码安全性。要求主要取自Sans password policy

  

强密码具有以下特征:

     

包含以下五个字符类中的至少三个:

     
      
  • 小写字符
  •   
  • 大写字符
  •   
  • 数字
  •   
  • 标点符号
  •   
  • “特殊”字符(例如@#$%^& *()_ + |〜 - =`{} []:“;'<> / etc)
  •   
  • 至少包含十五个字母数字字符。
  •   

还要求不允许用户的电子邮件地址出现在密码中。

理想情况下,我想要一个可以处理这个问题的宝石 - 它将被更广泛地测试和使用,并且我们不太可能有错误。

如果没有宝石涵盖这一点,那么处理这些要求的最佳和最强化方法是什么?理想情况下,我们可以说我们不仅安全,而且安全,因为我们有行业标准的实施。

到目前为止,我发现的宝石是:

  • Strong Password,它只是提供熵检查,让用户知道他们的密码是否很强。

(我们正在使用Rails 3.2.17和Ruby 1.9.3,但我们计划很快转向Rails 4和Ruby 2,因此也欢迎更新的解决方案。)

2 个答案:

答案 0 :(得分:12)

我们一直在使用设计安全扩展一段时间了。它有许多有趣的功能(例如密码历史记录,密码过期...)。

对于密码复杂性,我们想要一些更具可配置性的东西(在您的示例中:让客户选择他们想要的复杂程度)。

所以我们根据得分推出了我们自己的解决方案:5个中的3个(在你的例子中)意味着我们正在测试每个字符,如果找到则给出一个点。 如果总分等于或大于所需分数,则密码正常。

就代码而言,我们的验证如下:

validate :password_complexity

def password_complexity
 return if password.nil?

 if password.size < 8
   errors.add :password, "Must be at least 8 characters long."
   return
 end

 required_complexity = 3 # we're actually storing this in the configuration of each customer

 if !CheckPasswordComplexityService.new(password, required_complexity).valid?
   errors.add :password, "Your password does not match the security requirements."
 end
end

并且检查复杂性的服务如下所示:

class CheckPasswordComplexityService

  attr_reader :password, :required_complexity

  def initialize(password, required_complexity)
    @password = password
    @required_complexity = required_complexity
  end

  def valid?
    score = has_uppercase_letters? + has_digits? + has_extra_chars? + has_downcase_letters?

    score >= required_complexity
  end

  private

  def has_uppercase_letters?
    password.match(/[A-Z]/) ? 1 : 0
  end

  def has_digits?
    password.match(/\d/) ? 1 : 0
  end

  def has_extra_chars?
    password.match(/\W/) ? 1 : 0
  end

  def has_downcase_letters?
    password.match(/[a-z]{1}/) ? 1 : 0
  end
end

然后,添加一些您想要检查的新特征变得非常容易。

答案 1 :(得分:5)

我认为宝石在这里使用是不正确的。只需使用验证:

validate :secure_password

def secure_password
    return false if (password =~ /[a-z]/).blank? #lower letter test
    return false if (password =~ /[A-Z]/).blank? #upper letter test
    return false if (password =~ /[0-9]/).blank? #number test
    ...
end