В криптографических системах на базе библиотеки jose ошибки возникают не только из-за программных дефектов, но и как часть ожидаемой модели угроз: неверная подпись, истёкший токен, несовпадение алгоритма, повреждённый JWT или попытка использования ключа не того типа.
Ключевой парадокс заключается в том, что такие ошибки одновременно:
Любая ошибка уровня криптографии потенциально содержит сведения о структуре токена, используемых ключах, алгоритмах или даже инфраструктуре авторизации. Поэтому логирование должно строиться как фильтр, а не как дамп состояния.
Библиотека jose строго разделяет типы криптографических операций:
Каждый из этих уровней генерирует ошибки, которые могут содержать чувствительные данные в необработанном виде.
Типичные классы ошибок:
JOSEErrorJWSInvalidJWSSignatureVerificationFailedJWEInvalidJWTClaimValidationFailedВнутри сообщения ошибки может оказаться:
Прямое логирование этих данных без фильтрации создаёт риск компрометации системы.
Логирование криптографических ошибок строится по принципу минимальной достаточности: фиксируется только то, что необходимо для диагностики, но не то, что может быть использовано для атаки.
Основные принципы:
1. Исключение токенов из логов
JWT, JWS и JWE не должны попадать в лог даже частично. Любая их часть может содержать полезную информацию для злоумышленника.
2. Запрет на логирование ключей
Ни приватные, ни публичные ключи, ни их производные (fingerprint в сыром виде) не должны записываться в журнал.
3. Стабильная форма ошибок
Логи должны быть нормализованы, чтобы исключить утечки через текстовые поля ошибок библиотеки.
4. Контролируемая детализация
Разделение уровней логирования:
Наиболее распространённые ошибки при работе с jose связаны с избыточной детализацией:
catch (err) {
console.error(err);
}
Такая практика может вывести внутренние поля библиотеки, включая цепочки валидации токена.
console.log(token);
или даже:
console.log(token.split('.')[1]);
Base64-декодированная часть payload может содержать персональные данные или claims, которые нельзя раскрывать.
console.log(privateKey);
Даже в сериализованной форме это критическая утечка.
Корректная стратегия заключается в нормализации ошибки до безопасного формата до записи в лог.
import { JOSEError } from 'jose';
function sanitizeJoseError(err) {
if (err instanceof JOSEError) {
return {
type: 'JOSEError',
code: err.code || 'unknown_jose_error'
};
}
return {
type: 'UnknownError',
code: 'unexpected_error'
};
}
В этом подходе:
Для безопасного логирования важно отделять технический контекст от криптографического.
При работе с JWT в jose часто используется валидация:
Ошибка на этом уровне должна быть приведена к унифицированному виду:
try {
await jwtVerify(token, key, {
issuer: 'auth-service',
audience: 'api'
});
} catch (err) {
logger.error({
event: 'jwt_verification_failed',
reason: mapJoseError(err)
});
}
Функция mapJoseError должна исключать любые внутренние
детали библиотеки.
Опасный сценарий возникает, когда библиотека выбрасывает исключение, которое автоматически сериализуется логгером.
Рекомендуемая стратегия:
Это предотвращает случайное раскрытие структуры ошибки.
Если бизнес-логика требует логировать часть контекста, применяется маскирование:
Пример:
function maskToken(token) {
return `token:${crypto.createHash('sha256').update(token).digest('hex').slice(0, 8)}`;
}
Даже при этом оригинальный токен не должен сохраняться в памяти логов.
В продакшене библиотека jose должна работать в режиме:
Любая утечка через лог-файлы рассматривается как уязвимость уровня информации о внутреннем устройстве системы.
Архитектурно правильный подход предполагает единый слой обработки криптографических ошибок:
Это исключает дублирование логики и снижает риск случайного логирования чувствительных данных.
Логгер не должен знать о содержимом криптографии. Его задача — фиксировать факт события, а не его содержимое.
Пример правильного разделения:
При использовании jose в высоконагруженных системах необходимо регулярно проводить аудит логов:
Любое совпадение считается потенциальной утечкой и требует переработки слоя логирования.