题
确保用户提供的密码是注册或更改密码表单中的强密码的最佳方法是什么?
我的一个想法(用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
解决方案
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
刚刚关注 密码计 还不够,因为它的朴素算法确实看到了 密码1! 一样好,但又特别弱。评分时请务必忽略首字母大写字母以及尾随数字和符号(根据最后 3 条规则)。
计算香农熵
看: 在 Python 中计算熵的最快方法
3:不允许任何太弱的密码
不要强迫用户屈服于弄巧成拙的规则,而是允许任何能够给出足够高分数的事情。多高取决于您的用例。
最重要的是
当您接受密码并将其存储在数据库中时, 一定要加盐并散列它!.
其他提示
根据语言的不同,我通常使用正则表达式来检查它是否具有:
- 至少一个大写和一个小写字母
- 至少一个数字
- 至少一个特殊字符
- 长度至少为六个字符
您可以要求以上所有内容,或者使用强度计类型的脚本。对于我的强度计,如果密码长度正确,则评估如下:
- 满足一个条件:密码强度低
- 满足两个条件:中等密码
- 满足所有条件:强密码
您可以调整以上内容以满足您的需求。
面向对象的方法将是一组规则。为每个规则分配权重并迭代它们。在伪代码中:
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;
}
要检查的两个最简单的指标是:
- 长度。我想说至少 8 个字符。
- 密码包含的不同字符类别的数量。这些通常是小写字母、大写字母、数字和标点符号以及其他符号。强密码将包含至少其中三个类别的字符;如果强制使用数字或其他非字母字符,则会显着降低字典攻击的有效性。
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'
阅读其他有用的答案后,这就是我要处理的内容:
-1 与用户名相同
+0 包含用户名
+1 超过 7 个字符
+1 超过 11 个字符
+1 包含数字
+1 小写和大写混合
+1 包含标点符号
+1 个不可打印字符
pwscore.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."
可能不是最有效的,但看起来很合理。不确定FastistCheck =>'与用户名相似,这是值得的。
'abc123ABC!@£' = 分数 6/6(如果不是用户名的超集)
也许分数应该更低。
这里是开放和免费的 开膛手约翰 密码破解程序是检查现有密码数据库的好方法。
嗯,这就是我用的:
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;
}
除了混合字母、数字和符号的标准方法之外,我注意到上周注册 MyOpenId 时,密码检查器会告诉您密码是否基于字典单词,即使您添加数字或用类似数字替换字母(使用零代替“o”,“1”代替“i”等)。
我印象很深刻。
我编写了一个小型 Javascript 应用程序。看一看: 另一种密码计. 。您可以下载源代码并在 GPL 下使用/修改它。玩得开心!
我不知道是否有人会觉得这很有用,但我真的很喜欢 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
免责声明:未经过单元测试
如果您有时间,请对其运行密码破解程序。
密码强度检查器,如果您有时间+资源(仅当您检查多个密码时才合理),请使用彩虹表。
通过一系列检查以确保其符合最低标准:
- 至少 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/
确保用户提供的密码是注册或更改密码表单中的强密码的最佳方法是什么?
不要评估复杂性和/或强度,用户会找到一种方法来愚弄您的系统,或者会感到沮丧而离开。那只会让你陷入困境 像这样. 。只需要一定的长度,并且不会使用泄露的密码。奖励积分:确保您实施的任何方式都允许使用密码管理器和/或 2FA。