服务如何生成和使用公共和秘密 API 密钥?

Hyp*_*olf 6 security api-design node.js

Google、Stripe 和许多其他公司都有公共 API 密钥和秘密 API 密钥。

生成随机字符串很容易,但我的问题是,如何生成公钥和私钥、存储它们并正确使用它们?

公共 API 密钥用于告知用户是谁,秘密用于确认用户的身份。

我的流程如下: - 用户创建帐户 - 用户激活服务(内部) - 服务返回公共和秘密 API 密钥(UARRHAtPtJcLxx5RmMWo9oTrca4gRt2k、C9YS7Mhzichq2vqBuRkNJxkNci5W2Xua) - 用户在他/她的网站和网站上使用公共密钥服务器端的私钥

我使用的是nodejs,当用户请求API密钥时,公钥是按需生成的:

let public = await crypto.randomBytes(32).toString('base64');
Run Code Online (Sandbox Code Playgroud)

将秘密存储在数据库中就像以明文存储密码一样。我想我们不想要这个,它需要以某种方式进行散列。例如,我是否生成一个“私钥”并使用 argon2 对其进行哈希处理?用户将永远无法再次看到他/她的密钥,并且需要立即保存它,这是一个好的做法吗?

我找不到太多关于这应该如何工作的信息。

Gab*_*yel 6

从技术上讲,您所指的只是用户名和密码。唯一重要的区别是这些通常由 API 生成并且非常随机,而不是由用户选择的真实用户名和密码,并且通常不是非常随机。(调用这些公钥和私钥有点误导,因为公钥密码学是不同的——你通常不需要 API 密钥,管理 PKI 是一堆蠕虫,而且正确执行它的成本也很高。)

由于这些在技术上与用户名和密码相同,因此您希望以类似方式对待它们。让我们称这些客户端 ID(“公共”部分)和客户端密钥(“秘密”部分)。

一些想法:

  • 您应该使用加密安全的随机生成器来生成随机字符串。crypto.randomBytes()如上所述很好。
  • 您应该考虑使用熵来为密钥设置适当的长度。熵基本上是密钥的“随机性”,以位为单位。仅举个例子,如果密钥空间是 1024 个具有相同概率的不同可能密钥,那么您可以说密钥具有 log2(1024) = 10 位熵。熵将密钥的长度与随机源的安全性分离,例如,您可以拥有仍然不安全的很长的密钥,因为随机源存在缺陷。您希望密钥有多少熵取决于用例,例如在任何情况下是否可能进行离线攻击或仅在线请求等等(您也应该将离线攻击计算在内,这是非常重要的)快速地)。根据经验,您不应低于 128 位的熵,为了更高的安全性,您可能应该使用 256+ 位。如果密钥区分大小写和字母数字,则有 62 个不同的可能字符,22 的密钥长度提供 ~131 位 ( log2(62^22) =~ 130.99)。当然,您总是可以使用更长的时间,对于 256 位,您需要长度为 43 且区分大小写的字母数字。
  • 正如您正确指出的那样,存储此类密钥至关重要。至少,您希望将它们散列存储(使用适当的散列,见下文),就像任何其他密码一样,这样即使攻击者获得了对您数据库的访问权限,他们也不会看到您的 api 密钥. 任何适当的密钥派生函数(Argon2、bcrypt、PBKDF2 等)都适合此目的,但普通的加密哈希函数(sha1、sha2 等)则不适合。
  • 你是对的,如果你散列这些秘密,用户将只能在它们生成时看到它们,再也不会看到它们。这正是安全在线服务中发生的情况,通常是一种很好的做法。您可以警告您的用户记下该机密,因为您将无法再次向他们显示。(如果他们忘记了,他们也可以理想地生成一个新的,所以这通常没什么大不了的。)
  • 更好的是将这些密钥存储在其他地方。考虑到云,此类秘密的好地方是您的云提供商提供的服务,例如 AWS 中的 Secrets Manager。好处包括使用 KMS 密钥进行加密、审计,并且您可以确保访问您的密钥的唯一方法是通过适当的 IAM 角色(例如,您无需担心诸如泄露备份之类的事情)。
  • 虽然客户端 ID 不是机密,但您需要在存储中保护其完整性(以及与客户端机密关联的完整性)。想象一个场景,攻击者可以以某种方式更改您的数据库并将不同的(攻击者已知的)客户端密钥分配给现有客户端 ID。这将意味着与该客户端 ID 相关的数据完全泄露。因此,您要确保除了保持客户端机密安全外,攻击者也无法更改这些内容(例如通过对相关组件的访问控制)。

  • 你说得对,我还没有考虑过这个问题,谢谢!使用像 sha2 这样的普通哈希函数不会大幅增加风险,但我认为会增加一点风险,具体取决于我们是否接受的先决条件。我明天将编辑我的答案(当不通过电话时)。 (2认同)