有几次我尝试在各种 Linux 机器上更改用户密码,当新密码与旧密码相似时,操作系统会抱怨它们太相似。
我一直想知道,系统是如何知道这一点的?我以为密码是以哈希值形式保存的。这是否意味着当系统能够比较新密码的相似性时,旧密码实际上以纯文本形式保存?
答案1
由于你需要同时提供旧的和使用时输入新密码passwd
,可以在内存中以纯文本形式轻松进行比较,而无需将其写入驱动器上的某个位置。
确实,您的密码在最终存储时会被散列,但在此之前,您输入密码的工具当然可以直接访问它,就像任何其他程序在从 STDIN 读取时可以访问您在键盘上输入的内容一样。
这是聚丙烯酰胺系统用于该passwd
工具的后台。现代 Linux 发行版都使用 PAM。
更具体地说,pam_cracklib
这是一个 PAM 模块,它允许根据一些可能使密码非常脆弱的弱点来拒绝密码。
不仅仅是过于相似的密码才被认为是不安全的。源代码 有各种可以检查的示例,例如密码是否为回文或两个单词之间的编辑距离是多少。这个想法是让密码更能抵御字典攻击。
也可以看看手册pam_cracklib
页。
答案2
至少在我的 Ubuntu 中,“太相似”的消息出现了什么时候:“...超过一半的角色都是不同的....”(详情见下文)。感谢 PAM 支持,正如@slhck 答案中清楚解释的那样。
对于未使用 PAM 的其他平台,在以下情况下会出现“太相似”的消息:“...超过一半的角色都是不同的....”(详情见下文)
为了进一步自行检查此语句,可以检查源代码。方法如下。
“passwd”程序包含在passwd包中:
verzulli@iMac:~$ which passwd
/usr/bin/passwd
verzulli@iMac:~$ dpkg -S /usr/bin/passwd
passwd: /usr/bin/passwd
由于我们使用的是开源技术,因此我们可以不受限制地访问源代码。获取源代码非常简单:
verzulli@iMac:/usr/local/src/passwd$ apt-get source passwd
之后很容易找到相关的代码片段:
verzulli@iMac:/usr/local/src/passwd$ grep -i -r 'too similar' .
[...]
./shadow-4.1.5.1/NEWS:- new password is not "too similar" if it is long enough
./shadow-4.1.5.1/libmisc/obscure.c: msg = _("too similar");
快速检查“obscure.c”给出了以下内容(我只剪切并粘贴了相关的代码):
static const char *password_check (
const char *old,
const char *new,
const struct passwd *pwdp)
{
const char *msg = NULL;
char *oldmono, *newmono, *wrapped;
if (strcmp (new, old) == 0) {
return _("no change");
}
[...]
if (palindrome (oldmono, newmono)) {
msg = _("a palindrome");
} else if (strcmp (oldmono, newmono) == 0) {
msg = _("case changes only");
} else if (similar (oldmono, newmono)) {
msg = _("too similar");
} else if (simple (old, new)) {
msg = _("too simple");
} else if (strstr (wrapped, newmono) != NULL) {
msg = _("rotated");
} else {
}
[...]
return msg;
}
现在,我们知道有一个“类似”函数,它基于旧版本和新版本检查两者是否相似。以下是代码片段:
/*
* more than half of the characters are different ones.
*/
static bool similar (const char *old, const char *new)
{
int i, j;
/*
* XXX - sometimes this fails when changing from a simple password
* to a really long one (MD5). For now, I just return success if
* the new password is long enough. Please feel free to suggest
* something better... --marekm
*/
if (strlen (new) >= 8) {
return false;
}
for (i = j = 0; ('\0' != new[i]) && ('\0' != old[i]); i++) {
if (strchr (new, old[i]) != NULL) {
j++;
}
}
if (i >= j * 2) {
return false;
}
return true;
}
我还没有检查过 C 代码。我只能相信函数定义之前的注释 :-)
PAM 和非 PAM 感知平台之间的区别在“obscure.c”文件中定义,其结构如下:
#include <config.h>
#ifndef USE_PAM
[...lots of things, including all the above...]
#else /* !USE_PAM */
extern int errno; /* warning: ANSI C forbids an empty source file */
#endif /* !USE_PAM */
答案3
答案比你想象的要简单得多。事实上,它几乎可以算作魔术,因为一旦你解释了这个技巧,它就消失了:
$ passwd
Current Password:
New Password:
Repeat New Password:
Password changed successfully
它知道您的新密码相似...因为您刚刚输入了旧密码。
答案4
有一个方面没有涉及:密码历史记录。有些系统支持此功能。为了做到这一点,它会保留密码历史记录并使用当前密码对其进行加密。当您更改密码时,它会使用“旧”密码解密列表并进行验证。当它设置新密码时,它会(再次)保存使用从新密码派生的密钥加密的列表。
这就是remember=N
PAM 的工作方式(存储在 中/etc/security/opasswd
)。但 Windows 和其他 Unix 供应商也提供类似的功能。