大多数时候,当我看到一个随机生成的密码时,它是十六进制的,即只使用字符集[0-9a-f]。[0-9a-z]如果字符集是,不是更安全[0-9a-zA-Z]吗?
为什么随机生成的密码通常是十六进制的?
(你有证据表明随机密码通常是十六进制的吗?)
以下是您经常发现十六进制密码的一些可能原因:
您看到的密码可能已经过哈希处理。由于散列函数返回二进制字符串,因此您经常会看到它们以十六进制形式存储。
散列随机二进制序列(例如 from
/dev/urandom)是创建随机密码的一种廉价但相当安全的方法。你偶尔会在野外看到这样的东西:$ head -c 100 /dev/urandom | sha256sum | head -c 25 c9ea6b67af36753a68ef42429所有字符都很容易区分。也就是说,如果您首先只允许使用十六进制,就不会遇到混淆
1andI、Oand0等问题。随机生成的序列通常是二进制的,因此以十六进制形式显示它们似乎合乎逻辑且有效。
也就是说,[0-9a-f]如果您不调整长度,从更大范围内选择一个随机密码显然更安全。我通常会接受你的建议,[0-9a-zA-Z]这给了我一个广泛的范围,并避免了可能导致存储和传输问题的特殊字符。
此外,这是我在我的 shell 配置中放入的用于在 Linux 上快速生成密码的内容:
函数 spw() {
猫 /dev/urandom | tr-dc a-zA-Z0-9 | 头 -c ${1:-20}
}
例如,spw 25会给我一个 25 个字符的密码[0-9a-zA-Z]。
Arminius 提到了十六进制的简单性,我认为这是值得扩展的。
随机数生成器通常在位上工作,因此它们可以生成的数字范围是 2 的幂。一个字符范围[0-9a-zA-Z]有 62 个字符,比 2 的幂 (64) 少两个,因此计算机必须在范围之间进行一些转换。
这可以做到,但很容易出错。“标准”方法是取实际数字,除以您想要的范围,然后将余数作为您的随机数。但这会引入偏见。举个简单的例子,假设您在其中生成数字,[0-3]但您希望将它们输入[0-2]。范围将像这样[0-3]映射到[0-2]范围:
0 => 0 mod 3 => 0
1 => 1 mod 3 => 1
2 => 2 mod 3 => 2
3 => 3 mod 3 => 0
请注意如何通过0两种不同的方式获得 a:0并且3都转到0. 该方法偏向于生成0s,这将使您的密码更容易被猜到。
正确的方法涉及计算正确的填充以使您生成的内容均匀地适合该范围,这很棘手,并且可能会使您的密码更长,具体取决于您的输出范围与随机数生成器的输出范围的比较。
一种更简单的方法是只使用已经是 2 次方的范围,这样您就可以完全忽略偏差。大多数都有问题。
- Base-2(二进制)产生极长的字符串。
- Base-4(四元)也好不了多少。
- Base-8(八进制)更好,但仍然很长。
- Base-16(十六进制)有点长,但合理。它还对每个字符编码 4 位,这在计算机更喜欢 8 的倍数时非常方便。
- Base-32 编码每个字符 5 位,这在计算机更喜欢 8 的倍数时并不方便。
- Base-64 编码每个字符 6 位,这仍然很尴尬(但稍微少一点,因为至少它是一个偶数)。
- Base-96 很流行,但不是 2 的幂,所以它和
[0-9a-zA-Z]. - Base-128 和最重要的是涉及您无法在典型的 querty 键盘上轻松键入的符号。
为了进一步解释为什么 base-64 是一个问题,请考虑当您尝试编码单个字节(8 位)时会发生什么。您不能使用单个 base-64 字符来执行此操作,因为它只能获得 8 位中的 6 位。但是如果你使用两个字符,你必须弄清楚如何将你的 8 位字节填充到 12 位输出而不引入偏差或暗示可能有一个额外的字节。
相比之下,十六进制编码字节几乎是微不足道的。只需在 256 个元素表中查找每个字节,即可得到两个字符并将它们吐出。它提供了相当短的密码,不使用奇怪的符号,而且很容易实现。它是具有安全意识的密码生成器的最佳选择。
也许最重要的是大多数编程语言都支持开箱即用的十六进制。C 库有printf(),可以格式化为八进制、十进制或十六进制。同样,C++ 具有使用相同格式的IO 操纵器。虽然没有对其他基础的内置支持(甚至不是 base-64),所以你必须自己做(棘手)或找到一个正确的库(违反NIH 规则(并且可能还需要做很多工作)验证它))。只使用标准库中的内容要容易得多,尤其是在它工作的时候。
hex(random_bytes)确实,使用十六进制的每个字符的安全性低于使用其他编码的安全性,但正如Eric Lagergren 在评论中有用地指出的那样,与random_bytes. 只是更长而已。事实上,你只需要 16 个字节(编码为 32 个十六进制数字)就可以拥有一个如此强大的密码,以至于任何攻击者都会在他们获得微不足道的猜测机会之前耗尽太阳系中的所有能量。大多数网站都会很乐意接受 32 个字符的密码,因此使用它们没有任何问题。
概述
虽然 Internet 上有许多“随机”密码生成器程序的示例,但生成随机性可能很棘手,并且许多程序不会以确保强大安全性的方式生成随机字符。
生成的密码类型和强度
随机密码生成器通常输出一串指定长度的符号。这些可以是来自某些字符集的单个字符、旨在形成可发音密码的音节或来自某些单词列表以形成密码短语的单词。可以自定义程序以确保生成的密码符合本地密码策略,例如始终生成字母、数字和特殊字符的组合。应该注意的是,此类策略通常会稍微低于以下公式的强度,因为符号不再是独立产生的。
考虑到这一点,让我们继续讨论为什么你只看到a-f而不是a-z。
符号集
您看到 ,a-f而不是 ,的原因a-z主要是因为它是符号集。例如:
Hexadecimal Numerals: (0-9, A-F) (e.g. WEP Key)
如您所见,可以为WEP Key使用Hexadecimal Numerals符号集创建它。如果您想要 (az, AZ, 0-9) 之类的东西,那么我建议您使用Case Sensitive Alphanumeric比十六进制符号集更复杂的符号集。
资源
您可以通过以下链接详细了解生成的密码和符号集:
希望这可以帮助您解决问题。
除了程序员很懒(如 Arminuis 的回答中所述,即使您只有 shell 和标准工具,生成十六进制密码也是一种微不足道的单行代码),可以说是“更好”的编码将更多位打包到每个字符中有可用性问题。
问题不在于转换数字很困难,或者每个字符中有奇数位,这些位不容易加起来为 2 的幂(尽管 base-32 对例如 80- 或 160- 非常有效位数)。
转换代码是六行代码,而不是精确的火箭科学(除法、模数和查表)。位不总结的事实有点烦人,但不是真正的问题。这仅意味着最后 8 个字符的编码略少于 40 位(或者最后 4 个字符编码的 base-64 略少于 24 位)。虽然这意味着这些尾随字符中的熵较少,但密码本身仍然是完全随机的,除了可能“不如你希望的优雅”之外没有其他问题。最后你拥有它的事实=也不是问题,你可以把它放在一边。
更大的问题是人际互动。Base-32 和 base-64 包含足够的符号来生成(随机)有意义的人类可读单词或单词片段,其中一些更容易接受,而另一些则产生不太好的反应。
使用十六进制, dead, f001or affe(monkey in German) 几乎是你能得到的最糟糕的结果,但在 base-32 中,子字符串fuckorwanker将是完全可行的,以及几乎所有使用罗马字母的语言中存在的所有侮辱。你去吧,为你的用户提供通用的、国际化的侮辱-o-matic。
此外,朴素的 base-32 包含符号0和O以及用户可能会混淆1,I尤其是在打印或写下密码并重新输入密码或必须通过电话进行通信时。在 base-64 中,
您不仅可以1而且I还可以增加您的快乐。而且,当然,通过电话告诉别人一个大小写混合的 20 个字符的字符串是一种幸福。ij/
存在 - 至少对于 base-32 - 一些解决方法,如 RFC4648 字母表或 Crockford 的编码,它们试图在不同程度上解决上述问题(混淆符号和侮辱-o-matic)。
然而,这些以不同的方式“笨拙”。对于经常使用该系统的人来说,很快就会发现有些地方不对劲,一些符号(尤其是0和1)似乎从未出现过。有什么东西一定要坏掉吗?
现在,十六进制有点长,要输入几个字符,但它没有上述问题,而且它是最简单的生成。