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

时间:2019-03-05 18:43:12

标签: node.js security api-design

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

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

公共API密钥用于告诉用户是谁,秘密在于确认其身份。

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

我使用的是nodejs,当用户要求提供API密钥时,按需生成了公共密钥:

let public = await crypto.randomBytes(32).toString('base64');

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

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

2 个答案:

答案 0 :(得分:5)

从技术上讲,您所指的只是用户名和密码。唯一重要的区别是这些通常由 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 相关的数据完全泄露。因此,您要确保除了保持客户端机密安全外,攻击者也无法更改这些内容(例如通过对相关组件的访问控制)。

答案 1 :(得分:0)

我认为我们可以使用以下代码生成一对公共密钥和私有密钥(私钥)。您可以参考链接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)

      }
    });

现在我们有了秘密密钥和公钥。...因此我们可以实现HMAC身份验证... ref .. 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);
});