Пакет · jwt

HMAC-токены

HMAC — это самый простой способ использования JWT: одна секретная строка подписывает и проверяет. Обращайтесь к нему, когда один и тот же сервис и выпускает, и проверяет токены. Впервые здесь? Начните с «Быстрого старта».

Подпись с помощью секрета

encode() принимает JwtPayload и PrivateKey. Для HMAC материалом ключа служит просто ваша секретная строка:

php
use Flytachi\Jwt\JWT;
use Flytachi\Jwt\Entity\JwtPayload;
use Flytachi\Jwt\Entity\PrivateKey;

$token = JWT::encode(
  new JwtPayload([
      'sub' => 'user-42',
      'iat' => time(),
      'exp' => time() + 3600,
  ]),
  new PrivateKey('my-shared-secret', 'HS256')
);

Проверка тем же секретом

decode() принимает токен и список PublicKey. Для HMAC вы передаёте ровно один — верификатор использует первый ключ в массиве и игнорирует любой kid:

php
use Flytachi\Jwt\JWT;
use Flytachi\Jwt\Entity\PublicKey;

$payload = JWT::decode($token, [new PublicKey('my-shared-secret', 'HS256')]);
echo $payload->getClaim('sub'); // user-42

Только один ключ для HMAC

decode() выбирает первый PublicKey в списке для любого токена HS* — он никогда не смотрит на kid. Передавайте только один секрет. Выбор из нескольких ключей — это функция только для асимметричных ключей; см. «Проверка через JWKS».

Выбор стойкости хеша

Три HMAC-алгоритма различаются только базовым SHA-хешем. Подписывающая и проверяющая стороны должны согласовать его — алгоритм привязан к ключу с обеих сторон.

Алгоритм Хеш Размер подписи
HS256 SHA-256 32 байта
HS384 SHA-384 48 байт
HS512 SHA-512 64 байта
php
// Более стойкий хеш — всё остальное идентично
$token = JWT::encode(
  new JwtPayload(['sub' => 'user-42']),
  new PrivateKey('my-shared-secret', 'HS512')
);

$payload = JWT::decode($token, [new PublicKey('my-shared-secret', 'HS512')]);

HS256 — это стандартный выбор, и он вполне надёжен, когда ваш секрет длинный и случайный. HS384/HS512 добавляют запас при незначительных затратах CPU.

Генерация надёжного секрета

HMAC-секрет должен быть высокоэнтропийным — относитесь к нему как к паролю, а не как к парольной фразе:

bash
openssl rand -base64 32

Загружайте его из окружения, никогда из исходного кода:

php
$secret = getenv('JWT_SECRET')
  ?: throw new RuntimeException('JWT_SECRET is not set');

$token = JWT::encode(
  new JwtPayload(['sub' => 'user-42', 'exp' => time() + 3600]),
  new PrivateKey($secret, 'HS256')
);

Обработка сценариев отказа

Каждый отказ — неверный секрет, подделанный токен, истёкший срок — приходит как одно JWTException:

php
use Flytachi\Jwt\JWTException;

try {
  $payload = JWT::decode($token, [new PublicKey($secret, 'HS256')]);
} catch (JWTException $e) {
  // "Signature verification failed." / "Token has expired (exp)." / ...
  http_response_code(401);
  exit($e->getMessage());
}

Один секрет, две возможности

Поскольку HMAC симметричен, любой, у кого есть секрет, может выпускать токены, а не только проверять их. Если вам нужно, чтобы третьи стороны могли проверять, но не могли подделывать, используйте вместо этого асимметричные ключи.

Связанные материалы