В спецификации OpenID Connect (OIDC), построенной поверх OAuth 2.0 и
использующей JOSE (JSON Object Signing and Encryption), особое внимание
уделяется проверке целостности токенов через дополнительные хэши,
встроенные в ID Token. Среди них ключевую роль играют
at_hash и c_hash.
Эти значения позволяют клиентскому приложению убедиться, что
полученные access_token и code действительно
соответствуют ID Token, подписанному сервером авторизации, и не были
подменены в процессе передачи.
JOSE — это набор стандартов, включающий:
В OpenID Connect именно JWS используется для подписания ID Token, который представляет собой JWT (JSON Web Token). Внутри JWT могут присутствовать специальные claims, среди которых:
at_hash — хэш access tokenc_hash — хэш authorization codeat_hashat_hash предназначен для проверки соответствия
access_token, выданного вместе с ID Token.
access_tokenФормально:
at_hash = BASE64URL( leftmost_half( HASH(access_token) ) )
После получения ответа от Authorization Server:
access_tokenat_hash из ID Tokenaccess_tokenЕсли значения не совпадают — токен считается недействительным или подменённым.
at_hash в Node.js с использованием JoseБиблиотека jose предоставляет удобные инструменты для
работы с JWT и криптографией.
import { createHash } from 'crypto';
import { jwtVerify } from 'jose';
function base64url(input) {
return input
.toString('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
}
function computeAtHash(accessToken) {
const hash = createHash('sha256').update(accessToken).digest();
const leftHalf = hash.subarray(0, hash.length / 2);
return base64url(leftHalf);
}
// Пример сравнения
const accessToken = 'eyJ...';
const expectedAtHash = 'abc123...'; // из ID Token
const calculated = computeAtHash(accessToken);
if (calculated !== expectedAtHash) {
throw new Error('at_hash verification failed');
}
c_hashc_hash применяется аналогично, но для authorization
code, который используется в Authorization Code Flow.
c_hashПроцесс идентичен at_hash:
c_hash = BASE64URL( leftmost_half( HASH(authorization_code) ) )
Его наличие особенно важно в сценариях, где ID Token возвращается вместе с authorization code (например, hybrid flow).
at_hashc_hash обычно отсутствует, если code уже обменян на
сервереcode, id_token,
иногда access_tokenc_hash и/или
at_hashat_hash обязателен для проверки
access_tokenalgРазмер хэша зависит от алгоритма подписи JWT:
| alg | hash | размер усечения |
|---|---|---|
| HS256 / RS256 | SHA-256 | 128 бит |
| HS384 / RS384 | SHA-384 | 192 бит |
| HS512 / RS512 | SHA-512 | 256 бит |
Критически важно использовать правильный алгоритм, указанный в
заголовке JWT (alg), иначе вычисленный at_hash
будет некорректным.
Одна из самых частых ошибок — использование стандартного base64 вместо base64url. Отличия:
+ → -/ → _=Некоторые реализации ошибочно сравнивают полный SHA-хэш, хотя требуется только левая половина.
at_hash нельзя вычислять без учёта alg.
Например, SHA-256 и SHA-512 дадут несовместимые результаты.
В Node.js важно использовать бинарные данные (Buffer), а
не строковое представление хэша:
hash.subarray(0, hash.length / 2)
joseВ современных реализациях рекомендуется использовать встроенные
механизмы jose, которые автоматически обрабатывают проверку
хэшей.
import { jwtVerify, createRemoteJWKSet } from 'jose';
const JWKS = createRemoteJWKSet(new URL('https://issuer.example.com/.well-known/jwks.json'));
const { payload } = await jwtVerify(idToken, JWKS, {
audience: 'client_id',
issuer: 'https://issuer.example.com'
});
// payload.at_hash и payload.c_hash доступны для дополнительной проверки
При корректной настройке многие проверки выполняются автоматически, однако в высоконагруженных или security-critical системах часто добавляют ручную валидацию.
at_hash и c_hashЭти значения не являются средствами шифрования или защиты содержимого токена. Их задача — контроль целостности и связности:
access_token соответствует
id_tokenОтсутствие проверки at_hash и c_hash не
делает систему сразу уязвимой, но:
at_hash и c_hash всегда проверяются
после валидации подписи JWT. Порядок важен:
at_hash / c_hashНарушение порядка приводит к ложным ощущениям безопасности.
JWT signature valid
↓
claims validated (iss, aud, exp)
↓
compute at_hash / c_hash
↓
compare with ID Token
↓
token accepted or rejected