awesome-everything EN
↑ Обратно к восхождению

Безопасность

JWT pitfalls: чтение кода и токенов

Суть Читай реальные сниппеты верификации JWT и поддельный токен, находи уязвимость и выбирай фикс наибольшего рычага, который security-минд senior делает первым.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

Баги JWT прячутся в вызове verify и проверках claim после него. Читай каждый сниппет так, как делал бы это в security-ревью, найди, что эксплуатирует атакующий, и выбери фикс, прежде чем дописать ещё строку.

Цель

Отработай цикл ревью, который запускаешь на каждом auth-пути: читай вызов verify, спроси, что контролирует атакующий, и тянись к фиксу «пинь-и-валидируй», закрывающему брешь доверия.

Сниппет 1 — поддельный токен alg:none

header:  { "alg": "none", "typ": "JWT" }
payload: { "sub": "victim", "role": "admin" }
signature: (empty)

// код сервера
const claims = jwt.decode(token);   // только decode, без verify
if (claims.role === "admin") grantAdmin();
Викторина

Что этот код даёт атакующему и какой фикс?

Сниппет 2 — проверка публичным ключом

const publicKey = fs.readFileSync("rsa-public.pem");
function verifyToken(token) {
  // опция algorithms не передана
  return jwt.verify(token, publicKey);
}
Викторина

Атакующий подделывает токен с alg:HS256, подписанный содержимым rsa-public.pem как HMAC-секретом. Почему verifyToken его принимает?

Сниппет 3 — signature проверена, claim нет

const claims = jwt.verify(token, key, { algorithms: ["RS256"] });
// signature OK — теперь доверяем всему
req.user = claims.sub;
req.roles = claims.roles;
// нет проверок exp / iss / aud сверх дефолта библиотеки
Викторина

Алгоритм запинен и signature проверена. В микросервисной сети, делящей один ключ подписи, что всё ещё не так?

Сниппет 4 — резолв ключа из заголовка

function getKey(header) {
  // header.kid контролируется атакующим
  return fetch(header.jku)
    .then(r => r.json())
    .then(jwks => jwks.keys.find(k => k.kid === header.kid));
}
const claims = jwt.verify(token, await getKey(jwt.decodeHeader(token)));
Викторина

Как атакующий полностью контролирует верификацию здесь и какой фикс?

Итог

Каждый дефект JWT читается в вызове verify и проверках вокруг него: decode-only код доверяет неподписанным claim; отсутствующий allowlist algorithms позволяет заголовку выбрать HS256 и превращает публичный ключ в HMAC-секрет; проверенная signature без проверки aud/iss — это ждущий своего часа confused-deputy в сети с общим ключом; а резолв kid/jku из контролируемых атакующим заголовков отдаёт выбор ключа атакующему. Фикс всегда одной формы — пинь алгоритм, привязывай и allowlist-и ключ, затем валидируй зарегистрированные claim против того, что ожидает этот сервис.

Продолжить восхождение ↑JWT pitfalls: сломай и закали auth-флоу
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.