Keeping secrets out of logs (2024)
Коротко:
Секреты в логах — это не «одним фиксом» решить нельзя. Ни 80/20, ни чудо-инструмента нет. Есть 10 «свинцовых пуль» — несовершенных, но при правильной раскладке работают.
Почему течёт
Причина | Пример |
---|---|
Прямой логинг | log.info(user) вместо log.info(user.id) |
«Мусорные» дампы | logger.debug(req.headers) |
Конфиги | debug=true выводит весь env |
Зашитые секреты | JSON-поле password внутри структуры |
Телеметрия | APM-сборщик хватает всё подряд |
Пользователь | Вводит пароль в поле «имя» |
10 «пуль»
-
Архитектура данных
Разделяем «чувствительное» и «остальное» на уровне схемы; в логи идёт только последнее. -
Трансформации
Сериализуем черезsanitize()
илиtoLog()
— явно выбрасываем секретные поля. -
Domain-primitives
- Компиляция:
SecretString
не реализуетDisplay
. - Рантайм:
Redactable
интерфейс,toString() → "***"
.
- Компиляция:
-
Read-once
Пароль читается 1 раз, дальше объект пустой — логировать нечего. -
Taint-tracking
Помечаем вход как «грязный»; если доходит до логгера — exception. Дорого, но точно. -
Форматтеры логов
Пишем свойLayout
/Encoder
, который режет заранее заданные ключи рекурсивно. -
Unit-тесты
ПроверяемassertThat(log).doesNotContain(secret)
; запускаем на каждый PR. -
Сканеры
Regex-правила + entropy-фильтры в CI и в production-потоке. Сэмплируем, чтобы не умереть от CPU. -
Pre-processors
Vector / Logstash / Cribl вырезают поля ещё до попадания в Elasticsearch. -
Люди
Code-review чек-лист: «есть ли тут .toString / JSON.stringify / printf без фильтров?».
Стратегия
- Фундамент: классификация данных, единый словарь «что считать секретом».
- Карта потока: от источника до хранилища логов.
- Контрольные точки: валидация, sanitize, redact.
- Защита в глубину: 2-3 слоя из списка выше.
- План на инцидент: ротация, оповещение, forensics.
Итог:
Нет волшебства — только дисциплина и много мелких фиксов. Начните с 2-3 «пуль», которые дешёвле всего у вас, и двигайтесь дальше.