如何衡量一个密码的强度?(How do I measure the strength of a pa

2019-08-31 18:43发布

我一直在寻找一种有效的算法,可以给我的密码有多强是一个准确的概念。

我发现几个不同的网站使用几种不同的算法,因为我得到不同的网站不同的密码强度等级。

Answer 1:

This has grown to my general brain dump of best practices for working with passwords in PHP/MySQL. The ideas presented here are generally not my own, but the best of what I've found to date.


Ensure you are using SSL for all operations involving user information. All pages that involve these forms should check they are being called via HTTPS, and refuse to work otherwise.

You can eliminate most attacks by simply limiting the number of failed logins allowed.

Allow for relatively weak passwords, but store the number of failed logins per user and require a captcha or password verification by email if you exceed it. I set my max failures to 5.

Presenting login failures to the user needs to be carefully thought out as to not provide information to attackers. A failed login due to a non existent user should return the same message as a failed login due to a bad password. Providing a different message will allow attackers to determine valid user logins.

Also make sure you return exactly the same message in the event of a failure for too many logins with a valid password, and a failure with too many logins and a bad password. Providing a different message will allow attackers to determine valid user passwords. A fair number of users when forced to reset their password will simply put it back to what it was.

Unfortunately limiting the number of logins allowed per IP address is not practical. Several providers such as AOL and most companies proxy their web requests. Imposing this limit will effectively eliminate these users.


I've found checking for dictionary words before submit to be inefficient as either you have to send a dictionary to the client in javascript, or send an ajax request per field change. I did this for a while and it worked ok, but didn't like the traffic it generated.

Checking for inherently weak passwords minus dictionary words IS practical client side with some simple javascript.

After submit, I check for dictionary words, and username containing password and vice versa server side. Very good dictionaries are readily downloadable and the testing against them is simple. One gotcha here is that to test for a dictionary word, you need to send a query against the database, which again contains the password. The way I got around this was to encrypt my dictionary before hand with a simple encryption and end positioned SALT and then test for the encrypted password. Not ideal, but better than plain text and only on the wire for people on your physical machines and subnet.

Once you are happy with the password they have picked encrypt it with PHP first, then store. The following password encryption function is not my idea either, but solves a number of problems. Encrypting within PHP prevents people on a shared server from intercepting your unencrypted passwords. Adding something per user that won't change (I use email as this is the username for my sites) and add a hash (SALT is a short constant string I change per site) increases resistance to attacks. Because the SALT is located within the password, and the password can be any length, it becomes almost impossible to attack this with a rainbow table. Alternately it also means that people can't change their email and you can't change the SALT without invalidating everyone's password though.

EDIT: I would now recommend using PhPass instead of my roll your own function here, or just forget user logins altogether and use OpenID instead.

function password_crypt($email,$toHash) {
  $password = str_split($toHash,(strlen($toHash)/2)+1);
  return hash('sha256', $email.$password[0].SALT.$password[1]); 
}

My Jqueryish client side password meter. Target should be a div. It's width will change between 0 and 100 and background color will change based on the classes denoted in the script. Again mostly stolen from other things I've found:

$.updatePasswordMeter = function(password,username,target) {
$.updatePasswordMeter._checkRepetition = function(pLen,str) {
res = ""
for ( i=0; i<str.length ; i++ ) {
    repeated=true;
    for (j=0;j < pLen && (j+i+pLen) < str.length;j++)
        repeated=repeated && (str.charAt(j+i)==str.charAt(j+i+pLen));
    if (j<pLen) repeated=false;
    if (repeated) {
        i+=pLen-1;
        repeated=false;
    }
    else {
        res+=str.charAt(i);
    };
};
return res;
};
var score = 0;
var r_class = 'weak-password';
//password < 4
if (password.length < 4 || password.toLowerCase()==username.toLowerCase()) { 
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
  return true;
} 
//password length
score += password.length * 4;
score += ( $.updatePasswordMeter._checkRepetition(1,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(2,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(3,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(4,password).length - password.length ) * 1;
//password has 3 numbers
if (password.match(/(.*[0-9].*[0-9].*[0-9])/))  score += 5; 

//password has 2 symbols
if (password.match(/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/)) score += 5; 

//password has Upper and Lower chars
if (password.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/))  score += 10; 

//password has number and chars
if (password.match(/([a-zA-Z])/) && password.match(/([0-9])/))  score += 15; 
//
//password has number and symbol
if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([0-9])/))  score += 15; 

//password has char and symbol
if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([a-zA-Z])/))  score += 15; 

//password is just a nubers or chars
if (password.match(/^\w+$/) || password.match(/^\d+$/) )  score -= 10; 

//verifing 0 < score < 100
score = score * 2;
if ( score < 0 )  score = 0;
if ( score > 100 )  score = 100;
if (score > 25 ) r_class = 'okay-password';
if (score > 50  ) r_class = 'good-password';
if (score > 75 ) r_class = 'strong-password';
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
return true;
};


Answer 2:

从根本上说要防止主要类型的攻击

  • 字典攻击
  • 蛮力攻击

为了防止第一,你要考虑包含常用词弱密码。 为了防止第二,要鼓励合理长度的密码(8个字符是常见的),并有一个相当大的字符集(包括字母,数字和特殊字符)。 如果考虑小写和大写字母是不同的,大幅增加的字符集。 然而,这对于一些用户社区创建了一个可用性问题,所以你需要以平衡的考虑。

快速谷歌搜索打开了该帐户蛮力攻击(复杂的密码),而不是字典攻击的解决方案。 从PHP密码场强仪强度跳棋这个名单运行检查服务器端,所以它可以扩展到检查字典。

编辑:

顺便说...你也应该限制每个用户的登录尝试的次数。 这将使这两种类型的攻击可能性较小。 有效的,但不是用户友好的就是锁定X坏尝试后,一个帐户,并要求密码重置。 更多的用户友好的,但更多的精力是登录尝试之间油门的时间。 您也可以要求CAPTCHA前几登录尝试后(这是一件好事堆栈溢出太多编辑后需要或非常新的用户)。



Answer 3:

基本上,你可能想使用正则表达式来验证密码的长度和复杂性。

一个很好的例子这样使用JavaScript可以在这里找到:

http://marketingtechblog.com/programming/javascript-password-strength/



Answer 4:

作为达人Schwenke指出了这一点, 你最好自己的安全工作,而不是把这个在用户手中

但是,这是很好的提供了一些线索,以他的密码强度的用户,因为拿到一个密码的最佳方法仍然是社会engenering。

所以,你能砍一点客户端脚本检查用户密码实力出于礼貌指标,实时。 它阻止什么,只是给了他一个很好的温暖的感觉,当它变成绿色:-)

基本上你必须检查是黎民意义:检查密码包含字母,数字和非字母字符,在一个合理的数量。

你可以很容易破解自己的算法中:只是做/ 10标志:

  • 0是零lenght密码;
  • 2在密码每8个caracters(15被认为是一种安全lenght);
  • +1使用了一封信,+2使用2个英文字母的;
  • 1为使用一个数字,2为使用2号的;
  • 1为使用非字母字符的,2 2。

你并不需要检查神圣的密码(是否有大写字母,其中定位于特殊caracters等),您的用户不是在银行/军事/秘密服务/蒙提·派森的电影产业,是吗?

您可以编写代码,在一个小时没有疯狂的JavaScript技能。

不管怎样,有效的密码和移动服务器端所有的安全码。 如果你可以委托认证(如:开放ID),甚至更好。



Answer 5:

我想不出一个特定的算法来检查密码的那份力量。 我们做的是我们定义的几个标准,当密码尊重标准,我们增加1到其得分。 当密码达到阈值,密码为强。 否则,它是弱。

您可以定义多种不同级别车力量,如果有不同throeshold,或者你可以为特定的标准定义不同的值。 例如,如果密码有5个字符,我们加1,但如果它得到了10,然后我们加2。

这里是标准检查列表

长度(8至12是确定,更多的是更好)包含小写字母包含大写字母的大写字母是不是第一个。 包含数包含符号的最后一个字符是不喜欢的符号是人(例如:!或)看起来不像一个dictionnary字。 一些明智的密码进行破解包含单词和字母替代品库(如图书馆 - > L1br @ RY)

希望帮助。



Answer 6:

不滚你自己!

密码学专家劝阻滚你自己的密码 ,其原因应该是显而易见的。

对于同样的原因,一个不应该试图推出自己的解决方案来测量密码的强度问题; 这不能不说是一个加密问题。

不要进入创作为此,一些大规模的正则表达式的丑陋的业务; 你可能会没有考虑到影响一个密码的整体实力的几个因素。

这是一个棘手的问题

有固有的测量密码的强度的问题相当大的难度。 越研究,我对这个问题进行,我越来越意识到,这是一个“单向”的问题; 即,一个不能测量有效地裂化密码的“困难”(计算成本)。 相反,它是更有效地提供复杂性要求和测量密码的,以满足它们的能力。

当我们在逻辑上考虑问题,一个“裂化指数”没有多大意义,因为它的声音一样方便。 有这么多的因素推动的计算,其中大部分涉及到专门裂解过程中的计算资源,从而是不切实际的。

试想一下,点蚀约翰开膛手 (或类似的工具)对有问题的密码; 它可能需要数天来破解一个体面的密码,几个月来破解一个好的密码,直到太阳灼伤出破解一个特殊的密码。 这不是一个实用的方法来衡量密码强度。

从另一个方向接近的问题是远远更易于管理:如果我们提供了一套复杂的要求,它可以非常迅速判断密码的相对强度。 显然,所提供的复杂性要求,必须随着时间的推移,但有远为此,如果我们以这种方式处理这个问题考虑较少的变量。

可行的解决方案

目前可从OpenWall的题为一个独立的工具passwdqc (据推测,站立密码质量检查 )。 OpenWall的开发,太阳能设计,确实出现了一个真正的密码专家(他的作品自己说话),所以有资格编写这样的工具。

对于我的具体使用情况,这比使用一个考虑不周的JavaScript片段生活在网络的一些阴暗的角落一个更有吸引力的解决方案。

建立参数为您的特定需求是最难的部分。 执行是比较容易的部分。

一个实际的例子

我提供的PHP一个简单的实施提供一个好的开端。 标准的声明。

这个例子假设我们喂养密码PHP脚本的完整列表。 不言而喻,如果你与真正的密码,这样做(例如,那些甩了密码管理的),必须极其小心对于密码处理行使。 简单地写未加密的密码转储到磁盘危害您的密码的安全性!

passwords.csv

"Title","Password"
"My Test Password","password123"
"Your Test Password","123456!!!"
"A strong password","NFYbCoHC5S7dngitqCD53tvQkAu3dais"

password-check.php

<?php

//A few handy examples from other users:
//http://php.net/manual/en/function.str-getcsv.php#117692

$csv = array_map('str_getcsv', file('passwords.csv'), [',']);

array_walk($csv, function(&$a) use ($csv) {
    $a = array_combine($csv[0], $a);
});

//Remove column header.

array_shift($csv);

//Define report column headers.

$results[] = [
    'Title',
    'Result',
    'Exit Code',
];

$i = 1;

foreach ($csv as $p) {
    $row['title'] = $p['Title'];

    //If the value contains a space, it's considered a passphrase.

    $isPassphrase = stristr($p['Password'], ' ') !== false ? true : false;

    $cmd = 'echo ' . escapeshellarg($p['Password']) . ' | pwqcheck -1 min=32,24,22,20,16 max=128';

    if ($isPassphrase) {
        $cmd .= ' passphrase=3';
    }
    else {
        $cmd .= ' passphrase=0';
    }

    $output = null;
    $exitCode = null;

    $stdOut = exec($cmd, $output, $exitCode);

    //Exit code 0 represents an unacceptable password (not an error).
    //Exit code 1 represents an acceptable password (it meets the criteria).

    if ($exitCode === 0 || $exitCode === 1) {
        $row['result'] = trim($stdOut);
        $row['exitCode'] = $exitCode;
    }
    else {
        $row['result'] = 'An error occurred while calling pwqcheck';
        $row['exitCode'] = null;
    }

    $results[$i] = $row;

    $i++;
}

$reportFile = 'report.csv';

$fp = @fopen($reportFile, 'w');

if ($fp !== false) {
    foreach ($results as $p) {
        fputcsv($fp, $p);
    }

    fclose($fp);
}
else {
    die($reportFile . ' could not be opened for writing (destination is not writable or file is in use)');
}

exit;

结果report.csv

Title,Result,"Exit Code"
"My Test Password","Bad passphrase (too short)",1
"Your Test Password","Bad passphrase (too short)",1
"A strong password",OK,0

包起来

我还没有找到在网络上进行更彻底的解决方案; 不用说,我欢迎任何其他建议。

显然,这种方法是不理想的某些使用情况(例如,“密码强度计”落实“客户端”)。 即便如此,这将是微不足道的做一个AJAX调用返回使用上面介绍的方法通过/失败响应的服务器端资源,但这样的做法应该承担滥用(例如拒绝服务攻击)的潜力和需要客户端和服务器之间的安全通信,以及接受与发送的未散列处理的密码相关的风险。



文章来源: How do I measure the strength of a password?