跳至内容

HMAC SHA256 令牌认证器

HMAC-SHA256 认证器支持使用可撤销的 API 密钥,而无需使用 OAuth。这提供了一种替代方案,即令牌在每次请求中传递,而使用共享密钥以安全方式对请求进行签名。与授权令牌一样,这些令牌通常用于向第三方开发者提供对您的 API 的访问权限。这些密钥通常具有非常长的过期时间,通常为数年。

这些密钥也适用于移动应用程序。在这种情况下,用户将使用其电子邮件/密码进行注册/登录。应用程序将为他们创建一个新的访问令牌,并使用一个可识别的名称,例如“John's iPhone 12”,并将其返回到移动应用程序,在该应用程序中存储并用于所有未来的请求。

注意

出于本文档的目的,并且为了与授权令牌保持一致性,术语“令牌”将用于表示一组 API 密钥(密钥和 secretKey)。

用法

为了使用 HMAC 密钥/令牌,Authorization 标头将在请求中设置为以下内容

Authorization: HMAC-SHA256 <key>:<HMAC-HASH-of-request-body>

执行此操作的代码看起来像这样

header("Authorization: HMAC-SHA256 {$key}:" . hash_hmac('sha256', $requestBody, $secretKey));

使用 CodeIgniter CURLRequest 类

<?php

$client = \Config\Services::curlrequest();

$key = 'a6c460151b4cabbe1c1d73e08915ce8e';
$secretKey = '56c85232f0e5b55c05015476cd132c8d';
$requestBody = '{"name":"John","email":"[email protected]"}';

// $hashValue = b22b0ec11ad61cd4488ab1a09c8a0317e896c22adcc5754ea4cfd0f903a0f8c2
$hashValue = hash_hmac('sha256', $requestBody, $secretKey);

$response = $client->setHeader('Authorization', "HMAC-SHA256 {$key}:{$hashValue}")
    ->setBody($requestBody)
    ->request('POST', 'https://example.com/api');

HMAC 密钥/API 认证

使用 HMAC 密钥要求您使用/扩展 CodeIgniter\Shield\Models\UserModel 或在您自己的用户模型上使用 CodeIgniter\Shield\Authentication\Traits\HasHmacTokens。此特性提供了在您的应用程序中实现 HMAC 密钥所需的所有自定义方法。必要的数据库表 auth_identities 在 Shield 的唯一迁移类中创建,在首次使用 Shield 的任何特性之前必须运行此类。

生成 HMAC 访问密钥

访问密钥/令牌通过用户上的 generateHmacToken() 方法创建。这将名称作为第一个参数提供给令牌。该名称用于向用户显示,以便他们可以在多个令牌之间进行区分。

$token = $user->generateHmacToken('Work Laptop');

这使用加密安全的随机字符串创建密钥/令牌。这些密钥作为共享密钥操作。“密钥”以纯文本形式存储在数据库中,“密钥”以加密形式存储。该方法返回 CodeIgniters\Shield\Authentication\Entities\AccessToken 的实例。字段 secret 是“密钥”,字段 rawSecretKey 是共享的“密钥”。使用此身份验证方法时需要两者。

这些密钥的纯文本版本应立即显示给用户,以便他们可以复制它以供使用。建议之后仅向用户显示“密钥”字段。如果用户丢失了“密钥”,则应要求他们生成一组新的密钥以供使用。

$token = $user->generateHmacToken('Work Laptop');

echo 'Key: ' . $token->secret;
echo 'SecretKey: ' . $token->rawSecretKey;

撤销 HMAC 密钥

可以通过 revokeHmacToken() 方法撤销 HMAC 密钥。这将密钥作为唯一参数。撤销只是从数据库中删除记录。

$user->revokeHmacToken($key);

您可以使用 revokeAllHmacTokens() 方法撤销所有 HMAC 密钥。

$user->revokeAllHmacTokens();

检索 HMAC 密钥

以下方法可帮助您检索用户的 HMAC 密钥

// Retrieve a set of HMAC Token/Keys by key
$token = $user->getHmacToken($key);

// Retrieve an HMAC token/keys by its database ID
$token = $user->getHmacTokenById($id);

// Retrieve all HMAC tokens as an array of AccessToken instances.
$tokens = $user->hmacTokens();

HMAC 密钥范围

每个令牌(密钥集)都可以被赋予一个或多个可以在其中使用的范围。可以将这些视为令牌授予用户的权限。在生成令牌时提供范围,并且之后不能修改。

$token = $user->gererateHmacToken('Work Laptop', ['posts.manage', 'forums.manage']);

默认情况下,会向用户授予通配符范围,该范围提供对所有范围的访问权限。这与

$token = $user->gererateHmacToken('Work Laptop', ['*']);

在身份验证期间,用户使用的 HMAC 密钥存储在用户上。经过身份验证后,您可以对用户使用 hmacTokenCan()hmacTokenCant() 方法来确定他们是否有权访问指定的范围。

if ($user->hmacTokenCan('posts.manage')) {
    // do something....
}

if ($user->hmacTokenCant('forums.manage')) {
    // do something....
}

HMAC 密钥加密

HMAC 密钥以加密形式存储。在开始使用 HMAC 之前,您需要在 app/Config/AuthToken.php 中的 $hmacEncryptionKeys 中设置/覆盖加密密钥。这应使用 .env 和/或系统环境变量进行设置。有关如何执行此操作的说明,请参阅 CodeIgniter 4 文档的 设置加密密钥 部分。

您还将能够调整默认驱动程序 $hmacEncryptionDefaultDriver 和默认摘要 $hmacEncryptionDefaultDigest,它们分别默认为 'OpenSSL''SHA512'。这些也可以通过将它们包含在密钥数组中而针对单个密钥进行覆盖。

public $hmacEncryptionKeys = [
    'k1' => [
        'key' => 'hex2bin:923dfab5ddca0c7784c2c388a848a704f5e048736c1a852c862959da62ade8c7',
    ],
];

public string $hmacEncryptionCurrentKey    = 'k1';
public string $hmacEncryptionDefaultDriver = 'OpenSSL';
public string $hmacEncryptionDefaultDigest = 'SHA512';

当需要更新加密密钥时,您需要向上述 $hmacEncryptionKeys 数组添加一个附加密钥。然后调整 $hmacEncryptionCurrentKey 以指向新密钥。在新加密密钥就位后,运行 php spark shield:hmac reencrypt 以使用新的加密密钥重新加密所有现有密钥。您需要将旧密钥保留在数组中,因为它将在重新加密期间用于读取现有的“密钥”。

public $hmacEncryptionKeys = [
    'k1' => [
        'key' => 'hex2bin:923dfab5ddca0c7784c2c388a848a704f5e048736c1a852c862959da62ade8c7',
    ],
    'k2' => [
        'key'    => 'hex2bin:451df599363b19be1434605fff8556a0bbfc50bede1bb33793dcde4d97fce4b0',
        'digest' => 'SHA256',
    ],
];

public string $hmacEncryptionCurrentKey    = 'k2';
public string $hmacEncryptionDefaultDriver = 'OpenSSL';
public string $hmacEncryptionDefaultDigest = 'SHA512';
php spark shield:hmac reencrypt

你可以(并且应该)使用环境变量和/或.env文件设置这些值。为此,你需要将这些值设置为 JSON 字符串

authtoken.hmacEncryptionKeys = '{"k1":{"key":"hex2bin:923dfab5ddca0c7784c2c388a848a704f5e048736c1a852c862959da62ade8c7"},"k2":{"key":"hex2bin:451df599363b19be1434605fff8556a0bbfc50bede1bb33793dcde4d97fce4b0"}}'
authtoken.hmacEncryptionCurrentKey = k2

根据密钥长度和所用加密类型的设置,加密值可能会超过数据库列字符限制 255 个字符。如果发生这种情况,创建新的 HMAC 身份将抛出 RuntimeException

配置

根据你的需要配置 app/Config/AuthToken.php

注意

Shield 不会期望你同时使用访问令牌验证器和 HMAC 验证器。因此,一些配置项是通用的。

HMAC 密钥生命周期

自 HMAC 密钥/令牌使用以来,经过指定时间后它们将过期。

默认情况下,这设置为 1 年。你可以通过设置 $unusedTokenLifetime 值来更改此值。这是以秒为单位,以便你可以使用 CodeIgniter 提供的 时间常量

public $unusedTokenLifetime = YEAR;

登录尝试日志记录

默认情况下,只有失败的登录尝试才会记录在 auth_token_logins 表中。可以通过更改 $recordLoginAttempt 值来修改此设置。

public int $recordLoginAttempt = Auth::RECORD_LOGIN_ATTEMPT_FAILURE;

如果你不希望有任何日志,请将其设置为 Auth::RECORD_LOGIN_ATTEMPT_NONE

如果你想记录所有登录尝试,请将其设置为 Auth::RECORD_LOGIN_ATTEMPT_ALL。这意味着你记录所有请求。

日志记录

根据上述配置,登录尝试会记录在 auth_token_logins 表中。

当记录失败的登录尝试时,发送的原始令牌值将保存在 identifier 列中。

当记录成功的登录尝试时,令牌名称将保存在 identifier 列中。