Логирование и токены: что нельзя писать в лог

Логирование в серверных JavaScript-приложениях часто воспринимается как техническая вспомогательная функция, однако фактически это один из самых чувствительных каналов утечки данных. Любая информация, попавшая в лог, перестаёт быть «внутренней»: она может оказаться в системах мониторинга, в облачных хранилищах, у DevOps-инженеров, в сторонних сервисах аналитики и даже в резервных копиях.

В Iron-ориентированных приложениях логирование обычно встроено в слой инфраструктуры: middleware, обработчики запросов, сервисные адаптеры. Именно поэтому ошибки здесь масштабируются автоматически — один неправильный формат логирования распространяется на все входящие запросы.

Ключевой принцип: лог — это не место для хранения данных, а инструмент диагностики поведения системы.

Токены как критический класс данных

Токены в современных JavaScript-системах используются для идентификации и авторизации. В контексте Iron и похожих библиотек под токенами обычно подразумеваются:

  • access tokens (OAuth, JWT)
  • refresh tokens
  • session tokens
  • signed/encrypted cookies
  • временные одноразовые ключи
  • CSRF-токены

Каждый из этих элементов даёт прямой или косвенный доступ к пользовательской сессии. Их компрометация эквивалентна компрометации аккаунта.

Любое попадание токена в лог-файлы создаёт долговременный риск: логи хранятся дольше, чем живут токены, и часто имеют более широкую зону доступа, чем runtime-данные приложения.

Что категорически нельзя писать в логи

Авторизационные заголовки и Bearer-токены

Наиболее частая ошибка — логирование HTTP-заголовков целиком:

logger.info(req.headers);

или более опасный вариант:

logger.info(`Authorization: ${req.headers.authorization}`);

Заголовок Authorization почти всегда содержит Bearer-токен. Его утечка даёт возможность полностью эмулировать пользователя.

Правильный подход — полное исключение или маскирование:

const safeHeaders = { ...req.headers };
delete safeHeaders.authorization;

logger.info(safeHeaders);

API-ключи и секреты сервисов

API-ключи используются для доступа к внешним системам: платежным шлюзам, облачным хранилищам, сторонним API. Их утечка приводит к:

  • несанкционированным запросам от имени сервиса
  • финансовым потерям
  • исчерпанию квот

Даже частичное логирование ключа (например, первые 8 символов) создаёт риск атак по подбору или утечке через корреляцию данных.

Сессионные данные и cookies

Cookies, особенно содержащие сессии или зашифрованные токены Iron-сессий, не должны попадать в логи:

  • sessionId
  • iron-session
  • signed cookies
  • encrypted payloads

Любая сериализация request object без фильтрации приводит к утечке этих данных.

Почему даже «безопасные» токены опасно логировать

Часто разработчики считают, что токен можно логировать частично или в зашифрованном виде. Это ошибочная модель безопасности.

Причины:

  • частичный токен может быть использован в атаках угадывания
  • лог может содержать контекст, позволяющий восстановить полный токен
  • разные логи могут коррелировать значения и раскрыть структуру

Даже хэш токена в некоторых системах может быть использован для replay-анализа.

Логирование запросов: скрытые утечки

Автоматическое логирование HTTP-запросов — стандартная практика, но именно она чаще всего приводит к утечкам.

Опасные элементы request-объекта:

  • headers
  • body
  • query parameters
  • cookies
  • raw payload

Особенно критичны POST-запросы, где в теле могут содержаться:

  • пароли
  • токены
  • персональные данные
  • банковские реквизиты

Пример опасного подхода:

logger.info({
  url: req.url,
  body: req.body,
  headers: req.headers
});

Правильная стратегия — выборочное логирование:

logger.info({
  url: req.url,
  method: req.method,
  userId: req.user?.id
});

Iron и проблема сериализации данных

В системах, использующих Iron для защиты данных (например, encrypted cookies), важный риск связан с автоматической сериализацией объектов.

Типичная ошибка:

logger.info(ironData);

Если объект уже зашифрован, это не означает, что его можно безопасно логировать. Во-первых, структура может содержать метаданные, во-вторых — сам факт наличия защищённого payload может быть использован атакующим для анализа системы.

Лог должен содержать только смысловую информацию, а не техническую реализацию защиты.

Маскирование и редактирование чувствительных данных

Правильный подход к логированию — использование слоя редактирования (redaction layer).

Пример функции маскирования:

function redact(obj) {
  const sensitiveKeys = [
    'authorization',
    'password',
    'token',
    'cookie',
    'apiKey',
    'session'
  ];

  const copy = JSON.parse(JSON.stringify(obj));

  function walk(current) {
    for (const key in current) {
      if (!current.hasOwnProperty(key)) continue;

      if (sensitiveKeys.includes(key.toLowerCase())) {
        current[key] = '[REDACTED]';
      } else if (typeof current[key] === 'object' && current[key] !== null) {
        walk(current[key]);
      }
    }
  }

  walk(copy);
  return copy;
}

Такой подход снижает риск утечки даже при ошибках разработчиков.

Структурированное логирование и контроль полей

Структурированные логи (JSON) требуют строгой схемы данных. Без схемы невозможно гарантировать отсутствие чувствительных полей.

Рекомендуемая модель:

  • whitelist полей (разрешённые поля)
  • автоматическая фильтрация всех остальных
  • отдельные лог-каналы для debug и security

Пример:

logger.info({
  event: 'user_login',
  userId: user.id,
  ip: req.ip,
  success: true
});

Отсутствие «сырого» request object — обязательное условие безопасности.

Уровни логирования и утечки через debug

Debug-логи часто включают расширенную информацию, которая в production-среде становится источником утечки.

Особенно опасны:

  • stack traces с параметрами запросов
  • ошибки с дампом объекта request
  • логирование SQL-запросов с параметрами
  • trace middleware с полным контекстом запроса

Даже если данные не сохраняются постоянно, они могут попасть в:

  • системы мониторинга (Datadog, ELK, etc.)
  • crash reports
  • distributed tracing systems

Практика безопасного middleware логирования

В Iron-подобных архитектурах логирование обычно реализуется через middleware.

Базовый безопасный шаблон:

function loggingMiddleware(req, res, next) {
  const start = Date.now();

  res.on('finish', () => {
    logger.info({
      method: req.method,
      path: req.path,
      status: res.statusCode,
      duration: Date.now() - start
    });
  });

  next();
}

Отсутствуют:

  • headers
  • body
  • cookies
  • токены

Сохраняется только поведенческая метрика запроса.

Корреляция логов и риск косвенной утечки

Даже при отсутствии прямого логирования токенов возможны косвенные утечки:

  • одинаковые request-id позволяют связать события
  • последовательность действий пользователя раскрывает сессию
  • ошибки авторизации могут раскрыть структуру токенов
  • временные метки помогают восстановить сценарии атак

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

Итоговые принципы безопасного логирования

  • логировать поведение, а не данные
  • исключать любые токены и идентификаторы доступа
  • никогда не сохранять заголовки целиком
  • использовать whitelist вместо blacklist
  • применять централизованное маскирование
  • разделять debug и production логи
  • учитывать, что лог — долговременное хранилище

В Iron-архитектурах корректная работа с логами и токенами является не дополнительной оптимизацией, а обязательным элементом безопасности системы.