根据https://www.owasp.org/index.php/Insufficient_Session-ID_Length:
假设会话标识符是使用良好的随机数源生成的,我们将估计会话标识符中的熵位数为会话标识符中总位数的一半。
如何确定特定实现实际包含多少位熵?例如,如果我在 Java 下执行new BigInteger(130, new SecureRandom(),我真的会得到 130 位的熵吗?
根据https://www.owasp.org/index.php/Insufficient_Session-ID_Length:
假设会话标识符是使用良好的随机数源生成的,我们将估计会话标识符中的熵位数为会话标识符中总位数的一半。
如何确定特定实现实际包含多少位熵?例如,如果我在 Java 下执行new BigInteger(130, new SecureRandom(),我真的会得到 130 位的熵吗?
在熵会话标识符是底层数量的源的熵; PRNG 可能会产生 32 位的良好随机字,每个字都有 32 位的熵(但是,正如@forrest 指出的那样,熵不会“携带” - 它会增长直到达到 PRNG 内部状态的大小,从理论上讲,给定足够的输出,可以对状态进行反向计算。从那时起,PRNG 的输出是可预测的,熵为零。“真正随机”的生成器具有无限大小的内部状态)。
这意味着每个位或多或少是完全不可预测的(“或多或少”源于 PRNG 是(P)伪随机数生成器)。
当这个 32 位数字在字符串标识符中编码时,通常通过 ascii 或 utf8 中的十六进制编码,它变成类似"1A9F72EF"
. 现在这些是 8个字节,所以 64位,但它们仍然包含与以前相同的熵,即 32 位。
如果我们使用 base64 编码,我们的标识符每个字符会有 6 位熵,即 8 位;因此熵将是标识符长度的 3/4(或标识符将是其二进制表示长度的 8/6)。
这就是 OWASP 的“1/2”的来源。
现在,不能通过观察单个实例来确定源的熵;通常你需要确切地知道源是如何工作的。根据定义,随机源是纯熵。即使“42”由六位或八位表示,恒定源(始终返回 42)的熵也为零。
使用非常粗略且不可信的近似值,熵是从一个表示到下一个表示平均变化的比特数的两倍,前提是这些变化是随机且不重复的。[ 00FF, FF00, 00FF, FF00 ... ] 序列的所有位都会发生变化,但序列中只有两个符号,因此一个位足以完整描述它;生成器的内部状态是一位宽。
最大的问题是确定一个来源是否是“真正的”(伪)随机的。
如果您可以信任SecureRandom
(您可能可以!),那么是的,BigInteger(130, new SecureRandom())
会给您一个具有 130 位熵的数字。您可以将其编码为 260 位十六进制序列或 176 位 Base64 序列。
只需确保熵池馈送SecureRandom
足够频繁地刷新并具有一些真正的随机性,否则您的标识符可能会变得可预测。一个(不)著名的案例是Mersenne Twister,其中从生成器中有大约 624 个顺序值允许通过线性代数重建其内部状态的 19967 位,从而确定地预测其所有未来值。