服务如何生成和使用 public 和秘密 API 密钥?
How services generate and use public and secret API keys?
Google、Stripe 和许多其他公司都有 public API 密钥和 Secret API 密钥。
生成随机字符串很容易,但我的问题是,如何生成 public 和密钥、存储它们并正确使用它们?
publicAPI关键是告诉用户是谁,秘密是确认他们的身份。
我的流程如下:
- 用户创建一个帐户
- 用户激活服务(内部)
- 服务 return 一个 public 和一个秘密 API 密钥(UARRHAtPtJcLxx5RmMWo9oTrca4gRt2k,C9YS7Mhzichq2vqBuRkNJxkNci5W2Xua)
- 用户在 his/her 网站上使用 public 密钥,在服务器端使用私钥
我正在使用 nodejs 并且 public 密钥是按需生成的,当用户要求 API 密钥时:
let public = await crypto.randomBytes(32).toString('base64');
将秘密存储在数据库中就像以明文形式存储密码一样。
我想我们不想要这个,它需要以某种方式散列。我是否生成 "private" 密钥并使用 argon2 对其进行哈希处理?用户将永远无法再次看到 his/her 密钥,需要立即保存它,这是好习惯吗?
我找不到太多关于它应该如何工作的信息。
我想我们可以使用下面的代码生成一对 public 密钥和秘密密钥(私钥)..你可以参考 link link to generate key pair here doc
var pk="";
var sk="";
var string= payload;
const { generateKeyPair } = require('crypto');
generateKeyPair('rsa', {
modulusLength: 4096,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: 'top secret'
}
}, (err, publicKey, privateKey) => {
try {
pk=publicKey;
sk=privateKey;
} catch (error) {
console.log(err)
}
});
现在我们有了密钥和 public 密钥....因此我们可以实现 HMAC 身份验证...参考..go for hmac authentication doc
var hmac = crypto.createHmac('sha384', sk).update(string).digest('hex');
request.post({uri:..., json: { hmac, pk, string }, function(err, response, body) {
console.log(body);
});
从技术上讲,您所指的只是用户名和密码。唯一重要的区别是这些通常由 API 生成并且非常随机,这与用户选择的真实用户名和密码相反,通常不是非常随机。 (将这些称为 public 和私钥有点误导,因为 public 密钥密码学是不同的 - 您通常不需要 API 密钥,管理 PKI 是一堆蠕虫,而且正确地做到这一点也非常昂贵。)
由于这些在技术上与用户名和密码相同,因此您希望以类似的方式对待它们。我们称这些客户端 ID(“public”部分)和客户端密钥(“秘密”部分)。
一些想法:
- 您应该使用密码安全的随机生成器来生成随机字符串。
crypto.randomBytes()
如上即可。
- 您应该考虑熵来为密钥设置适当的长度。熵基本上是密钥的“随机性”,以位为单位。仅举个例子,如果密钥 space 是 1024 个具有相同概率的不同可能密钥,那么您可以说该密钥具有 log2(1024) = 10 位熵。熵将密钥的长度与随机源的安全性分离,例如,您可以拥有非常长的密钥,但仍然不安全,因为随机源存在缺陷。您需要多少熵取决于用例,例如在任何情况下都可能发生离线攻击或仅在线请求等等(您也应该算上离线攻击,非常 快)。根据经验,您不应低于 128 位的熵,并且为了更高的安全性,您应该使用 256 位以上的熵。如果密钥区分大小写和字母数字,则有 62 个不同的可能字符,22 的密钥长度提供 ~131 位 (
log2(62^22) =~ 130.99
)。当然,您总是可以更长,对于 256 位,您需要长度为 43 且区分大小写的字母数字。
- 正如您正确指出的那样,存储此类密钥至关重要。至少,您希望将它们存储为哈希值(使用适当的哈希值,见下文),就像任何其他密码一样,这样即使攻击者获得了对您数据库的访问权限,他们也不会看到您的 api 键。任何适当的密钥派生函数(Argon2、bcrypt、PBKDF2 等)都适用于此目的,但普通加密哈希函数(sha1、sha2 等)not.
- 你是对的,如果你对这些秘密进行哈希处理,用户将只能在它们生成时看到它们,而永远不会再看到。这正是安全在线服务中发生的事情,通常是一种很好的做法。您可以警告您的用户注意这个秘密,因为您将无法再次向他们展示。 (如果他们忘记了,他们也可以理想地生成一个新的,所以这通常没什么大不了的。)
- 更好的方法是将这些密钥存储在其他地方。考虑到云,此类秘密的一个好地方是您的云提供商提供的服务,例如 AWS 中的 Secrets Manager。好处包括使用 KMS 密钥加密、审计,并且您可以确保访问密钥的唯一方法是通过适当的 IAM 角色(例如,您无需担心公开的备份等事情)。
- 虽然客户端 ID 不是机密,但您需要在存储中保护其完整性(以及与客户端机密关联的完整性)。想象一个场景,攻击者可以以某种方式改变您的数据库并将不同的(攻击者已知的)客户端密码分配给现有的客户端 ID。这将意味着与该客户端 ID 关联的数据的完全泄露。因此,您要确保除了保持客户端机密安全之外,攻击者也不可能更改这些机密(例如通过对所涉及组件的访问控制)。
Google、Stripe 和许多其他公司都有 public API 密钥和 Secret API 密钥。
生成随机字符串很容易,但我的问题是,如何生成 public 和密钥、存储它们并正确使用它们?
publicAPI关键是告诉用户是谁,秘密是确认他们的身份。
我的流程如下: - 用户创建一个帐户 - 用户激活服务(内部) - 服务 return 一个 public 和一个秘密 API 密钥(UARRHAtPtJcLxx5RmMWo9oTrca4gRt2k,C9YS7Mhzichq2vqBuRkNJxkNci5W2Xua) - 用户在 his/her 网站上使用 public 密钥,在服务器端使用私钥
我正在使用 nodejs 并且 public 密钥是按需生成的,当用户要求 API 密钥时:
let public = await crypto.randomBytes(32).toString('base64');
将秘密存储在数据库中就像以明文形式存储密码一样。 我想我们不想要这个,它需要以某种方式散列。我是否生成 "private" 密钥并使用 argon2 对其进行哈希处理?用户将永远无法再次看到 his/her 密钥,需要立即保存它,这是好习惯吗?
我找不到太多关于它应该如何工作的信息。
我想我们可以使用下面的代码生成一对 public 密钥和秘密密钥(私钥)..你可以参考 link link to generate key pair here doc
var pk="";
var sk="";
var string= payload;
const { generateKeyPair } = require('crypto');
generateKeyPair('rsa', {
modulusLength: 4096,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: 'top secret'
}
}, (err, publicKey, privateKey) => {
try {
pk=publicKey;
sk=privateKey;
} catch (error) {
console.log(err)
}
});
现在我们有了密钥和 public 密钥....因此我们可以实现 HMAC 身份验证...参考..go for hmac authentication doc
var hmac = crypto.createHmac('sha384', sk).update(string).digest('hex');
request.post({uri:..., json: { hmac, pk, string }, function(err, response, body) {
console.log(body);
});
从技术上讲,您所指的只是用户名和密码。唯一重要的区别是这些通常由 API 生成并且非常随机,这与用户选择的真实用户名和密码相反,通常不是非常随机。 (将这些称为 public 和私钥有点误导,因为 public 密钥密码学是不同的 - 您通常不需要 API 密钥,管理 PKI 是一堆蠕虫,而且正确地做到这一点也非常昂贵。)
由于这些在技术上与用户名和密码相同,因此您希望以类似的方式对待它们。我们称这些客户端 ID(“public”部分)和客户端密钥(“秘密”部分)。
一些想法:
- 您应该使用密码安全的随机生成器来生成随机字符串。
crypto.randomBytes()
如上即可。 - 您应该考虑熵来为密钥设置适当的长度。熵基本上是密钥的“随机性”,以位为单位。仅举个例子,如果密钥 space 是 1024 个具有相同概率的不同可能密钥,那么您可以说该密钥具有 log2(1024) = 10 位熵。熵将密钥的长度与随机源的安全性分离,例如,您可以拥有非常长的密钥,但仍然不安全,因为随机源存在缺陷。您需要多少熵取决于用例,例如在任何情况下都可能发生离线攻击或仅在线请求等等(您也应该算上离线攻击,非常 快)。根据经验,您不应低于 128 位的熵,并且为了更高的安全性,您应该使用 256 位以上的熵。如果密钥区分大小写和字母数字,则有 62 个不同的可能字符,22 的密钥长度提供 ~131 位 (
log2(62^22) =~ 130.99
)。当然,您总是可以更长,对于 256 位,您需要长度为 43 且区分大小写的字母数字。 - 正如您正确指出的那样,存储此类密钥至关重要。至少,您希望将它们存储为哈希值(使用适当的哈希值,见下文),就像任何其他密码一样,这样即使攻击者获得了对您数据库的访问权限,他们也不会看到您的 api 键。任何适当的密钥派生函数(Argon2、bcrypt、PBKDF2 等)都适用于此目的,但普通加密哈希函数(sha1、sha2 等)not.
- 你是对的,如果你对这些秘密进行哈希处理,用户将只能在它们生成时看到它们,而永远不会再看到。这正是安全在线服务中发生的事情,通常是一种很好的做法。您可以警告您的用户注意这个秘密,因为您将无法再次向他们展示。 (如果他们忘记了,他们也可以理想地生成一个新的,所以这通常没什么大不了的。)
- 更好的方法是将这些密钥存储在其他地方。考虑到云,此类秘密的一个好地方是您的云提供商提供的服务,例如 AWS 中的 Secrets Manager。好处包括使用 KMS 密钥加密、审计,并且您可以确保访问密钥的唯一方法是通过适当的 IAM 角色(例如,您无需担心公开的备份等事情)。
- 虽然客户端 ID 不是机密,但您需要在存储中保护其完整性(以及与客户端机密关联的完整性)。想象一个场景,攻击者可以以某种方式改变您的数据库并将不同的(攻击者已知的)客户端密码分配给现有的客户端 ID。这将意味着与该客户端 ID 关联的数据的完全泄露。因此,您要确保除了保持客户端机密安全之外,攻击者也不可能更改这些机密(例如通过对所涉及组件的访问控制)。