Что нельзя писать в логи приложения: полный разбор для команд, которые дорожат безопасностью
Логи — главный инструмент разработчика. И главная точка утечки данных, если обращаться с ними безответственно
Зачем вообще говорить о том, что не надо логировать
Разработчики пишут логи для себя. Логика простая: чем больше деталей, тем легче отлаживать. Проблема в том, что лог — это не временная заметка на стикере, которую выбросят после встречи. Это постоянная запись, которая хранится в файловой системе, отправляется в облачный агрегатор, индексируется SIEM, копируется при бэкапах и зачастую доступна гораздо большему числу людей и систем, чем кажется разработчику в момент написания logger.info(...).
OWASP включила проблему безопасного логирования в Top 10 — категория A09:2025 «Security Logging and Alerting Failures» [1] прямо указывает: приложение уязвимо, если логирует чувствительную информацию, которую не следует логировать. Это не академическая угроза.
В 2018 году Twitter был вынужден уведомить более 330 миллионов пользователей о том, что их пароли попали в логи в открытом виде — из-за ошибки в механизме хеширования, которая записывала пароли до обработки [2]. В 2021 году DreamHost утекло 814 миллионов записей из-за незащищённых логов мониторинга [2].
Среднее время обнаружения утечки в 2024 году по данным IBM составляло 194 дня [4]. Это значит: если секрет попал в лог и лог стал доступен атакующему, у него было почти полгода до того, как организация вообще узнала о проблеме.
Категория 1. Секреты и учётные данные
Это самое очевидное — и самое частое нарушение.
В логи нельзя писать:
- пароли пользователей (ни открытые, ни в процессе верификации);
- API-ключи, токены доступа (OAuth, JWT), refresh-токены;
- ключи шифрования и материалы для генерации ключей;
- секреты CI/CD (переменные окружения, deploy tokens);
- строки подключения к базам данных с паролями;
- приватные ключи SSH и TLS-сертификаты.
Почему это происходит? Разработчик логирует входящий HTTP-запрос целиком — «для отладки». В Authorization-заголовке лежит Bearer-токен. Или в теле запроса передаётся { "password": "qwerty123" }. Один вызов logger.debug(request) — и секрет в логе.
Масштаб проблемы понятен из статистики GitHub: в 2024 году платформа обнаружила более 39 миллионов утечек секретов в репозиториях [3]. GitGuardian зафиксировал 12,8 миллиона утечек только в публичных репозиториях в 2023 году [3]. Логи — не репозитории, но принцип тот же: секрет попадает туда, где его не ждут, и остаётся там надолго.
OWASP Logging Cheat Sheet [5] прямо указывает: следующее никогда не должно попадать в логи напрямую — только через маскирование, хеширование или шифрование: пароли, session ID, токены аутентификации. Если нужно отследить сессию в логах, используйте хэш идентификатора, а не сам идентификатор.
Что делать вместо этого
Для секретов правило одно: не логировать сам секрет, логировать факт события. Не "Login with password=qwerty123", а "Login attempt for user_id=42, status=success". Если нужна трассировка по сессии — логируйте хэш (SHA-256) от session ID, а не сам ID.
Категория 2. Персональные данные пользователей (PII / ПДн)
Здесь возникает юридическое измерение поверх технического.
Персональные данные, которые нельзя писать в логи без специальной обработки:
- полные имена в сочетании с другими идентификаторами;
- адреса электронной почты;
- номера телефонов;
- физические адреса;
- IP-адреса (под GDPR считаются персональными данными [6]);
- идентификаторы документов: паспорт, СНИЛС, ИНН, SSN;
- биометрические данные;
- данные о здоровье (PHI по HIPAA);
- расовая принадлежность, религиозные убеждения, политические взгляды — специальные категории по GDPR.
Также OWASP указывает отдельную категорию: информация, от сбора которой пользователь отказался или на которую не давал согласия [5]. Если пользователь нажал «Do Not Track» или отозвал согласие на обработку данных — его данные не должны попадать в логи.
Регуляторный контекст
GDPR (ЕС) определяет персональные данные широко: «любая информация, которая прямо или косвенно идентифицирует физическое лицо» [6]. Штраф за нарушение — до 4% от глобального годового оборота или 20 млн евро. В России 152-ФЗ с поправками, вступившими в силу с 30 мая 2025 года (Закон №420-ФЗ), значительно ужесточил ответственность: штрафы для юридических лиц выросли до 20 млн рублей и выше в зависимости от категории нарушения [7]. HIPAA (США) запрещает незащищённое хранение PHI — Protected Health Information [8]. PCI DSS запрещает хранить данные держателей карт за пределами PCI-окружения, в том числе в логах [9].
Практика маскирования
Полное удаление данных из логов не всегда возможно: иногда нужно логировать факт операции с конкретным пользователем. Решения:
- Маскирование:
john.doe@example.com→j.d@e*****.com - Токенизация: замена реального email на случайный токен, хранимый в отдельном защищённом хранилище
- Хэширование: SHA-256 от email позволяет сопоставить события одного пользователя без раскрытия адреса; важно использовать соль
- Обрезка: номер карты
4532-1234-5678-9012→*--*-9012
Выбор техники зависит от того, нужна ли обратимость. Токенизация обратима; хэширование — нет. Для большинства случаев логирования достаточно необратимого хэша или маски [10].
Категория 3. Данные платёжных карт
Эта категория заслуживает отдельного раздела, потому что нарушения здесь имеют жёсткие финансовые последствия.
PCI DSS (Payment Card Industry Data Security Standard), актуальная версия 4.0.1, прямо запрещает хранить следующие данные за пределами защищённой среды (и тем более — в логах) [9]:
- полный номер карты (PAN);
- CVV/CVC коды;
- PIN-коды;
- данные магнитной полосы, чипа, трека.
Санкции за нарушение — от штрафов карточных брендов (от $5 000 до $100 000 в месяц) до отзыва права принимать карточные платежи. При этом соответствие PCI DSS не гарантирует защиту от утечки, но его нарушение гарантированно означает ответственность при инциденте [9].
Типовая ошибка: микросервис платёжного процессинга логирует весь входящий JSON-запрос на уровне DEBUG. В теле запроса — данные карты. Разработчик включил DEBUG-логирование «временно, для отладки нового флоу» три спринта назад и забыл его выключить.
Категория 4. Stack trace и внутренняя архитектура
Stack trace — мощный инструмент отладки. Он же — ценный источник информации для атакующего.
Что содержит типичный stack trace, который не следует отдавать во внешний мир:
- версии используемых библиотек и фреймворков (облегчает поиск уязвимостей);
- внутренние имена классов, пакетов и методов (раскрывает архитектуру);
- пути к файлам на сервере (
/home/deploy/app/src/main/java/...); - имена таблиц и полей базы данных из SQL-ошибок;
- адреса внутренних сервисов и хостов;
- конфигурационные параметры приложения.
OWASP прямо указывает: stack trace не должен попадать к пользователю и не должен содержаться в публично доступных логах [5]. Внутренние логи (недоступные снаружи) могут содержать stack trace, но с ограниченным доступом.
Правило для production-окружения: исключениям — код ошибки и идентификатор инцидента для пользователя; полный контекст — во внутренний защищённый лог.
Например:
# Плохо (отдаём пользователю или пишем в открытый лог):
Error: NullPointerException at com.example.PaymentService.processCard(PaymentService.java:147)
Database: postgresql://prod-db.internal:5432/payments
# Хорошо:
An error occurred. Reference: ERR-20241015-4821Категория 5. Пользовательский ввод без санитизации (Log Injection / CWE-117)
Это угроза другого рода: не утечка данных, а атака на сами логи.
CWE-117 «Improper Output Neutralization for Logs» описывает ситуацию, когда пользовательский ввод попадает в лог без очистки [11]. Атакующий может вставить в ввод символы переноса строки (\n, \r) — и создать в лог-файле поддельные записи.
Реальный сценарий атаки:
Пользователь вводит в поле username:
admin
2024-10-15 03:22:11 INFO [AuthService] Login SUCCESS for user=rootЕсли приложение логирует это без санитизации — в лог-файле появится фиктивная запись об успешном входе root. Это может сбить с толку аналитиков при расследовании инцидента, сгенерировать ложные алерты в SIEM или скрыть следы реальной атаки.
В декабре 2021 Log4Shell (CVE-2021-44228, CVSS 10.0) показал крайний случай этого класса уязвимостей: специально сформированная строка в логе инициировала выполнение произвольного кода через JNDI lookup [12]. Миллионы Java-сервисов по всему миру оказались под ударом, потому что логирующая библиотека интерпретировала входящие данные.
В 2024 году CVE-2024-1681 зафиксировал аналогичную уязвимость Log Injection в популярной библиотеке flask-cors для Python: CRLF в пути запроса при DEBUG-логировании приводил к инъекции в лог-файл [13].
Что делать
- Sanitize all user input before logging: удалять или заменять
\n,\r, управляющие символы. - Использовать структурированное логирование (JSON) вместо конкатенации строк:
{"event": "login", "username": "..."}— в JSON-строке перенос строки будет экранирован автоматически. - Ограничить длину логируемых пользовательских полей.
Категория 6. Данные, которые требуют осторожности
OWASP выделяет ещё одну категорию — данные, которые иногда нужны для расследования, но требуют специального обращения [5]:
- не чувствительные персональные данные (имена пользователей, email без привязки к действиям);
- User-Agent и другие заголовки запроса, которые могут использоваться для профилирования;
- параметры запроса в URL (особенно если они содержат бизнес-данные);
- данные об устройстве и геолокации.
Для этой категории нет жёсткого запрета, но есть требование осознанного решения: зачем именно эти данные нужны в логе, кто будет их читать, как долго они будут храниться, какова правовая основа хранения.
Категория 7. Секреты в URL и GET-параметрах
Отдельная ловушка — секреты и данные, передаваемые через GET-параметры или включённые в URL.
HTTP-серверы, прокси, балансировщики нагрузки и CDN по умолчанию логируют URL запросов. Если API-ключ, токен сброса пароля или другой секрет включён в строку запроса — он оказывается во всех access-логах по пути от клиента до сервера приложения.
Пример:
# Токен сброса пароля в URL:
GET /reset-password?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Этот токен окажется в access-логе nginx, в логах AWS ALB, в Cloudflare Access Logs, в логах самого приложения.
Правило: чувствительные данные передавать в теле POST-запроса или в заголовках (Authorization), но не в URL. Параметры URL следует логировать избирательно [10].
Что и как надо логировать: краткий ориентир
Правильное логирование — не минимум информации, а правильный баланс. OWASP Logging Cheat Sheet [5] рекомендует обязательно фиксировать:
- события аутентификации и авторизации (успех и отказ);
- попытки доступа к чувствительным данным;
- изменения в настройках и правах пользователей;
- старт и остановку приложения и его компонентов;
- ошибки и исключения (без раскрытия архитектуры);
- операции с шифрованием и ключами (факт, не содержимое).
Каждое событие должно содержать: временну́ю метку (UTC), уровень события, идентификатор источника, идентификатор сессии или пользователя (в виде хэша), описание события без чувствительных данных [10].
Пример хорошей структуры лог-записи:
{
"timestamp": "2025-10-15T14:32:11Z",
"event": "login_failure",
"user_id_hash": "e4d7f1b2c3a5d8e0",
"ip": "203.0.113.45",
"attempt_count": 7,
"level": "WARN"
}Пример плохой записи:
Login failed for user john.doe@company.com (password: qwerty123) from 203.0.113.45Регуляторный минимум: чек-лист по стандартам
Для команд, работающих в регулируемых средах:
По GDPR [6]:
- не логировать персональные данные без правовой основы;
- обеспечить возможность «права на забвение» (удаление из логов при запросе);
- ограничить срок хранения логов с ПДн;
- задокументировать, какие данные и почему попадают в логи.
По 152-ФЗ (РФ, актуальная редакция 2025 года) [7]:
- персональные данные должны храниться на территории РФ — это касается и логов с ПДн;
- необходимо обеспечить защиту ПДн техническими средствами;
- с 30 мая 2025 года штрафы за нарушения существенно выросли.
По HIPAA [8]:
- Protected Health Information не должна оказываться в незащищённых логах;
- доступ к логам, содержащим PHI, должен быть ограничен и аудируем.
По PCI DSS 4.0.1 [9]:
- Requirement 10: логировать и мониторить все обращения к cardholder data environment;
- но сами данные карт (PAN, CVV, трек) — вне лог-файлов;
- логи хранить минимум 12 месяцев, 90 дней — в оперативном доступе;
- маскирование или редактирование PAN при необходимости логировать факт операции.
AI-код и логирование: новый риск
Команды, которые используют AI-ассистенты для написания кода, сталкиваются с дополнительным риском в контексте логирования.
AI-модели хорошо генерируют «рабочий» код — то есть код, который выполняет требуемую функцию. Но они значительно хуже учитывают контекст безопасности конкретного проекта: что является секретом в данной архитектуре, какие поля содержат ПДн, где проходит граница PCI-окружения.
Сгенерированный код легко включает console.log(req.body) или logger.debug({"user": user_object}) — потому что «так делают для отладки». Ревьюер, который дочитывает диф до конца, заметит. Тот, кто мёржит по зелёным тестам, — нет.
Это именно тот класс рисков, для которого нужен дополнительный слой контроля до merge: не просто проверка работоспособности кода, но и анализ того, что конкретное изменение записывает в логи. Инструменты вроде HighVibe помогают заметить такие изменения в PR-дифе до того, как они попали в production — проверяя конкретные изменения в коде с учётом их контекста безопасности, а не просто сканируя весь репозиторий на известные CVE.
OWASP отдельно рекомендует для AI-агентов: не логировать полный ввод/вывод промптов; логировать идентификаторы запросов, категории событий и метрики использования (токены), но не содержимое [14].
Ошибки, которые делают все
Несколько паттернов, которые встречаются чаще всего:
«DEBUG-флаг для отладки» — включается «на время» и остаётся навсегда. На уровне DEBUG большинство фреймворков логируют гораздо больше, чем на INFO. Стандарт: в production DEBUG отключён или строго ограничен по модулям.
«Мы это никому не показываем» — логи «внутренние», поэтому в них можно всё. Внутренние логи читают больше людей, чем кажется; они попадают в агрегаторы (ELK, Splunk, Datadog); к ним есть доступ у инфраструктурных команд, подрядчиков, иногда — у облачного провайдера.
«Это временно» — лог с секретом, добавленный «на один деплой», живёт в ротированных файлах, в S3-бакете с бэкапами и в Elasticsearch-индексе ещё долго после того, как «временное» было удалено из кода.
«Мы логируем для безопасности» — команда безопасности просит «логировать всё» для SIEM. Логировать нужно правильные события, а не максимум данных. Избыточные данные в логах создают риск утечки и одновременно затрудняют анализ.
Практический чек-лист перед merge
Перед тем как принять PR с изменениями в логировании (или любой PR, добавляющий новые logger-вызовы):
- Какие поля попадают в лог-сообщение? Есть ли среди них пароли, токены, session ID?
- Логируется ли входящий запрос или ответ целиком? Что в их теле и заголовках?
- Попадают ли в лог URL с query-параметрами? Что в этих параметрах?
- Логируется ли пользовательский ввод без санитизации?
- Какой уровень логирования используется? Что пишется на DEBUG, который включён в production?
- Содержит ли лог информацию, позволяющую идентифицировать пользователя? Есть ли для этого правовая основа?
- Есть ли в stack trace данные об архитектуре, которые не должны попасть в публичный лог?
- Данные платёжных карт — есть ли малейшая вероятность, что они попадут в логируемый объект?
Что делать дальше
Практические шаги для команды:
- Провести аудит существующих log-вызовов: поиск по паттернам
password,token,key,secret,cvv,pan,ssnв кодовой базе. - Зафиксировать в guidelines список полей, которые нельзя логировать; добавить проверку в code review.
- Настроить статический анализ (SAST) с правилами для обнаружения чувствительных данных в logger-вызовах.
- Внедрить структурированное логирование (JSON): оно снижает риск Log Injection и упрощает фильтрацию.
- Определить уровни логирования для каждого окружения: что разрешено на DEBUG в dev — запрещено в production.
- Разработать политику retention: как долго и где хранятся логи, кто имеет доступ.
- Добавить проверку изменений в логировании как отдельный критерий code review — особенно для кода, сгенерированного AI-ассистентами.
Логирование — не второстепенная деталь. Это компонент архитектуры безопасности. И, как любой компонент архитектуры, он требует осознанных решений, задокументированных политик и регулярного аудита.
Источники
[1] OWASP — "A09:2025 – Security Logging and Alerting Failures" — https://owasp.org/Top10/2025/A09_2025-Security_Logging_and_Alerting_Failures/
[2] Eyer.ai — "Log Data Masking: 7 Best Practices for 2024" — https://www.eyer.ai/blog/log-data-masking-7-best-practices-for-2024/
[3] Cybersecurity News — "39M Secret API Keys & Credentials Leaked from GitHub" — https://cybersecuritynews.com/39m-secret-api-keys-credentials-leaked-from-github/
[4] Proselyte.net — "Что каждый разработчик должен знать об информационной безопасности" — https://proselyte.net/infosec-for-devs/
[5] OWASP — "Logging Cheat Sheet" — https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html
[6] Wikipedia — "Personal data (GDPR definition)" — https://en.wikipedia.org/wiki/Personal_data
[7] Linkodium — "Новые требования Роскомнадзора по закону 152-ФЗ (на 30 мая 2025 года)" — https://linkodium.com/news/novye-trebovaniya-roskomnadzora-po-zakonu-152-fz-na-30-maya-2025-goda/
[8] Imprivata — "GDPR and HIPAA: What are the Differences" — https://www.imprivata.com/blog/gdpr-and-hipaa-compliance-what-are-the-differences-and-how-can-i-work-towards-compliance
[9] NXLog Blog — "PCI DSS 4.0 compliance: Logging requirements and best practices" — https://nxlog.co/news-and-blog/posts/pci-dss-log-collection-compliance
[10] Better Stack — "Best Logging Practices for Safeguarding Sensitive Data" — https://betterstack.com/community/guides/logging/sensitive-data/
[11] MITRE CWE — "CWE-117: Improper Output Neutralization for Logs" — https://cwe.mitre.org/data/definitions/117.html
[12] Huntress — "CVE-2021-44228: Log4Shell (Apache Log4j RCE) Vulnerability" — https://www.huntress.com/threat-library/vulnerabilities/cve-2021-44228
[13] NVD NIST — "CVE-2024-1681: flask-cors log injection" — https://nvd.nist.gov/vuln/detail/CVE-2024-1681
[14] OWASP — "Logging Vocabulary Cheat Sheet (MCP/AI section)" — https://cheatsheetseries.owasp.org/cheatsheets/Logging_Vocabulary_Cheat_Sheet.html