你的密码真的安全吗?

作者:Eric Hodges

在“你的密码真的安全吗?”的比赛中,参赛者必须编写一个脚本,根据提供的标准来判断密码的强度。

比赛场景

在当今时代,人们被鼓励在任何需要创建或更改密码的时候使用“强”密码。这是一个很好的建议,但它也引出了几个问题:1)究竟什么是强密码,以及 2)同样地,我们如何判断一个给定的密码是否安全?第五场比赛旨在帮助你回答这两个问题。

在本场比赛中,你将创建一个可以判断密码“强度”的脚本。密码强度将通过以下检查来确定。脚本必须完成以下所有操作

  • 确保密码不是一个真实的单词。 密码 rhubarb 未通过此测试,因为 rhubarb 是一个真实的单词。要确定一个单词是否为真实单词,请始终使用 WordList.txt 文件,这是一个官方单词列表,作为脚本游戏参赛者包的一部分提供。(请确保将此文件放在 C:\Scripts 文件夹中。)请注意,此检查应区分大小写:rhubarb 是一个真实的单词,RHUBARB、rhUBArb 等也是如此。

  • 确保密码去掉最后一个字母后不是一个真实的单词。 例如,密码 rhubarb5 未通过此测试,因为如果删除最后一个字母,则剩余的字符串值 - rhubarb - 是一个真实的单词。此检查应区分大小写。

  • 确保密码去掉第一个字母后不是一个真实的单词。 例如,密码 @rhubarb 未通过此测试,因为如果删除第一个字母,则剩余的字符串值 - rhubarb - 是一个真实的单词。此检查应区分大小写。

  • 确保密码不是简单地用 0(零)替换字母 o(大写字母 O 或小写字母 o)。 例如,密码 t00lb0x 未通过此测试。为什么?因为如果将每个零替换为字母 O,你将得到一个真实的单词:toolbox。

  • 确保密码不是简单地用 1(一)替换字母 l(大写字母 L 或小写字母 l)。 例如,密码 f1oti11a 未通过此测试。为什么?因为如果将每个一替换为字母 L,你将得到一个真实的单词:flotilla。

  • 确保密码长度至少为 10 个字符,但不超过 20 个字符。 密码 rhubarb 未通过此测试,因为它只有 7 个字符。

  • 确保密码至少包含一个数字(0 到 9 之间的数字)。 密码 rhubarb%$qwC 未通过此测试,因为它不包含数字。

  • 确保密码至少包含一个大写字母。 密码 rhubarb 未通过此测试,因为它没有大写字母。

  • 确保密码至少包含一个小写字母。 密码 RHUBARB 未通过此测试,因为它没有小写字母。

  • 确保密码至少包含一个符号。 这可以是任何既不是大写字母也不是小写字母或数字的字符;包括但不限于符号 ~、@、#、$、% 和 ^。

  • 确保密码不包含四个(或更多)连续的小写字母。 密码 rhubARB 未通过此测试,因为它包含四个连续的小写字母 (rhub)。

  • 确保密码不包含四个(或更多)连续的大写字母。 密码 rHUBArb 未通过此测试,因为它包含四个连续的大写字母 (HUBA)。

  • 确保密码不包含任何重复字符。 密码 rhubarb 未通过此测试,因为它有两个 r 和两个 b。此检查应区分大小写:A 和 a 被视为不同的字母。因此,密码 Oboe 不会未通过此特定测试。

注意:是的,要记住的事情很多,不是吗?为了帮助您跟踪所有内容,我们在参赛者资料包中包含了一个清单 (Password_Checklist.doc)。

要成功完成此活动,您的脚本必须接受可能的密码作为命令行参数并对该密码进行评级。例如,如果您想评估密码 rhubarb33! 的强度,您可以使用类似于以下命令的命令启动脚本

myscript.pl rhubarb33!

您的脚本应以密码分数 13 开始;这意味着通过每项检查的密码的最终分数为 13。从参数集合中检索密码后(我们将一次只向脚本传递一个密码),您的脚本应该针对该密码运行前面提到的每项检查。如果密码通过了给定的检查(例如,检查密码是否是实际单词的测试),则脚本应该简单地继续进行下一个测试。这应该很容易。

现在,如果密码未通过给定的检查会发生什么?例如,密码 rhubarb 将未通过“密码不能有四个连续的小写字母”的测试。在这种情况下,脚本必须做两件事

  • 从密码分数中减去 1。例如,如果密码分数为 11 并且脚本未通过四个连续小写字母的检查,则密码分数应降低到 10

  • 回显一条消息,说明建议的密码未通过此测试。例如,在这种情况下,您将回显一条类似于以下内容的消息

Four consecutive lowercase letters in password.

完成所有检查后,脚本应使用以下等级对密码进行评级

  • 6 分或更低的分数表示弱密码。

  • 7、8、9 或 10 分表示中等强度的密码。

  • 11 分或更高的分数表示强密码。

您应该回显分数和密码等级。例如

A password score of 4 indicates a weak password.

例如,以下是您在检查密码 rhubarb33! 时应该获得的输出类型

No uppercase letters in password.
Four consecutive lowercase letters in password.
Duplicate letters in password.

A password score of 10 indicates a moderately-strong password.

这就是你需要做的全部。

http://web.archive.org/web/20080410170315/http://www.microsoft.com/technet/scriptcenter/funzone/games/games08/aevent5.mspx

源代码:event005-eric256.pl

use v6;

sub MAIN(Str :$pw = "", Bool :$verbose = False) {
    my $password = $pw || prompt("Enter password to test: ");

    my $input-file = $*SPEC.catdir($*PROGRAM-NAME.IO.dirname, "wordlist.txt");
    my %dict = (($input-file.IO.lines.grep: {.chars > 6}) X 1).flat;

    say "Testing strength of password '$password'" if $verbose;

    my $score = 13;

    if %dict{$password} :exists {
        $score--;
        say "Password matched dictionary";
    }

    if %dict{$password.substr(0, $password.chars -1 )} :exists {
        $score--;
        say "Password minus last char is in dictionary";
    }

    if %dict{$password.substr(1,$password.chars-1)} :exists {
        $score--;
        say "Password minus first char is in dictionary";
    }

    my $test = $password;
    $test.subst(/0/, "o");
    if %dict{$test} :exists {
        $score--;
        say "Password replaces 'o' with '0'";
    }

    $test = $password;
    $test.subst(/1/, "i");
    if %dict{$test} :exists {
        $score--;
        say "Password replaces 'i' with '1'";
    }

    if $password.chars == none(10..20) {
        $score--;
        say "Password is too short (less than 10) or too long (more than 20)"
    }

    unless $password ~~ rx/<[A..Z]>/ {
        $score--;
        say "No uppercase letters in password.";
    }

    if $password ~~ rx/<[a..z]> ** 4..*/ {
        $score--;
        say "Four consecutive lowercase letters in password.";
    }

    my @chars = $password.split('');
    my %letter-frequency;
    for @chars -> $char {
        if $char ~~ rx/<[a..zA..Z]>/ {
            %letter-frequency{$char} =
                %letter-frequency{$char}:exists ?? ++%letter-frequency{$char} !! 1;
        }
    }
    if %letter-frequency.values.any > 1 {
        $score--;
        say "Duplicate letters in password.";
    }

    say "'$password' scored $score" if $verbose;

    say "";
    given $score {
        when $_ <= 6 {
            say "A password score of $score indicates a weak password.";
        }
        when 7 < $_ <= 10 {
            say "A password score of $score indicates a moderately-strong password.";
        }
        when $_ >= 11 {
            say "A password score of $score indicates a strong password.";
        }
    }
}