kynx/api-key-generator

生成和解析有效的API密钥

2.0.0 2023-07-23 10:42 UTC

This package is auto-updated.

Last update: 2024-09-23 13:26:13 UTC


README

Build Status

生成和解析有效的API密钥。

简介

API密钥是验证用户访问API的一种常见方式。虽然没有标准说明它们应该如何构建,但有一些最佳实践。

API密钥应该

  • 是安全的(好吧,这有点明显...)
  • 不会泄露与其关联的用户身份的任何信息
  • 易于安全存储和验证持久层
  • 可追踪,以防某个笨蛋将其检查到VCS中 - 例如,使用GitHub的Secret Scanning
  • 提供一种方法,可以快速拒绝明显格式错误的密钥,而无需击中持久层
  • 包含可读信息,以便最终用户/支持人员可以轻松地查看他们是否正在使用正确的密钥
  • 易于从UI复制粘贴到消费应用程序

这个库有助于实现这一点,提供了一个用于生成和解析密钥的ApiKeyGenerator,以及一个用于处理密钥本身的ApiKey对象。它不提供存储和验证密钥的帮助 - 这部分由你自己负责。

安装

composer require kynx/api-key-generator

用法

生成密钥

use Kynx\ApiKey\KeyGenerator;

require 'vendor/autoload.php';

$generator = new KeyGenerator('xyz_sandbox');
$apiKey    = $generator->generate();
echo $apiKey->getKey() . "\n";

这将输出类似以下内容

xyz_sandbox_miWh6l3ftyzi9TRmpZeJ4nU3LpBF5T37FguT1p4y_dab13e9d

请参阅 示例 目录,其中包含如何修改密钥强度、更改使用的字符等代码。

解析密钥

use Kynx\ApiKey\KeyGenerator;

require 'vendor/autoload.php';

$generator = new KeyGenerator('xyz_sandbox');
$apiKey    = $generator->parse('xyz_sandbox_miWh6l3ftyzi9TRmpZeJ4nU3LpBF5T37FguT1p4y_dab13e9d');
if ($apiKey === null) {
    echo "Invalid key!\n";
} else {
    echo "Identifier : " . $apiKey->getIdentifier() . "\n";
    echo "Secret     : " . $apiKey->getSecret() . "\n";
}

由于密钥是格式良好的,这将输出

Identifier : miWh6l3f
Secret     : tyzi9TRmpZeJ4nU3LpBF5T37FguT1p4y

现在对密钥进行随机更改。parse()方法将返回null,您将看到“无效密钥!”消息。

注意:密钥格式良好并不意味着用户可以进行认证。它只是意味着它看起来是由您生成的。您必须仍然验证密钥与您的持久存储。

处理旧密钥

如果您更改密钥的前缀,或者想要增加密钥的密钥强度(见下文),会发生什么?您将希望使用更新的设置颁发所有新的密钥,但仍需要解析旧密钥。

这就是KeyGeneratorChain的作用所在

use Kynx\ApiKey\KeyGenerator;
use Kynx\ApiKey\KeyGeneratorChain;

require '../vendor/autoload.php';

$newPrefix = 'abc_sandbox';
$oldPrefix = 'xyz_sandbox';

$primary  = new KeyGenerator($newPrefix);
$fallback = new KeyGenerator($oldPrefix);
$chain    = new KeyGeneratorChain($primary, $fallback);

$oldKey = $chain->parse('xyz_sandbox_miWh6l3ftyzi9TRmpZeJ4nU3LpBF5T37FguT1p4y_dab13e9d');
if ($oldKey !== null) {
    echo "Old key parsed\n";
}

$newKey = $chain->parse('abc_sandbox_miWh6l3ftyzi9TRmpZeJ4nU3LpBF5T37FguT1p4y_dab13e9d');
if ($newKey !== null) {
    echo "New key parsed\n";
}

新密钥始终由$primary KeyGenerator生成。在解析时,首先尝试$primary。如果返回null,将依次尝试每个$fallback KeyGenerator,直到返回第一个成功的解析结果,或者如果没有任何匹配项,则返回null。请参阅parse-chain以获取更完整的示例。

API密钥结构

您会注意到在上述示例中,生成的密钥由三个部分组成,由下划线分隔。它们是

<prefix>_<identifier><secret>_<checksum>

前缀

前缀始终是传递给KeyGenerator构造函数的第一个参数。它的目的是使密钥易于识别,既便于最终用户,也便于在野外找到泄露的密钥的秘密扫描器。在我们的示例中,我们使用了一个公司标识符(xyz)以及一个表示它是用于我们的productionsandbox环境的字符串。如果您曾经与Stripe集成过,您将熟悉这种模式,但您可以使用任何有意义的格式。

标识符

标识符是一个随机字符串,可以用来在数据库中查找密钥。请将其以区分大小写的形式存储在未哈希的列中(例如,MySQL中的VARBINARY),并对其施加唯一约束。生成的标识符不一定是唯一的,因此在将数据插入数据库时,您应该准备好捕获约束违规,生成新的密钥并重新尝试。有关示例,请参阅存储和认证

密钥

密钥部分提供了安全性。在存储之前,必须对它进行哈希处理(使用PHP的password_hash())。如果您不确定如何进行此操作,请参阅存储和认证示例代码。

校验和

最后,校验和是其余密钥的crc32b哈希值。它的目的是快速过滤掉攻击你的API的垃圾数据,而无需麻烦你的持久化层。仅仅因为匹配并不意味着用户可以认证!您仍然需要检查标识符和密钥是否与生成密钥时存储的内容相匹配。

默认值

默认情况下,标识符长度为8个字符。使用默认字符,这给出了pow(59, 8) - 1种组合。即使存储了数百万个密钥,碰撞的风险也非常小。鉴于API密钥仅适用于组织级别的访问控制,这应该足够了。但如果需要,您可以将其增加。

默认情况下,密钥长度为32个字符。您可以增加此值来提高API的安全性。请参阅生成安全密钥示例。

默认情况下,生成的密钥部分由字符[a-zA-Z0-9_]组成。您应该确保您的前缀也是如此:这使得从密钥管理控制台复制它变得很容易。尝试双击xyz-sandbox_Pu!Lo&jP_N22/Oh5hz48h4.QM_e07f9ca3以查看其他字符存在时会发生什么。如果您想要更多的熵,请使密钥更长。

升级

1.x -> 2.x

在1.x中,默认密钥长度为16个字符。尽管我讨厌BC不兼容,但这对于提倡最佳实践的库来说太低了。在2.x中,默认值为32,最小值为24。我认为最好早点纠正这个错误。

为了在不破坏现有密钥的情况下升级,您需要使用KeyGeneratorChain以及用于解析旧密钥的BC生成器。

use Kynx\ApiKey\BcKeyGenerator;
use Kynx\ApiKey\KeyGenerator;
use Kynx\ApiKey\KeyGeneratorChain;

require 'vendor/autoload.php';

$primary  = new KeyGenerator('xyz_sandbox');
$fallback = new BcKeyGenerator('xyz_sandbox');
$chain    = new KeyGeneratorChain($primary, $fallback);

$newKey = $chain->generate(); // get a new 2.x key
$oldKey = $chain->parse('xyz_sandbox_PudLoQjP_N227Oh5hz48h4FQM_e07f9ca3'); // 1.x key still parsed

BcKeyGenerator不能用于生成新密钥,只能用于解析旧密钥。它和相关联的BcApiKey已被弃用,将在3.x版本中删除。

进一步阅读