生成API密钥的最佳方法

Phi*_*ill 64 security api-key

因此,现在有很多不同的服务,谷歌API,Twitter API,Facebook API等.

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

AIzaSyClzfrOzB818x55FASHvX4JuGQciR9lv7q

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

我不是要求特定的语言,只是创建密钥的一般方法,它们是用户应用程序的详细信息加密,哈希,或随机字符串的哈希等等.我们应该担心哈希算法(MSD,SHA1,bcrypt)等?

编辑: 我已经和一些朋友(电子邮件/推特)进行了交谈,他们建议只使用带有短划线的GUID.

这对我来说似乎有些惹人注意,希望能得到更多的想法.

Edw*_*rey 45

使用专为加密设计的随机数生成器.然后base-64对数字进行编码.

这是一个C#示例:

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

  • @JamesWierzba,如果攻击者已经在您的数据库中,那么他们对您的 API 的不安全访问可能是您最不用担心的...... (11认同)
  • 存储随机生成的API密钥具有与存储散列密码相同的安全特性.在大多数情况下,没关系.正如您所建议的那样,可以将随机生成的数字视为盐,并将其与服务器密钥进行哈希处理; 但是,通过这样做,您会在每次验证时产生哈希开销.在不使所有API密钥无效的情况下,也无法使服务器机密无效. (10认同)
  • @EdwardBrey 不完全相同的特征。读取包含 API 密钥的数据库的人现在拥有有效的 API 密钥。读取哈希密码的人无法使用该哈希作为密码。 (7认同)
  • 这不是很安全,获得数据库访问权限的攻击者可以获得密钥.最好将密钥生成为用户唯一的哈希值(如盐),并结合服务器密钥. (2认同)
  • @RobGrant 好点。服务器可以将 API 密钥提供给应用程序,但将其哈希值存储在数据库中。进行身份验证时,服务器可以对所提供的 API 密钥进行哈希处理,并验证该哈希值是否与数据库中的哈希值相匹配。只要用于散列的盐与散列分开存储,这样攻击者就不可能同时获得散列和盐,散列的泄漏不会授予攻击者 API 访问权限。 (2认同)

Mik*_*uel 25

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

  • 唯一标识授权的API用户 - "API密钥"的"关键"部分
  • 验证该用户 - 无法猜到/伪造
  • 如果用户行为不当,可以撤销 - 通常他们键入可以删除记录的数据库.

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

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

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

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

并使用私人秘密签名.

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

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


或随机字符串的散列

哈希不会阻止伪造. 签名是保证钥匙来自你的原因.

  • @sappenin,是的.如果您在服务器上存储了一个不可用的密钥,那么您不需要防止伪造.API请求通常由一组机器中的任何一个处理 - **服务器是许多服务器之一.签名检查可以在任何机器上完成,无需往返数据库,在某些情况下可以避免竞争条件. (3认同)
  • 如果客户端提供的API密钥是根据提供API的服务器上已注册的API密钥的数据库进行检查,那么算法的签名步骤是否必要?如果服务器是提供密钥的服务器,那么签名在这里似乎是多余的. (2认同)
  • @MikeSamuel 如果 API 密钥已签名并且您没有往返数据库,那么当密钥被撤销但仍用于访问 API 时会发生什么? (2认同)

Chr*_*son 16

2023 注意:在 Chrome 中,默认的新标签页不允许在控制台中使用加密模块,因此请使用其他页面。

更新,在 Chrome 的控制台和 Node.js 中,您可以发出:

crypto.randomUUID()
Run Code Online (Sandbox Code Playgroud)

输出示例:

4f9d5fe0-a964-4f11-af99-6c40de98af77
Run Code Online (Sandbox Code Playgroud)

原答案(更强):

您可以通过在任何网站上打开一个新选项卡(请参阅本答案顶部的 2023 年注释)来尝试 Web 浏览器控制台,在 Chrome 上按 CTRL + SHIFT + i,然后输入以下立即调用函数表达式 (IIFE):

(async function (){
  let k = await window.crypto.subtle.generateKey(
    {name: "AES-GCM", length: 256}, true, ["encrypt", "decrypt"]);
  const jwk = await crypto.subtle.exportKey("jwk", k)
  console.log(jwk.k)
})()
Run Code Online (Sandbox Code Playgroud)

输出示例:

gv4Gp1OeZhF5eBNU7vDjDL-yqZ6vrCfdCzF7HGVMiCs
Run Code Online (Sandbox Code Playgroud)

参考:

https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey

https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/exportKey

我承认我写这篇文章主要是为了自己以后参考......


Edw*_*rey 6

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

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();
}
Run Code Online (Sandbox Code Playgroud)


Ada*_*lph 5

我使用UUID,格式为小写,没有破折号.

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

API密钥可能会受到损害,在这种情况下,用户可能希望取消其API密钥并生成新密钥,因此密钥生成方法必须能够满足此要求.

  • 不要以为UUID难以猜测; 它们不应该用作安全功能(UUID规范[RFC4122第6节](http://tools.ietf.org/html/rfc4122#section-6)).API密钥需要一个安全的随机数,但[UUID不是安全无法使用的](http://stackoverflow.com/q/3652944/145173). (13认同)
  • @MicroR只有当用于制作它的随机数生成器具有加密安全性且128位足够时,随机UUID才是安全的.尽管UUID RFC不需要安全的随机数生成器,但给定的实现可以免费使用.在[randomUUID](https://docs.oracle.com/javase/6/docs/api/java/util/UUID.html#randomUUID())的情况下,API文档明确声明它使用"加密"强伪随机数发生器".因此,特定的实现对于128位API密钥是安全的. (5认同)
  • @EdwardBrey在Java中用`UUID uuid = UUID.randomUUID();`怎么样?你是说随机不够好吗? (3认同)