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

Почему bcrypt.js требует особого отношения к логированию

bcrypt.js используется для хеширования паролей и проверки их соответствия. Несмотря на то, что результат хеширования выглядит как случайная строка, он остаётся чувствительными данными. Хеш пароля:

  • может быть использован для атак перебора (brute force / rainbow tables при слабых настройках)
  • содержит соль и параметры алгоритма
  • является постоянным идентификатором исходного пароля

Формат результата bcrypt включает информацию о стоимости вычислений (cost factor), соли и самом хеше:

$2a$10$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36uXq0v1YcQ2eZ8zW3XkG6K

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


Какие данные нельзя логировать при работе с bcrypt.js

Пароли в любом виде

Наиболее критическая ошибка — логирование исходного пароля:

console.log(password);

или через отладочные middleware:

console.log(req.body);

Если объект содержит пароль, он попадает в логи полностью.


Хеши паролей

Логирование результата bcrypt.hash() создаёт риск:

const hash = await bcrypt.hash(password, 10);
console.log(hash);

Хотя это не исходный пароль, сам хеш:

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

Результаты сравнения bcrypt.compare

Ошибка распространённого характера — логирование результатов сравнения:

const match = await bcrypt.compare(password, hash);
console.log(match);

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


Основные источники утечек в приложениях

HTTP-запросы и middleware

В Node.js приложениях наиболее частый источник утечек — промежуточные обработчики:

app.use((req, res, next) => {
  console.log(req.body);
  next();
});

Если req.body содержит пароль, он автоматически попадает в лог-файлы.

Особенно опасно использование таких middleware в production без фильтрации полей.


Логи ORM и запросов к базе данных

ORM-библиотеки могут логировать SQL-запросы:

INS ERT INTO users (email, password_hash) VALUES (...)

Если в логах присутствует password_hash, это создаёт риск компрометации даже при отсутствии исходного пароля.

Некоторые ORM по умолчанию включают debug-режим, который выводит все параметры запросов.


Клиентская сторона (bcrypt.js в браузере)

Так как bcrypt.js может выполняться в браузере, появляется дополнительный класс ошибок:

  • логирование в console.log в dev-инструментах
  • сохранение промежуточных значений в localStorage
  • отправка отладочных данных на сервер аналитики
localStorage.setItem("debug_hash", hash);

Такая практика приводит к сохранению хешей вне защищённого контекста.


Риски утечки через системы логирования

Централизованные лог-системы

Системы вроде ELK Stack, Loki, Datadog или Splunk агрегируют логи со всей инфраструктуры. Если пароль или хеш попал в лог:

  • он становится доступен множеству сервисов
  • увеличивается поверхность атаки
  • усложняется удаление данных (лог-репликация)

Ошибки и stack trace

Stack trace часто содержит контекст входных данных:

Error: Invalid password
    at authService.login (/app/auth.js:42)
    at req.body.password

При неправильной обработке ошибок возможно автоматическое включение чувствительных данных в отчёты об ошибках (например, Sentry).


Маскирование и фильтрация данных

Редакция чувствительных полей

Перед логированием необходимо исключать поля:

function sanitize(body) {
  const { password, passwordHash, ...safe } = body;
  return safe;
}

console.log(sanitize(req.body));

Маскирование значений

Иногда требуется сохранить структуру объекта, но скрыть содержимое:

function mask(val ue) {
  return value ? "***" : value;
}

Пример логирования:

console.log({
  email: req.body.email,
  password: mask(req.body.password)
});

Использование логгеров с поддержкой redaction

Современные логгеры поддерживают автоматическое скрытие полей:

  • pino
  • winston с форматтерами
  • bunyan

Пример конфигурации (pino):

const logger = require("pino")({
  redact: ["req.body.password", "req.headers.authorization"]
});

Утечки через ошибки конфигурации bcrypt.js

Слишком высокая детализация логов

При разработке часто включается debug-режим:

  • вывод всех операций хеширования
  • логирование salt rounds
  • вывод промежуточных значений

Это не должно попадать в production-среду.


Неправильная обработка исключений

Ошибка:

try {
  const hash = await bcrypt.hash(password, 10);
} catch (err) {
  console.log(err);
}

Некоторые ошибки могут содержать контекст входных данных или внутренние состояния библиотеки.


Практики безопасного логирования при использовании bcrypt.js

Логирование только событий, а не данных

Корректный подход:

logger.info("User password hash created");

Некорректный:

logger.info(hash);

Разделение уровней логирования

  • error — только системные ошибки без пользовательских данных
  • warn — события безопасности без payload
  • info — бизнес-события без чувствительных полей
  • debug — запрещён в production

Изоляция контекста аутентификации

Логирование должно исключать любые данные:

  • пароль
  • хеш
  • salt
  • результаты сравнения

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

logger.info({
  event: "login_attempt",
  userId: user.id,
  status: "success"
});

Типичные ошибки разработчиков

Логирование для отладки в production

console.log("AUTH DEBUG:", req.body);

Перенос кода разработки в продакшен

Часто забываются:

  • временные console.log
  • debug middleware
  • тестовые endpoint’ы

Использование внешних сервисов без фильтрации

Аналитические SDK могут автоматически собирать:

  • body запросов
  • URL параметры
  • заголовки

Без явной конфигурации это приводит к утечке паролей.


Влияние bcrypt.js на стратегию логирования

Использование bcrypt.js требует пересмотра логики наблюдаемости системы:

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

Сам факт наличия bcrypt-хеша в логах уже рассматривается как инцидент безопасности, независимо от контекста.