为什么要使用 API 密钥和密钥?

125

我遇到了许多提供用户API密钥和秘钥的API。但我的问题是:两者之间有什么区别?

在我看来,一个密钥就足够了。假设我有一个密钥,只有我和服务器知道它。我使用此密钥创建HMAC哈希并进行API调用。在服务器上,我们再次创建HMAC哈希并将其与发送的哈希进行比较。如果相同,则调用已通过身份验证。

那么为什么要使用两个密钥呢?

编辑:或者该API密钥用于查找API秘钥吗?


4个回答

82
你需要两个独立的密钥,一个用于告诉他们你是谁,另一个用于证明你就是你所说的那个人。 "密钥" 是你的用户 ID,而 "密码" 是你的口令。他们只是使用 "密钥" 和 "密码" 的术语,因为这是他们实现的方式。

5
如果您正在使用HTTPS进行通信,那加密消息有什么意义呢?使用秘密密钥加密消息的目的是什么? - kamuniaft
11
重点始终是降低风险。如果HTTPS通信被攻击,那么能够读取您请求的攻击者将无法伪造新请求。如果您的API是用于排列猫图片的,那没什么大不了的,但如果它是一个支付API,最好有多个安全层面 :) - Yall
1
我想,将其分为两个密钥的目的是因为单个客户端应用程序的不同用户可能具有不同的密钥。否则,如果他们都有相同的密钥,那么拥有一个密钥就没有用了。对吧? - mfaani
1
为什么这些API不使用“Bearer:”身份验证呢?你会在那里有一个ID和密码。 - Stefan Haberl

11

简单回答,如果我理解正确的话...

如果您使用API密钥进行加密,服务将如何知道是谁在联系他们?他们如何解密该消息?

您使用API密钥来说明您是谁,这是您以明文形式发送的内容。 您不会向任何人发送SECRET密钥。您只需将其用于加密,然后发送加密消息。您不会发送用于加密的密钥,否则就失去了加密的目的。


你需要发送API密钥到服务器。这意味着你正在将该值提供给可能拦截你与服务器通信的任何人。 - AndroC
我看到的几乎所有API都要求你将密钥和秘钥一起发送到服务器。与服务器的连接使用理论上相同级别的加密进行保护。但是除了服务器之外,我从未将它们提供给任何其他人。 - sudo
我从未见过明文发送“secret”。你能给我一个链接吗?我所看到的是使用“secret”来加密一些数据。并且随着加密数据,发送“apiKey”,以便服务器知道如何解密数据。 - AndroC
我看到的激发我在StackOverflow上搜索的例子是https://www.twilio.com/docs/sms/tutorials/how-to-send-sms-messages-bash-command-line和https://nexmo.github.io/Quickstarts/sms/send/。 - sudo
Twilio并没有使用这些术语。但Nexmo确实是... 但是,经过快速浏览,似乎他们只是将数据称为“secret”和“apiKey”,而他们实际上正在做的是“用户名”和“密码”。这完全是两码事... - AndroC

11

在这里我没有看到提到的一件事,虽然这是Marcus Adams答案的扩展,但如果有时序攻击的可能性,您不应该使用单个信息来标识和验证用户,因为时序攻击可以利用响应时间的差异猜测字符串比较的进程。

如果您正在使用一个系统来使用“键”查找用户或凭证,那么这个信息可以通过发送成千上万的请求并检查数据库查找(或未找到)记录所需的时间来逐步猜测。如果“键”以明文而不是“键”的单向哈希存储,则更是如此。如果您需要再次显示密钥,则需要将用户的密钥以明文或对称加密方式存储。

通过拥有第二个信息或“秘钥”,您可以首先使用“键”查找用户或凭证,然后使用时序安全比较函数来检查“秘钥”的值。

以下是Python实现该功能的代码:

https://github.com/python/cpython/blob/cd8295ff758891f21084a6a5ad3403d35dda38f7/Modules/_operator.c#L727

这个问题暴露在hmac库中(可能还有其他库):

https://docs.python.org/3/library/hmac.html#hmac.compare_digest


需要注意的是,我认为这种攻击方式不会对在查找之前进行哈希或加密的值起作用,因为正在比较的值在输入字符串中每次更改一个字符时都会随机变化。我在这里找到了一个很好的解释。

存储API密钥的解决方案如下:

  1. 使用单独的密钥和秘钥,使用密钥查找记录,并使用时间安全比较来检查秘钥。这使您能够再次向用户显示密钥和秘钥。
  2. 使用单独的密钥和秘钥,在秘钥上使用对称、确定性加密,并对加密后的秘钥进行常规比较。这使您能够再次向用户显示密钥和秘钥,并可以避免实现时间安全比较。
  3. 使用单独的密钥和秘钥,显示秘钥,对其进行哈希并存储,然后对哈希后的秘钥进行常规比较。这消除了使用双向加密的必要性,并具有在系统被攻击时保持秘钥安全的附加优势。缺点是不能再次向用户显示秘钥。
  4. 使用单一密钥,向用户显示一次,对其进行哈希,然后对哈希或加密后的密钥进行常规查找。这使用单个密钥,但无法再次向用户显示。如果系统被攻击,有保持密钥安全的好处。
  5. 使用单一密钥,向用户显示一次,加密它,然后对加密后的秘密进行常规查找。可以再次向用户显示,但代价是如果系统被攻击,密钥易受攻击。

其中,我认为3是安全和便利之间的最佳平衡。在获取密钥时,我已经看到许多网站采用了这种方法。

此外,我邀请任何真正的安全专家批评此答案。我只是想把这个作为另一个讨论点。


8
有些答案解释了什么是秘钥和公钥。它们是一对公私密钥,但命名方式可能会让人困惑。但没有人说过为什么API需要两者,很多API只会提供一个秘钥!我也从来没有看到任何API文档解释为什么有两个密钥,所以我能做的最好猜测……

最好在请求中只放置您的公钥,并使用您的私钥在本地签署请求;不需要发送其他内容。但有些人只将秘钥放在请求中。好吧,任何好的API都会使用一些传输安全机制,比如TLS(通常通过HTTPS)。但是这样您仍然将您的私钥暴露给服务器,增加了他们某种方式处理错误的风险(参见:GitHub和Twitter最近发现的密码记录漏洞)。理论上,HTTPS也同样安全,但总有实现缺陷存在。

但许多 - 实际上似乎大多数 - API要求您在请求中发送两个密钥,因为这比让用户自己签名容易得多;否则无法获得纯cURL示例!在这种情况下,将它们分开是毫无意义的。我想分离密钥只是为了以防将来改变API以利用它们。或者一些客户端库可能会更安全地执行。


提到cURL是个好点子。我同意,而且很奇怪这些细节经常被忽视和解释不清楚。 - Daniel Miradakis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接