0

不要使用UUID,它不安全!

 1 year ago
source link: https://www.jdon.com/63422
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

不要使用UUID,它不安全!


如果您需要一个不可猜测的随机字符串(例如,用于会话 cookie 或访问令牌),可能很想获取一个随机 UUID,如下所示:

88cf3e49-e28e-4c0e-b95f-6a68a785a89d

这是一个 128 位值,格式为 36 个十六进制数字,用连字符分隔。在 Java 和大多数其他编程语言中,这些很容易生成:

import java.util.UUID;
String id = UUID.randomUUID().toString();

在引擎盖下,这使用了一个加密安全的伪随机数发生器(CSPRNG),所以生成的ID是相当独特的。然而,使用随机UUID有一些缺点,使它们没有最初看起来那么有用。在这篇笔记中,我将描述这些缺点以及我建议使用的替代方法。

...
详细点击标题

122 位 UUID 面对黑客攻击只能坚持……2048 秒,或者少于 35 分钟。

对 Java 的随机 UUID 实现的一个具体批评是:
它在内部使用单个共享SecureRandom实例来生成随机位。根据配置的后端,这可能会获得一个锁,如果您生成大量随机令牌,特别是如果您使用系统阻塞熵源(不要那样做,请使用 /dev/urandom),这可能会变得激烈竞争。通过滚动您自己的令牌生成,您可以使用本地线程或 SecureRandom 实例池来避免此类争用。(注意——NativePRNG 在内部使用共享静态锁,所以这在这种情况下没有帮助,但它也持有较短的关键部分的锁,因此不太容易出现问题)。

我们应该用什么代替?
我的建议是使用一个 160 位(20 字节)的随机值,然后对其进行URL 安全的 base64编码。URL 安全的 base64 变体几乎可以在任何地方使用,而且相当紧凑。在Java中:

import java.security.SecureRandom;
import java.util.Base64;

public class SecureRandomString {
    private static final SecureRandom random = new SecureRandom();
    private static final Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding();

    public static String generate() {
        byte[] buffer = new byte[20];
        random.nextBytes(buffer);
        return encoder.encodeToString(buffer);
    }
}

这产生的输出值如下:
Xl3S2itovd5CDS7cKSNvml4_ODA

这比 UUID 更短,而且具有 160 位的熵更安全。
如果需要,我还可以将 SecureRandom 变成 ThreadLocal。

那么我们的极端攻击者需要多长时间才能找到一个 160 位的随机令牌?大约一千七百九十万年。通过稍微调整我们的令牌格式,我们可以从担心攻击者的能力和资源转移到内心的平静和幸福。这远远超出了我们可以停止担心的可能范围。
为什么不更进一步呢?为什么不是 256 位?
这会将攻击成本推向更加荒谬的境地。我发现 160 位是出色安全性的最佳选择,同时仍然具有紧凑的字符串表示形式。


 


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK