【问题标题】:What's the best approach for generating a new API key?生成新 API 密钥的最佳方法是什么?
【发布时间】:2020-10-05 13:18:23
【问题描述】:

所以现在有很多不同的服务,Google API、Twitter API、Facebook API 等等。

每个服务都有一个 API 密钥,例如:

AIzaSyClzfrOzB818x55FASHvX4JuGQciR9lv7q

所有密钥的长度和它们包含的字符都不同,我想知道生成 API 密钥的最佳方法是什么?

我不是要求一种特定的语言,只是创建密钥的一般方法,它们应该是对用户应用程序细节的加密,还是散列,或随机字符串的散列等。我们应该担心关于哈希算法(MSD、SHA1、bcrypt)等?

编辑: 我和几个朋友谈过(电子邮件/推特),他们建议只使用去掉破折号的 GUID。

这对我来说似乎有点 hacky,希望能得到更多的想法。

【问题讨论】:

标签: security api-key


【解决方案1】:

如果您想要一个只有字母数字字符的 API 密钥,您可以使用 base64-random 方法的变体,只使用 base-62 编码。 base-62 编码器基于this

public static string CreateApiKey()
{
    var bytes = new byte[256 / 8];
    using (var random = RandomNumberGenerator.Create())
        random.GetBytes(bytes);
    return ToBase62String(bytes);
}

static string ToBase62String(byte[] toConvert)
{
    const string alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    BigInteger dividend = new BigInteger(toConvert);
    var builder = new StringBuilder();
    while (dividend != 0) {
        dividend = BigInteger.DivRem(dividend, alphabet.Length, out BigInteger remainder);
        builder.Insert(0, alphabet[Math.Abs(((int)remainder))]);
    }
    return builder.ToString();
}

【讨论】:

    【解决方案2】:

    使用专为密码学设计的随机数生成器。然后base-64编码数字。

    这是一个 C# 示例:

    var key = new byte[32];
    using (var generator = RandomNumberGenerator.Create())
        generator.GetBytes(key);
    string apiKey = Convert.ToBase64String(key);
    

    【讨论】:

    • 这不是很安全,访问您的数据库的攻击者可以获得密钥。最好将密钥生成为用户独有的哈希值(如盐),并结合服务器机密。
    • 存储随机生成的 API 密钥与存储散列密码具有相同的安全特性。在大多数情况下,这很好。正如您所建议的,可以将随机生成的数字视为盐并使用服务器机密对其进行散列;但是,这样做会在每次验证时产生散列开销。如果不使所有 API 密钥失效,也无法使服务器机密失效。
    • 不错的解决方案,但您可能需要在 apiKey 之前使用 var 关键字:) var apiKey = Convert.ToBase64String(key);
    • @JamesWierzba 如果被攻击者已经在您的数据库中,那么他们对您的 API 的不安全访问可能是您最不关心的问题...
    • @EdwardBrey 的特征不太一样。读取包含 API 密钥的数据库的人现在拥有一个有效的 API 密钥。读取散列密码的人不能使用该散列作为密码。
    【解决方案3】:

    API 密钥需要具备以下属性:

    • 唯一标识授权 API 用户——“API 密钥”的“密钥”部分
    • 验证该用户 - 无法猜测/伪造
    • 可以在用户行为不端时被撤销 - 通常他们键入可以删除记录的数据库。

    通常您将拥有数千或数百万个 API 密钥,而不是数十亿,因此它们不需要:

    • 可靠地存储有关 API 用户的信息,因为这些信息可以存储在您的数据库中。

    因此,生成 API 密钥的一种方法是获取两条信息:

    1. 保证唯一性的序列号
    2. 足够的随机位来填充密钥

    并使用私人秘密对其进行签名。

    计数器保证他们唯一标识用户,并且签名防止伪造。可撤销性要求在执行任何需要 API 密钥授权的操作之前检查密钥在数据库中是否仍然有效。

    如果您需要从多个数据中心生成密钥,或者没有其他好的分布式方式来分配序列号,那么一个好的 GUID 生成器是递增计数器的一个很好的近似值。


    或随机字符串的散列

    散列并不能防止伪造。 Signing 保证密钥来自您。

    【讨论】:

    • 如果根据提供 API 的服务器上已注册 API 密钥的数据库检查客户端提供的 API 密钥,您的算法的签名步骤是否必要?如果服务器是提供密钥的服务器,似乎在这里签名将是多余的。
    • @sappenin,是的。如果您在服务器上存储了一个不可猜测的密钥,那么您就不需要防止伪造。 API 请求通常由一组机器中的任何一台处理—— 服务器是众多服务器之一。签名检查可以在任何机器上完成,无需往返数据库,这可以避免在某些情况下出现竞争条件。
    • @MikeSamuel 如果 API 密钥已签名并且您没有往返数据库,那么当密钥被撤销但仍用于访问 API 时会发生什么?
    • @AbhyuditJain,在任何分布式系统中,您都需要一致的消息顺序(撤销happen-before 随后使用已撤销凭据)或其他方式来约束歧义。有些系统不会对每个请求进行往返——如果一个节点缓存了一个密钥在数据库中存在 10 分钟的事实,那么只有 10 分钟。攻击者可以在其中滥用已撤销凭据的窗口。但是可能会导致混淆:用户撤销一个凭证,然后测试它是否被撤销,并且感到惊讶,因为非粘性会话导致两个请求转到不同的节点。
    【解决方案4】:

    API 密钥应该是某个随机值。随机到无法预测。它不应包含其所针对的用户或帐户的任何详细信息。如果您确定创建的 ID 是随机的,则使用 UUID 是个好主意。

    例如,早期版本的 Windows 生成可预测的 GUID,但这是一个老故事。

    【讨论】:

    【解决方案5】:

    我使用 UUID,格式为小写,不带破折号。

    生成很容易,因为大多数语言都内置了它。

    API 密钥可能会被泄露,在这种情况下,用户可能希望取消其 API 密钥并生成一个新的,因此您的密钥生成方法必须能够满足这一要求。

    【讨论】:

    • 不要假设 UUID 很难猜;它们不应用作安全功能(UUID 规范 RFC4122 section 6)。 API 密钥需要一个安全的随机数,但 UUIDs are not securely unguessable.
    • @EdwardBrey Java 中的UUID uuid = UUID.randomUUID(); 怎么样?你是说随机还不够好?
    • @MicroR 随机 UUID 仅在用于使其具有加密安全性且 128 位足够的随机数生成器时才是安全的。尽管 UUID RFC 不需要安全的随机数生成器,但给定的实现可以免费使用。对于randomUUID,API 文档特别声明它使用“加密强伪随机数生成器”。因此,对于 128 位 API 密钥,该特定实现是安全的。
    猜你喜欢
    • 2013-09-26
    • 2010-10-12
    • 2022-11-03
    • 2016-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-16
    相关资源
    最近更新 更多