bcrypt.js используется для хеширования паролей и
проверки их соответствия. Несмотря на то, что результат хеширования
выглядит как случайная строка, он остаётся чувствительными данными. Хеш
пароля:
Формат результата bcrypt включает информацию о стоимости
вычислений (cost factor), соли и самом хеше:
$2a$10$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36uXq0v1YcQ2eZ8zW3XkG6K
Любая утечка этой строки уже считается утечкой чувствительного материала, даже если пароль неизвестен напрямую.
Наиболее критическая ошибка — логирование исходного пароля:
console.log(password);
или через отладочные middleware:
console.log(req.body);
Если объект содержит пароль, он попадает в логи полностью.
Логирование результата bcrypt.hash() создаёт риск:
const hash = await bcrypt.hash(password, 10);
console.log(hash);
Хотя это не исходный пароль, сам хеш:
bcrypt.compareОшибка распространённого характера — логирование результатов сравнения:
const match = await bcrypt.compare(password, hash);
console.log(match);
Хотя булево значение не раскрывает пароль, оно может использоваться в атакующих сценариях при массовом анализе логов и поведения системы.
В Node.js приложениях наиболее частый источник утечек — промежуточные обработчики:
app.use((req, res, next) => {
console.log(req.body);
next();
});
Если req.body содержит пароль, он автоматически попадает
в лог-файлы.
Особенно опасно использование таких middleware в production без фильтрации полей.
ORM-библиотеки могут логировать SQL-запросы:
INS ERT INTO users (email, password_hash) VALUES (...)
Если в логах присутствует password_hash, это создаёт
риск компрометации даже при отсутствии исходного пароля.
Некоторые ORM по умолчанию включают debug-режим, который выводит все параметры запросов.
Так как bcrypt.js может выполняться в браузере,
появляется дополнительный класс ошибок:
console.log в dev-инструментахlocalStoragelocalStorage.setItem("debug_hash", hash);
Такая практика приводит к сохранению хешей вне защищённого контекста.
Системы вроде ELK Stack, Loki, Datadog или Splunk агрегируют логи со всей инфраструктуры. Если пароль или хеш попал в лог:
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)
});
Современные логгеры поддерживают автоматическое скрытие полей:
Пример конфигурации (pino):
const logger = require("pino")({
redact: ["req.body.password", "req.headers.authorization"]
});
При разработке часто включается debug-режим:
Это не должно попадать в production-среду.
Ошибка:
try {
const hash = await bcrypt.hash(password, 10);
} catch (err) {
console.log(err);
}
Некоторые ошибки могут содержать контекст входных данных или внутренние состояния библиотеки.
Корректный подход:
logger.info("User password hash created");
Некорректный:
logger.info(hash);
error — только системные ошибки без пользовательских
данныхwarn — события безопасности без payloadinfo — бизнес-события без чувствительных полейdebug — запрещён в productionЛогирование должно исключать любые данные:
Пример безопасного подхода:
logger.info({
event: "login_attempt",
userId: user.id,
status: "success"
});
console.log("AUTH DEBUG:", req.body);
Часто забываются:
Аналитические SDK могут автоматически собирать:
Без явной конфигурации это приводит к утечке паролей.
Использование bcrypt.js требует пересмотра логики
наблюдаемости системы:
Сам факт наличия bcrypt-хеша в логах уже рассматривается как инцидент безопасности, независимо от контекста.