生成新 API 密钥的最佳方法是啥?
Posted
技术标签:
【中文标题】生成新 API 密钥的最佳方法是啥?【英文标题】:What's the best approach for generating a new API key?生成新 API 密钥的最佳方法是什么? 【发布时间】:2013-01-02 22:38:01 【问题描述】:所以现在有很多不同的服务,Google API、Twitter API、Facebook API 等等。
每个服务都有一个 API 密钥,例如:
AIzaSyClzfrOzB818x55FASHvX4JuGQciR9lv7q
所有密钥的长度和它们包含的字符都不同,我想知道生成 API 密钥的最佳方法是什么?
我不是要求一种特定的语言,只是创建密钥的一般方法,它们应该是对用户应用程序细节的加密,还是散列,或随机字符串的散列等。我们应该担心关于哈希算法(MSD、SHA1、bcrypt)等?
编辑: 我和几个朋友谈过(电子邮件/推特),他们建议只使用去掉破折号的 GUID。
这对我来说似乎有点 hacky,希望能得到更多的想法。
【问题讨论】:
我已经在这里详细回答了。generating keys and using it as hmac auth 【参考方案1】:使用专为密码学设计的随机数生成器。然后base-64编码数字。
这是一个 C# 示例:
var key = new byte[32];
using (var generator = RandomNumberGenerator.Create())
generator.GetBytes(key);
string apiKey = Convert.ToBase64String(key);
【讨论】:
这不是很安全,访问您的数据库的攻击者可以获得密钥。最好将密钥生成为用户独有的哈希值(如盐),并结合服务器机密。 存储随机生成的 API 密钥与存储散列密码具有相同的安全特性。在大多数情况下,这很好。正如您所建议的,可以将随机生成的数字视为盐并使用服务器机密对其进行散列;但是,这样做会在每次验证时产生散列开销。如果不使所有 API 密钥失效,也无法使服务器机密失效。 不错的解决方案,但您可能需要在 apiKey 之前使用 var 关键字:) var apiKey = Convert.ToBase64String(key); @JamesWierzba 如果被攻击者已经在您的数据库中,那么他们对您的 API 的不安全访问可能是您最不关心的问题... @EdwardBrey 的特征不太一样。使用 API 密钥读取数据库的人现在拥有一个有效的 API 密钥。读取散列密码的人不能使用该散列作为密码。【参考方案2】:API 密钥需要具备以下属性:
唯一标识授权 API 用户——“API 密钥”的“密钥”部分 验证该用户 - 无法猜测/伪造 如果用户行为不端,可以撤销 - 通常他们键入可以删除记录的数据库。通常您将拥有数千或数百万个 API 密钥,而不是数十亿,因此它们不需要:
可靠地存储有关 API 用户的信息,因为这些信息可以存储在您的数据库中。因此,生成 API 密钥的一种方法是获取两条信息:
-
保证唯一性的序列号
足够的随机位来填充密钥
并使用私人秘密对其进行签名。
计数器保证他们唯一标识用户,并且签名防止伪造。可撤销性要求在执行任何需要 API 密钥授权的操作之前检查密钥在数据库中是否仍然有效。
如果您需要从多个数据中心生成密钥,或者没有其他好的分布式方式来分配序列号,那么一个好的 GUID 生成器是递增计数器的一个很好的近似值。
或随机字符串的散列
散列并不能防止伪造。 Signing 保证密钥来自您。
【讨论】:
如果根据提供 API 的服务器上已注册 API 密钥的数据库检查客户端提供的 API 密钥,您的算法的签名步骤是否必要?如果服务器是提供密钥的服务器,似乎在这里签名将是多余的。 @sappenin,是的。如果您在服务器上存储了一个不可猜测的密钥,那么您就不需要防止伪造。 API 请求通常由一组机器中的任何一台处理—— 服务器是众多服务器之一。可以在任何机器上进行签名检查,而无需往返数据库,这可以避免在某些情况下出现竞争条件。 @MikeSamuel 如果 API 密钥已签名并且您没有往返数据库,那么当密钥被撤销但仍用于访问 API 时会发生什么? @AbhyuditJain,在任何分布式系统中,您都需要一致的消息顺序(撤销happen-before 随后使用已撤销凭据)或其他方式来约束歧义。有些系统不会对每个请求进行往返——如果一个节点缓存了一个密钥在数据库中存在 10 分钟的事实,那么只有 10 分钟。攻击者可以在其中滥用已撤销凭据的窗口。但是可能会导致混淆:用户撤销一个凭证,然后测试它是否被撤销,并且感到惊讶,因为非粘性会话导致两个请求转到不同的节点。【参考方案3】:我使用 UUID,格式为小写,不带破折号。
生成很容易,因为大多数语言都内置了它。
API 密钥可能会被泄露,在这种情况下,用户可能希望取消其 API 密钥并生成一个新的,因此您的密钥生成方法必须能够满足这一要求。
【讨论】:
不要假设 UUID 很难猜;它们不应用作安全功能(UUID 规范 RFC4122 section 6)。 API 密钥需要一个安全的随机数,但 UUIDs are not securely unguessable. @EdwardBrey Java 中的UUID uuid = UUID.randomUUID();
怎么样?你是说随机还不够好?
@MicroR 随机 UUID 仅在用于使其具有加密安全性且 128 位足够的随机数生成器时才是安全的。尽管 UUID RFC 不需要安全的随机数生成器,但给定的实现可以免费使用。对于randomUUID,API 文档特别声明它使用“加密强伪随机数生成器”。因此,对于 128 位 API 密钥,该特定实现是安全的。【参考方案4】:
如果您想要一个只有字母数字字符的 API 密钥,您可以使用 base64-random 方法的变体,只使用 base-62 编码。 base-62 编码器基于this。
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();
【讨论】:
【参考方案5】:API 密钥应该是一些随机值。随机到无法预测。它不应包含其所针对的用户或帐户的任何详细信息。如果您确定创建的 ID 是随机的,则使用 UUID 是个好主意。
例如,早期版本的 Windows 产生可预测的 GUID,但这是一个老故事。
【讨论】:
Windows 2000 使用 random numbers 切换到 GUID。但是,不能保证随机数无法预测。例如,如果攻击者为自己创建了多个 API 密钥,则有可能确定未来用于生成另一个用户的 API 密钥的随机数。一般来说,do not consider UUIDs to be securely unguessable.以上是关于生成新 API 密钥的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
对于使用 Facebook iOS SDK 的混合 SSO 场景,为我们自己的自定义用户记录生成密码/密钥的最佳方式是啥?
从 bson 结构转换 protoc 生成的结构的最佳方法是啥?
在反应应用程序中保护 Firebase API 密钥以使其无法公开访问的最佳方法是啥? [复制]