44

确保用户提供的密码是注册或更改密码表单中的强密码的最佳方法是什么?

我有一个想法(在python中)

def validate_password(passwd):
    conditions_met = 0
    conditions_total = 3
    if len(passwd) >= 6: 
        if passwd.lower() != passwd: conditions_met += 1
        if len([x for x in passwd if x.isdigit()]) > 0: conditions_met += 1
        if len([x for x in passwd if not x.isalnum()]) > 0: conditions_met += 1
    result = False
    print conditions_met
    if conditions_met >= 2: result = True
    return result
4

11 回答 11

16

根据语言的不同,我通常使用正则表达式来检查它是否具有:

  • 至少一个大写字母和一个小写字母
  • 至少一个数字
  • 至少一个特殊字符
  • 长度至少为六个字符

您可以要求以上所有内容,或使用强度计类型的脚本。对于我的强度计,如果密码长度合适,则评估如下:

  • 满足一个条件:弱密码
  • 满足两个条件:中等密码
  • 满足所有条件:强密码

您可以调整以上内容以满足您的需求。

于 2008-09-16T18:02:24.570 回答
10

面向对象的方法将是一组规则。为每个规则分配一个权重并遍历它们。在伪代码中:

abstract class Rule {

    float weight;

    float calculateScore( string password );

}

计算总分:

float getPasswordStrength( string password ) {     

    float totalWeight = 0.0f;
    float totalScore  = 0.0f;

    foreach ( rule in rules ) {

       totalWeight += weight;
       totalScore  += rule.calculateScore( password ) * rule.weight;

    }

    return (totalScore / totalWeight) / rules.count;

}

基于存在的字符类数量的示例规则算法:

float calculateScore( string password ) {

    float score = 0.0f;

    // NUMBER_CLASS is a constant char array { '0', '1', '2', ... }
    if ( password.contains( NUMBER_CLASS ) )
        score += 1.0f;

    if ( password.contains( UPPERCASE_CLASS ) )
        score += 1.0f;

    if ( password.contains( LOWERCASE_CLASS ) )
        score += 1.0f;

    // Sub rule as private method
    if ( containsPunctuation( password ) )
        score += 1.0f;

    return score / 4.0f;

}
于 2008-09-16T18:32:22.453 回答
7

1:消除常用密码
根据常用密码列表检查输入的密码(参见泄露的 LinkedIn 密码列表中的前 100.000 个密码:http: //www.adeptus-mechanicus.com/codex/linkhap/combo_not.zip ),请确保包含leetspeek 替换:A@、E3、B8、S5 等。
在进入下面的第 2 部分之前,从输入的短语中删除与此列表匹配的部分密码。

2:不要对用户强加任何规则

密码的黄金法则是越长越好。
忘记强制使用大写字母、数字和符号,因为(绝大多数)用户会: - 将第一个字母设为大写;- 把数字1放在最后;-!如果需要符号,请在其后添加。

而是检查密码强度

对于一个体面的起点,请参阅: http: //www.passwordmeter.com/

我建议至少遵循以下规则:

Additions (better passwords)
-----------------------------
- Number of Characters              Flat       +(n*4)   
- Uppercase Letters                 Cond/Incr  +((len-n)*2)     
- Lowercase Letters                 Cond/Incr  +((len-n)*2)     
- Numbers                           Cond       +(n*4)   
- Symbols                           Flat       +(n*6)
- Middle Numbers or Symbols         Flat       +(n*2)   
- Shannon Entropy                   Complex    *EntropyScore

Deductions (worse passwords)
----------------------------- 
- Letters Only                      Flat       -n   
- Numbers Only                      Flat       -(n*16)  
- Repeat Chars (Case Insensitive)   Complex    -    
- Consecutive Uppercase Letters     Flat       -(n*2)   
- Consecutive Lowercase Letters     Flat       -(n*2)   
- Consecutive Numbers               Flat       -(n*2)   
- Sequential Letters (3+)           Flat       -(n*3)   
- Sequential Numbers (3+)           Flat       -(n*3)   
- Sequential Symbols (3+)           Flat       -(n*3)
- Repeated words                    Complex    -       
- Only 1st char is uppercase        Flat       -n
- Last (non symbol) char is number  Flat       -n
- Only last char is symbol          Flat       -n

仅仅跟随passwordmeter是不够的,因为它的幼稚算法确实看起来Password1!不错,但它却异常弱。确保在评分时忽略首字母大写以及尾随数字和符号(根据最后 3 条规则)。

计算香农熵
请参阅:在 Python 中计算熵的最快方法

3:不允许任何太弱
的密码与其强迫用户屈从于弄巧成拙的规则,不如允许任何能给出足够高分数的密码。多高取决于您的用例。

最重要
的是 ,当您接受密码并将其存储在数据库中时,请确保对其进行加盐和哈希处理!.

于 2018-05-23T13:42:23.527 回答
3

要检查的两个最简单的指标是:

  1. 长度。我会说至少8个字符。
  2. 密码包含的不同字符类别的数量。这些通常是小写字母、大写字母、数字和标点符号等符号。强密码将包含至少三个这些类别的字符;如果您强制使用数字或其他非字母字符,则会显着降低字典攻击的有效性。
于 2008-09-16T18:04:19.760 回答
2

Cracklib 很棒,在较新的包中,有一个 Python 模块可供使用。然而,在还没有它的系统上,比如 CentOS 5,我已经为系统 cryptlib 编写了一个 ctypes 包装器。这也适用于无法安装 python-libcrypt 的系统。它确实需要具有可用 ctypes 的 python,因此对于 CentOS 5,您必须安装和使用 python26 软件包。

它还有一个优点,它可以获取用户名并检查包含它或基本相似的密码,例如 libcrypt “FascistGecos” 功能,但不需要用户存在于 /etc/passwd 中。

我的ctypescracklib 库在 github 上可用

一些示例用途:

>>> FascistCheck('jafo1234', 'jafo')
'it is based on your username'
>>> FascistCheck('myofaj123', 'jafo')
'it is based on your username'
>>> FascistCheck('jxayfoxo', 'jafo')
'it is too similar to your username'
>>> FascistCheck('cretse')
'it is based on a dictionary word'
于 2010-11-16T04:05:10.447 回答
2

在阅读了其他有用的答案后,这就是我要做的:

-1 与用户名相同
+0 包含用户名
+1 超过 7 个字符
+1 超过 11 个字符
+1 包含数字
+1 大小写混合
+1 包含标点符号
+1 不可打印字符

pwcore.py:

import re
import string
max_score = 6
def score(username,passwd):
    if passwd == username:
        return -1
    if username in passwd:
        return 0
    score = 0
    if len(passwd) > 7:
        score+=1
    if len(passwd) > 11:
        score+=1
    if re.search('\d+',passwd):
        score+=1
    if re.search('[a-z]',passwd) and re.search('[A-Z]',passwd):
        score+=1
    if len([x for x in passwd if x in string.punctuation]) > 0:
        score+=1
    if len([x for x in passwd if x not in string.printable]) > 0:
        score+=1
    return score

示例用法:

import pwscore
    score = pwscore(username,passwd)
    if score < 3:
        return "weak password (score=" 
             + str(score) + "/"
             + str(pwscore.max_score)
             + "), try again."

可能不是最有效的,但似乎是合理的。不确定 FascistCheck => '与用户名太相似' 是否值得。

'abc123ABC!@£' = 如果不是用户名的超集,则得分 6/6

也许那应该得分更低。

于 2010-11-28T19:55:31.107 回答
1

好吧,这就是我使用的:

   var getStrength = function (passwd) {
    intScore = 0;
    intScore = (intScore + passwd.length);
    if (passwd.match(/[a-z]/)) {
        intScore = (intScore + 1);
    }
    if (passwd.match(/[A-Z]/)) {
        intScore = (intScore + 5);
    }
    if (passwd.match(/\d+/)) {
        intScore = (intScore + 5);
    }
    if (passwd.match(/(\d.*\d)/)) {
        intScore = (intScore + 5);
    }
    if (passwd.match(/[!,@#$%^&*?_~]/)) {
        intScore = (intScore + 5);
    }
    if (passwd.match(/([!,@#$%^&*?_~].*[!,@#$%^&*?_~])/)) {
        intScore = (intScore + 5);
    }
    if (passwd.match(/[a-z]/) && passwd.match(/[A-Z]/)) {
        intScore = (intScore + 2);
    }
    if (passwd.match(/\d/) && passwd.match(/\D/)) {
        intScore = (intScore + 2);
    }
    if (passwd.match(/[a-z]/) && passwd.match(/[A-Z]/) && passwd.match(/\d/) && passwd.match(/[!,@#$%^&*?_~]/)) {
        intScore = (intScore + 2);
    }
    return intScore;
} 
于 2011-09-02T15:02:49.987 回答
0

我不知道是否有人会觉得这很有用,但我真的很喜欢 phear 建议的规则集的想法,所以我编写了一个规则 Python 2.6 类(尽管它可能与 2.5 兼容):

import re

class SecurityException(Exception):
    pass

class Rule:
    """Creates a rule to evaluate against a string.
    Rules can be regex patterns or a boolean returning function.
    Whether a rule is inclusive or exclusive is decided by the sign
    of the weight. Positive weights are inclusive, negative weights are
    exclusive. 


    Call score() to return either 0 or the weight if the rule 
    is fufilled. 

    Raises a SecurityException if a required rule is violated.
    """

    def __init__(self,rule,weight=1,required=False,name=u"The Unnamed Rule"):
        try:
            getattr(rule,"__call__")
        except AttributeError:
            self.rule = re.compile(rule) # If a regex, compile
        else:
            self.rule = rule  # Otherwise it's a function and it should be scored using it

        if weight == 0:
            return ValueError(u"Weights can not be 0")

        self.weight = weight
        self.required = required
        self.name = name

    def exclusive(self):
        return self.weight < 0
    def inclusive(self):
        return self.weight >= 0
    exclusive = property(exclusive)
    inclusive = property(inclusive)

    def _score_regex(self,password):
        match = self.rule.search(password)
        if match is None:
            if self.exclusive: # didn't match an exclusive rule
                return self.weight
            elif self.inclusive and self.required: # didn't match on a required inclusive rule
                raise SecurityException(u"Violation of Rule: %s by input \"%s\"" % (self.name.title(), password))
            elif self.inclusive and not self.required:
                return 0
        else:
            if self.inclusive:
                return self.weight
            elif self.exclusive and self.required:
                raise SecurityException(u"Violation of Rule: %s by input \"%s\"" % (self.name,password))
            elif self.exclusive and not self.required:
                return 0

        return 0

    def score(self,password):
        try:
            getattr(self.rule,"__call__")
        except AttributeError:
            return self._score_regex(password)
        else:
            return self.rule(password) * self.weight

    def __unicode__(self):
        return u"%s (%i)" % (self.name.title(), self.weight)

    def __str__(self):
        return self.__unicode__()

我希望有人觉得这很有用!

示例用法:

rules = [ Rule("^foobar",weight=20,required=True,name=u"The Fubared Rule"), ]
try:
    score = 0
    for rule in rules:
        score += rule.score()
except SecurityException e:
    print e 
else:
    print score

免责声明:未经单元测试

于 2009-12-09T09:00:01.137 回答
-1

密码强度检查器,如果您有时间+资源(仅当您检查多个密码时才合理),请使用 Rainbow Tables。

于 2008-09-16T17:58:12.800 回答
-1

通过一系列检查以确保其符合最低标准:

  • 至少 8 个字符长
  • 包含至少一个非字母数字符号
  • 不匹配或不包含用户名/电子邮件/等。
  • ETC

这是一个报告密码强度的 jQuery 插件(我自己没有尝试过): http: //phiras.wordpress.com/2007/04/08/password-strength-meter-a-jquery-plugin/

同样的东西移植到 PHP: http ://www.alixaxel.com/wordpress/2007/06/09/php-password-strength-algorithm/

于 2008-09-16T17:58:28.370 回答
-1

确保用户提供的密码是注册或更改密码表单中的强密码的最佳方法是什么?

不要评估复杂性和/或强度,用户会找到一种方法来欺骗您的系统或感到沮丧以至于他们会离开。那只会让你遇到这样的情况。只需要一定的长度并且不使用泄露的密码。加分项:确保您实施的任何内容都允许使用密码管理器和/或 2FA。

于 2019-01-05T20:29:24.817 回答