LJ 密码生成器工具

作者:Dave Taylor

助记密码通常很糟糕。字母、数字和标点符号的随机序列更安全——只是不要像《头号玩家》中的笨蛋反派那样写下你的密码!

在我上一篇文章的密码生成工具中,最简单的情况下,您可以指定密码中所需的字符数,然后从一组可接受的值中随机选择每个字符。使用 Linux shell 内置的 RANDOM,这非常容易实现


okay="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
okay="${okay}0123456789<>/?,>;:[{]}\|=+-_)(^%$#@!~
length=10
ltrs=${#okay}

while [ $length -ge 0 ]
do
   letter="${okay:$RANDOM % $ltrs:1}"
   result="$result$letter"
   length=$(( $length - 1 ))
done

echo "Result: $result"

在实际脚本中,我将 okay 设置为单个值,而不是分两步构建;这只是为了在线格式化。否则,`ltrs` 被设置为 `$okay` 的长度,作为一个快速快捷方式,结果是通过使用字符串切片语法构建的


${variable:indexlocation:length}

例如,要提取字符串的第四个字符,`${string:4:1}`,这效果很好且容易。结果不言自明


$ sh lazy-passwords.sh
Result: Ojkr9>|}dMr

还有一些


Result: Mi8]TfJKVaH
Result: >MWvF2D/R?r
Result: h>J6\p4eNPH
Result: KixhCFZaesr

当您决定不希望随机选择事物,而是希望对结果进行加权,以便您拥有比数字更多的字母,或者即使在 15-20 个字符的密码中,标点符号字符也不超过几个时,这就变成了一个更复杂的挑战。

当然,这正是我一直在构建的。

我不得不承认,制作复杂的东西有一定的诱惑力,即使仅仅是为了看看它是否可以完成并正常工作。

为字母选择添加权重

因此,上面简单的几行代码在我上一篇文章中变成了这样


while [ ${#password} -lt $length ] ; do
   case $(( $RANDOM % 7 )) in
     0|1 ) letter=${uppers:$(( $RANDOM % ${#uppers})):1} ;;
     2|3 ) letter=${lowers:$(( $RANDOM % ${#lowers})):1} ;;
     4|5 ) letter=${punct:$(( $RANDOM % ${#punct}  )):1} ;;
     6 ) letter=${digits:$(( $RANDOM % ${#digits}  )):1} ;;
   esac
  password="${password}$letter"
done

在上面的代码中,我分配了一个给定字母属于四个类别之一的概率:大写字母、小写字母、标点符号或数字。前三个类别各有 2:7 的机会,而标点符号是 1:7,或者产生的可能性只有一半。

上述算法运行四次迭代会产生以下结果


q.x0bAPmZb
P}aWX2N-U]
5jdI&ep7rt
-k:TA[1I!3

由于随机性本质上是随机的,因此在两种情况下,您实际上最终可能会得到一个不包含标点符号的密码。那么,如何强制特定数量的标点符号出现呢?一种解决方案是在事后检查您是否达到了目标计数。

要做到这一点,您需要两件事:目标和计数器。让我们在典型的 `getops` 块中添加前者作为启动选项


while getopts "l:d:p:" arg
do
   case "$arg" in
     l) length=$OPTARG ;;
     d) digitGoal=$OPTARG ;;
     p) punctGoal=$OPTARG ;;
     *) echo "Valid -l length, -d digits, -p punctuation"
        exit 1 ;;
   esac
done

计数器也很容易;每次在 case 语句(前面显示)中满足数字或标点符号条件时,计数器都会递增一。

最后,在结尾处,您可以比较两者,看看您是否达到了加权随机化目标


if [ $digitsAdded -lt $digitGoal ] ; then
  echo "Didn't add enough digits. [goal = $digitGoal,
    inserted = $digitsAdded]"
  exit 1
elif [ $punctAdded -lt $punctGoal ] ; then
  echo "Didn't add enough punctuation. [goal = $punctGoal,
    inserted = $punctAdded]"
  exit 1
fi

如果请求的密码总长度与添加数字或标点符号字符的随机机会相比是合理的,那么这将工作良好。一个包含至少两个标点符号字符的 15 个字符密码几乎每次都可以顺利生成。

虽然偶尔


$ sh makepw.sh -l 15 -p 4
Didn't add enough punctuation. [goal = 4, inserted = 1]

这就引出了脚本算法最重要的问题:一旦您意识到您没有达到您的数字和标点符号字符目标,您会怎么做?

密码生成失败:现在怎么办?

一种解决方案是简单地再次尝试,但是如果用户设置了一个不可能的情况,比如一个包含四个数字和四个标点符号字符的六字符密码,或者甚至是四个和两个,那就不可行了。

另一种可能性是逐步遍历生成的密码,用所需的特定值替换不受约束的值(例如大写和小写字母)。这样做会导致如果您要求大量的标点符号或数字,您最终会使这些请求的字符靠前加载,这并不是完全随机的。

所以,让我们重新思考这个问题

如果,不是生成随机密码,而是将其分为两个步骤呢?第一步是生成所需数量的随机数字和随机标点符号字符,添加完全随机的值以达到所需的长度,然后“洗牌”结果以生成最终密码。

我知道,您几乎完成了这个程序,但这是一个非常有趣的解决方案,可以避开很多问题,所以让我们后退一步,重新开始!

实际上,这并没有那么糟糕,因为大部分工作已经完成了。这只会让它更简单


while [ ${#password} -lt $length ] ; do
   if [ $digitsAdded -lt $digitGoal ] ; then
     letter=${digits:$(( $RANDOM % ${#digits} )):1}
     digitsAdded=$(( $digitsAdded +1 ))
   elif [ $punctAdded -lt $punctGoal ] ; then
     letter=${punct:$(( $RANDOM % ${#punct} )):1}
     punctAdded=$(( $punctAdded +1 ))
   else
     case $(( $RANDOM % 7 )) in
       0|1) letter=${uppers:$(($RANDOM % ${#uppers})):1} ;;
       2|3) letter=${lowers:$(($RANDOM % ${#lowers})):1} ;;
       4|5) letter=${punct:$(($RANDOM % ${#punct} )):1}
             punctAdded=$(( $punctAdded + 1 ))        ;;
       6) letter=${digits:$(( $RANDOM % ${#digits} )):1}
             digitsAdded=$(( $digitsAdded +1 ))       ;;
     esac
   fi
   password="${password}$letter"
done

在没有最终密码加扰代码的情况下,以下是您在对一个包含至少四个数字和至少四个标点符号字符的 15 个字符密码进行几次调用时得到的结果


$ sh makepw.sh -l 15 -p 4 -d 4
Interim password generated: 8119?:)@_g&rw%=
$ sh makepw.sh -l 15 -p 4 -d 4
Interim password generated: 7599}(|&l*4KFY/

您可以清楚地看到这些是如何靠前加载的,首先是所需的数字,然后是所需的标点符号,然后是“其他所有内容”。

在 shell 脚本中,有很多方法可以对单词中的字母进行洗牌,从调用 Perl 或 Awk 到使用 Linux `shuf` 命令,再到自己解决。我将把这留给读者作为练习,因为有了这个小的附加步骤,您就拥有了一个功能齐全的密码生成器,可以处理您的数百个系统用户。

Dave Taylor 长期以来一直在 UNIX 和 Linux 系统上编写 shell 脚本。他是《Learning Unix for Mac OS X》和《Wicked Cool Shell Scripts》的作者。您可以在 Twitter 上找到他 @DaveTaylor,也可以通过他的技术问答网站:Ask Dave Taylor 联系他。

加载 Disqus 评论