Логирование и мониторинг безопасности: как заметить атаку

Детектирование — не опция, а последняя линия обороны. Разница между «взлом длился две минуты» и «взлом длился пять месяцев» — это ровно то, насколько быстро вы заметили. В OWASP Top 10:2025 это самостоятельная уязвимость — A09: Security Logging and Alerting Failures.

Дано по состоянию на октябрь 2026; версии стеков помечены явно, чтобы сверить при чтении.


Часть I. Зачем отдельный журнал безопасности

1. Два разных вида логов, которые вечно смешивают

OWASP Logging Cheat Sheet прямо разделяет операционные и безопасностные случаи использования одного и того же приложения:

Операционный логЖурнал безопасности
дебаг, профилирование, таймауты страницинциденты, нарушения политики, audit-trail
короткий TTL (дни)длинный TTL (месяцы–год+)
можно фильтровать и дропать шумнельзя молча терять события ради экономии
читают разработчикичитают SOC / ИБ / суд / регулятор
объёмный, дешёвыйсжатый, защищённый, дорогой

Практический смысл: если у вас включён только define('WP_DEBUG_LOG', true) — у вас нет журнала безопасности. Это ровно то состояние, которое OWASP в Top 10:2025 считает самостоятельным риском (A09): когда событие атаки произошло, но нигде не зафиксировано, вы не сможете ни расследовать инцидент, ни доказать его факт, ни понять, что было скомпрометировано.

2. Что такое «успех» детектирования

Метрика не «число алертов», а время:

  • MTTD (mean time to detect) — сколько проходило от начала атаки до сигнала.
  • MTTR (mean time to respond) — сколько с сигнала до реакции.

Среднестатистический компрометационный доступ «живёт» в сети месяцами именно потому, что никто не знает, на что смотреть. В статье про аудит мы инвентаризировали векторы; этот логично продолжает тот список: вектор «мы даже не узнаем, что нас взломали».

3. Логи — не бесплатные

У OWASP-читшита отдельный раздел «Attacks on Logs». Журнал — это данные, к ним применима та же модель CIA, что к остальным:

  • Конфиденциальность: в access-log слились токены сброса пароля и PII → читатель логов получает больше, чем пользователь.
  • Целостность: злоумышленник перезаписал/удалил строки, скрыв вторжение.
  • Доступность: флудом событий забит диск, на котором живёт и /var/lib/mysql → падает и приложение.
  • Подотчётность: атака подставила чужой IP или «доверенный» внутренний хост в поле identity.

Правило: то, что защищает вас, — отдельная цель атакующего. Проектируйте логирование именно так, а не «как-нибудь выведу в файл».


Часть II. Что логировать и что — никогда

4. Обязательные события (без «размера кошелька»)

Из OWASP-читшита берём ядро списка. Логируйте всегда:

  • Провалы валидации ввода — протокольные нарушения, неприемлемые кодировки, невалидные имена/значения параметров. Это ваш первый сигнал на сканеры и фаззинг.
  • Успехи и отказы аутентификации — критично для брутфорса, credential stuffing и password spraying.
  • Отказы авторизации (access control) — попытка чужого заказа/чужого ID (IDOR-шаги из статьи про аудит).
  • Сбои управления сессией — подмена cookie-session, подозрительные JWT validation failures.
  • Ошибки приложения и события системы — runtime-исключения, обрывы соединения, ошибки файловой системы, антивирус на загрузке, смена конфигурации.
  • Высокорисковый функционал:
    • админ-пользователи (добавление/удаление, смена привилегий, назначение токенов);
    • все действия администраторов приложения;
    • использование дефолтных/общих или «break-glass» аккаунтов;
    • доступ к чувствительным данным (платёжные, мед.);
    • криптографические операции (использование/ротация ключей — см. статью про шифрование);
    • создание/удаление системных объектов;
    • импорт/экспорт данных (включая экранные отчёты);
    • загрузка пользовательских файлов;
    • провалы десериализации;
    • сетевые соединения и отказы TLS бэкенда, неожиданные HTTP-глаголы.
  • Юридические/согласительные события — принятые юзером условия, согласия, opt-in (мобильные разрешения).
  • Подозрительная бизнес-логика — действия не по порядку (пропуск шага оплаты/подтверждения), превышение лимитов действия, «то, чего в бизнесе не бывает».

Важная формулировка из читшита по A10 (Mishandling of Exceptional Conditions): не выбрасывайте всё событие из-за кривого поля. Поле, не прошедшее валидацию, — безопасно omit’ните или замените заглушкой и отдельно залогируйте сам факт провала с ограниченным контекстом. Терять событие целиком нельзя: вы теряете улику.

5. Поля события: «кто, что, где, когда»

Каждая запись несёт минимум:

  • Когда: дата+время в международном формате; отдельно — время события (может отличаться от времени записи в лог при офлайн-клиентах); interaction id — один идентификатор на всю цепочку событий одного пользовательского действия. Это ключ к корреляции: вместо того чтобы потом гадать, какие пять записей принадлежали одной загрузке формы, вы зашиваете связь в момент логирования.
  • Где: имя+версия приложения, узел/порт/cluster, сервис, entry-point URL + HTTP-метод, расположение кода (модуль/скрипт).
  • Кто: source address (IP / device id), identity пользователя (логин или PK пользователя в БД, не его пароль).
  • Что: тип события, severity (RFC 5424: 0=emergency … 7=debug), флаг «безопасностное ли событие», описание.

Полезные дополнения из той же таблицы читшита: action, object (на что направлено), result status (success/fail/defer), reason, HTTP status code, User-Agent, классификация типа юзера (public / CMS / поисковый робот / uptime-монитор / пентестер), аналитическая уверенность детекта (low/med/high), внутренняя/внешняя классификация (NIST SCAP, MITRE CAPEC).

Note A из читшита про interaction identifier особенно ценен для асинхронных цепочек: «input validation fail» и «output validation fail» одного «saga request» могут лежать в разное время и в разных логах — идентификатор взаимодействия склеивает их обратно.

6. Что НЕ писать в лог (даже «очень надо для дебага»)

Читшит даёт закрытый список «remove, mask, sanitize, hash or encrypt». Не логируются в открытом виде:

  • пароли (любые — ни хеш, ни plaintext, ни «старый хеш» при смене);
  • access tokens и refresh tokens;
  • session identification values (cookie sid, JWT id_token целиком) — вместо них хеш от session id, если идентификатор сессии нужен для корреляции;
  • ключи шифрования и первичные секреты, строки подключения к БД;
  • чувствительные PII (здоровье, гос. идентификаторы, платёжные данные);
  • банковские/платёжные реквизиты (кардхолдер-данные);
  • сведения более высокого грифа, чем позволено системе логирования;
  • коммерческая тайна; то, что запрещено собирать в вашей юрисдикции; то, от чего пользователь opt-out‘нул (включая «Do Not Track»).

Никогда не исключайте из журнала «доверенные» источники — внутренние системы, поисковых ботов, uptime-мониторы, аудиторов, пентестеров. Их можно пометить классификационным флагом, но нельзя перестать логировать: именно взломанный «доверенный» сервис обходит периметр.

Практика по IP при GDPR: полный IP — персональные данные. Для детекта брутфорса IP нужен, но его можно хранить усечённым (IPv4 /24, IPv6 /48) или hash_hmac(ip, secret). Так вы теряете точную привязку одного адреса, но оставляете возможность кластеризовать атаки и сопоставить «этот же IP» между записями за период хранения секрета.

7. CRLF-инъекция в лог (CWE-117)

Если вы пишете в текстовый лог строкой, управляющие символы в данных пользователя создают фальшивые записи. Пример для PHP: error_log("login failed for {$userInput}"), где $userInput = "admin\n2026-10-12 09:00:00 login success for attacker" — это вторая, чистая, «успешная» строка атаки. В статье про OWASP Top 10 мы уже закрывали это так:

PHP
function safe_log(string $msg, string $channel = 'app'): void {
    // вырезаем CR/LF и любые управляющие символы
    $msg = preg_replace('/[\x00-\x1F\x7F]/u', '', $msg);
    error_log(sprintf('[%s] %s', $channel, $msg));
}

Если лог пишется не строкой, а JSON-структурно, инъекция не сработает: управляющие символы экранируются форматом (\n станет \n, а не переводом строки). Поэтому для безопасностных событий предпочтительны structured logging (JSON lines), а не конкатенация в строку.


Часть III. Практическое логирование: PHP, nginx, WordPress

8. Структурированный лог на PHP (Monolog / PSR-3)

Базовый рецепт: один обработчик, сериализующий событие в одну JSON-строку; severity по PSR-3 (совместим с RFC 5424); correlation-id в контексте.

PHP
<?php
// composer require monolog/monolog
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\JsonFormatter;
use Monolog\Level;

$log = new Logger('app');
$h = new StreamHandler('php://stderr', Level::Info); // stdout/stderr — идеал для systemd/docker
$h->setFormatter(new JsonFormatter());
$log->pushHandler($h);

$correlationId = $_SERVER['HTTP_X_REQUEST_ID']
    ?? bin2hex(random_bytes(8));

// пример: провал аутентификации — без пароля, с хешем логина и IP
function log_auth_fail(Logger $log, string $correlationId, string $identifier, string $ip): void {
    $log->warning('authentication_failure', [
        'interaction_id' => $correlationId,
        'actor_hash'     => hash_hmac('sha256', mb_strtolower($identifier), SECRET_PEPPER),
        'source_ip'      => long2ip((ip2long($ip) ?? 0) & 0xFFFFFF00), // IPv4, обрезанный до /24
        'user_agent'     => $_SERVER['HTTP_USER_AGENT'] ?? '',
        'reason'         => 'invalid_credentials',
        'action'         => 'login',
        'result'         => 'fail',
    ]);
}

php://stderr — осознанный выбор: логи не на диске приложения, а их перехватывает journald/Docker и отправляет дальше. Отдельный раздел ниже про то, куда это течёт.

9. nginx: access_log, который полезен для детекта

combined формат — минимум. Для безопасности добавьте: время, method+status+bytes, request_time/upstream_response_time (помогает отличить медленный легитим-запрос от DoS), $request_id (correlation с PHP), весь query string, User-Agent и Referer, и $http_authorization — но его только как флаг наличия, не сам заголовок:

log_format sec escape=json
  '$remote_addr '
  '$time_iso8601 '
  '"$request_method $request_uri $server_protocol" '
  '$status $body_bytes_sent '
  'rt=$request_time urt=$upstream_response_time '
  'rid="$request_id" '
  'uid="$http_x_forwarded_for" '
  'ua="$http_user_agent" '
  'ref="$http_referer"';

access_log /var/log/nginx/access.log sec;
error_log  /var/log/nginx/error.log warn;

Отдельно: nginx-переменная $request_id + fastcgi_param HTTP_X_REQUEST_ID $request_id пробрасывает correlation-id в PHP — связывает «запрос→приложение→БД-ошибка» без гадания.

10. WordPress: куда втыкать крючки

Хуки (точное число аргументов и сигнатуры сверяйте в актуальном Codex/developer.wordpress.org, т.к. менялись в минорных релизах):

  • wp_login — успешный вход.
  • wp_login_failed — провал входа (логин передаётся, пароль — никогда; не пишите полученный $username прямо, хешируйте по §6).
  • wp_logout — выход.
  • authenticate + wp_authenticate_user — точки, где понятен WP_Error (заблокирован/не тот пользователь).
  • switch_theme, activated_plugin, deactivated_plugin, add_user, remove_user, promote_user — админские/рисковые события из обязательного списка.
  • wp_handle_upload — загрузка файла.

Минимальный пример (в mu-plugin, чтобы работало при отказе темы):

PHP
<?php
defined('ABSPATH') || exit;

add_action('wp_login_failed', function (string $username): void {
    $ip  = $_SERVER['REMOTE_ADDR'] ?? '';
    $log = sprintf(
        'AUTH_FAIL user=%s ip=%s ua=%s',
        substr(hash_hmac('sha256', $username, wp_salt('auth')), 0, 16),
        $ip,
        isset($_SERVER['HTTP_USER_AGENT'])
            ? preg_replace('/[\x00-\x1F\x7F]/u', '', substr(wp_unslash($_SERVER['HTTP_USER_AGENT']), 0, 200))
            : ''
    );
    error_log($log);
}, 10, 1);

Про «audit trail»-плагины для WP (например, записывающие действия админов в таблицу БД): они делают ровно одно полезное — пишут то, что штатный WP не пишет (кто и что редактировал). Но: (1) таблица логов в той же БД, что и приложение = тот же компромат стирает её одним DELETE; (2) часть таких плагинов сохраняет запрос целиком — а в $_POST иногда оказывается wpnonce, а при ошибке входа на экране может остаться хеш пароля. Не заводите плагин аудита, не посмотрев, что он пишет в БД и как защищён сам. Для серьёзного хранения нужен вынос логов с хоста.


Часть IV. Агрегация: от файла к системе

11. Конвейер в пяти стадиях

  1. Источники (приложение, nginx, authd, ядро/auditd, контейнеры) пишут структурированно в stdout/socket.
  2. Shipper/агент на хосте читает и отправляет в центр (Fluent Bit, Vector, Promtail, filebeat/wazuh-logcollector).
  3. Брокер/буфер (Kafka/Redis, либо напрямую в хранилище на малых объёмах).
  4. Хранилище+индекс (OpenSearch/Elasticsearch/Loki/ClickHouse/Wazuh Indexer).
  5. Запрос+детект+алерт (Grafana/LibreNMS/Kibana/OWASP-HIDS/Security Analytics/Sigma-движок) и дашборды.

Главная архитектурная мысль OWASP-читшита в разделе «Network architecture»: путь записи логов и путь чтения логов физически разные приложения и разные сетевые доступы. То есть запись (collector) и чтение/выдача (loader+UI) — раздельные сегменты. Если у вас всё стоит на том же VPS, что и сайт, и ломается одним выключателем — это не «журнал безопасности», а «журнал на время жизни атаки».

12. Три стека: что выбрать на самохостинге

СтекСутьКогда братьАктуально (10.2026)
Grafana Loki«Prometheus для логов»: индекс только по лейблам, не полнотекстдёшево, быстро, если объём большой, а ищете по структурированным меткам и по известному шаблону3.7.8 (17.09.2026), LogQL
OpenSearch (форк ES)полнотекстовый индекс + дашборды + Security Analyticsнужны сложные корреляции, полнотекст-расследования, SIEM «по-взрослому», и вы готовы платить RAM3.9.0 (latest tag), Detection rules в формате Sigma
WazuhHIDS+SIEM «под ключ»: агент, decoders, ruleset, MITRE ATT&CK, active response, FIM/whodata, SCAхотите готовые детекты и комплаенс-отчёты, а не собирать их руками из raw-логов4.14.8 (25.09.2026)

Почему не «один на всех»: Loki идеален по стоимости и для метрик «сколько 4xx за 5 минут», но плохо тянет полнотекст-расследование «найди все запросы с этим sha256 шелла» — там нужен инвертированный индекс (OpenSearch/ClickHouse). Wazuh даёт готовый ruleset и карту ATT&CK «из коробки», но дублирует то, что вы уже делаете через nginx/Loki. На практике комбинируют: Loki (операционные логи, дёшево) + Wazuh/OpenSearch (безопасностные события, детект) + Falco (syscall-детект в контейнерах). Это не избыточность, а разные горизонты наблюдения.

Замечание про Falco 0.45.0 (rel. 21.09.2026, libs 0.26.0, driver 11.0.0+driver, falco-rules 5.2.0): в этой версии breaking change — движок матчит условия правил по сырым байтам поля (FALCO_ENGINE_VERSION 0.62.0 → 0.63.0), невалидные UTF-8 теперь матчатся строковыми операторами, а regex работает по санитизированному входу. Старые правила, полагавшиеся на match replacement-character, нужно пересмотреть — иначе они тихо перестанут срабатывать.

13. Loki + Promtail: минимум на практике

promtail (или более новый Loki Alloy) тащит логи в loki:3100. Config-минимум для nginx-аутентификационных строк:

# promtail-config.yaml
server:
  http_listen_port: 9080
positions:
  filename: /var/lib/promtail/positions.yaml
clients:
  - url: http://127.0.0.1:3100/loki/api/v1/push
scrape_configs:
  - job_name: app-sec
    static_configs:
      - targets: [localhost]
        labels:
          job: app-sec
          host: web-01
          __path__: /var/log/app/secure.log
    pipeline_stages:
      - json:                 # приложение пишет JSON-lines
          expressions:
            severity: level
            event: message
            actor: actor_hash
            src: source_ip
      - labels:
          severity:
          event:

LogQL (язык запросов Loki) — примеры, которые реально полезны для детекта:

# доля неудачных авторизаций от источника, по 5-минутному окну
sum by (host) (
  rate({job="app-sec", event="authentication_failure"} [5m])
)

# топ-20 айпишников по числу failed логин за 15 минут — сигнал брутфорса
topk(20,
  sum by (src) (
    count_over_time({job="app-sec", event="authentication_failure"} [15m])
  )
)

# всплеск 4xx/5xx в nginx-логе (нужен парсинг статуса в метку)
sum(rate({job="nginx"} | status=~"4..|5.." [1m]))

Loki не индексирует полный текст, поэтому |~ (регулярка по содержимому) медленнее, чем по метке — важные для детекта поля вытаскивайте в labels на этапе pipeline, а не ищите позже регуляркой.


Часть V. Детектирование и алерты

14. Что реально видно в логах

Три группы событий, покрывающие большую часть «тихой» атаки:

Брутфорс и credential stuffing. Различаются по форме распределения (данные по методике читшита — повторяющиеся отказы одного аккаунта, отказы с разных IP, быстрые всплески):

СхемаПаттерн «IP↔логин»
Brute force (один аккаунт)1 IP → много попыток 1 user
Password sprayingмного IP → 1 user
Credential stuffing (слив)много user, мало IP, высокий % попаданий сразу после серии fail→success

Детект: порог failed_login_count за скользящее окно + «успех после N провалов с того же ip/actor-hash». В статье про защиту API у нас был rate-limit по тому же ключу — счётчик отказов живёт рядом, на том же хранилище-окне (Redis).

Сканеры и разведка. wp-login.php, /.env, xmlrpc.php (см. аутентификация → закрытие xmlrpc/?author), массовый ?rest_route=/, типовой User-Agent (sqlmap, nikto, python-requests), высокий темп 404. Один запрос — ничего, распределение по времени + уникальность путей на IP — сигнал.

События после входа. Неожиданная смена пароля/email после login из новой гео; promote_user/add_user из админки; загрузка wp-content/uploads/*.php; изменение wp-config, .htaccess, crontab (это зона Wazuh FIM/whodata и Falco). Плюс — сам факт остановки логирования: событие «log collector disconnected» = приоритетный алерт, потому что это ровно попытка ослепить детект.

15. Sigma: писать детекты один раз

Sigma — независимый от вендора формат описания правил (YAML-логическое «что ищем»), который конвертится в запросы к OpenSearch/Detection Rules, Splunk, Elasticsearch, QRadar и др. Это ваш способ не привязывать детект к одному стеку. Пример правила на брутфорс по нашим JSON-событиям:

title: Multiple Authentication Failures
id: 6f1b5f9c-...        # свой UUID
status: experimental
description: >
  >5 authentication failures per (actor_hash, source_ip) within 5m.
author: you
logsource:
  product: application
  service: auth
detection:
  selection:
    event: 'authentication_failure'
  condition: selection | count() by actor_hash, source_ip > 5
falsepositives:
  - пользователь забыл пароль и «тыкает»
  - CI/скрипт с неверным ключом, ложащийся на auth
level: medium
tags: [attack.t1110.001, attack.t1110.004]   # MITRE: Brute Force

Правила OpenSearch Security Analytics и Wazuh-детекты в MITRE ATT&CK — из того же леса: не изобретать свои техники, а маркировать свои события ATT&CK-ID, чтобы потом строить корреляцию по тактикам (куча «Initial Access» подряд с одного actor — это одна цепочка, а не 30 алертов).

16. Falco: поведенческий слой поверх syscall

Логи приложения не увидят, что из-под PHP-FPM запустили /bin/bash -c curl .... Falco слушает syscalls и алертит по поведению. Мини-правило (свой rules-file поверх falco-rules 5.2.0):

- macro: webproc
  condition: proc.pname in (php-fpm, php, litespeed, nginx)

- rule: Shell spawned by web process
  desc: Web-сервер породил shell — типовой признак RCE/webshell.
  condition: spawned_process and webproc and proc.name in (bash, sh, zsh, python, perl, nc, curl, wget)
  output: >
    Web process spawned shell
    parent=%proc.pname cmdline=%proc.cmdline uid=%user.uid pid=%proc.pid
    container=%container.id image=%container.image.repository
  priority: CRITICAL
  tags: [attack.t1059, attack.execution]

Falco-новинка про raw bytes (см. §12) касается и таких правил, если вы матчите нечитаемые payload’ы в cmdline: теперь совпадение идёт по байтам, а строка вывода экранирует непечатное — старые правила с матчем replacement-символа надо пересмотреть, чтобы они не «замолчали».

17. Как не утонуть в «alarm fog»

OWASP прямо предупреждает: «no one size fits all, и approach по blind-списку даёт alarm fog, в котором реальные проблемы не видны». Отсюда практика алертов:

  • Три уровня эскалации: page (человек встаёт ночью: компрометация, mass-export, отключение логгера), ticket (разобрать до конца дня: аномалия входа, всплеск 4xx, новые 404-паттерны), log-only (в дашборд, без пуша).
  • Cooldown + дедуп по (rule id, actor, окно). Одна атака ≠ 1000 писем.
  • Baseline перед детектом. Две недели только сбора и нормировки: какой у вас нормальный % auth-fail, нормальное число админ-входов. Без baseline все пороги — гадание.
  • Не алертить по одному событию — алертить по корреляции/скорости. Один провал входа — не инцидент; «провалы×скорость + смена гео + последующий success» — инцидент.
  • False-positive бюджет. Если правило срабатывает >5 раз в неделю на легитим — либо сузьте (добавьте контекст/исключения, как исключения CRS в WAF-статье), либо снесите в log-only. Детект, который все глушат, бесполезен.

Часть VI. Целостность, хранение и приватность

18. Защита самого журнала

Собираем разделы «Protection» + «Verification» + «Attacks on Logs» в чек-лист (по формулировкам читшита, а не по памяти):

  • Отдельный раздел диска для логов — чтобы «забивание логами» не убило БД/приложение (раздел Availability).
  • Права: кто пишет — отдельный, только на запись (write-only), кто читает — отдельно; доступ к логам тоже логируется (все обращения к журналу сами суть события — см. раздел At-rest).
  • Не в web-root: доступ через UI/аналитику, а не через https://site/logs/error.log. Если всё же отдаётся — plain-text MIME, а не HTML.
  • Скорость записи = скорость отправки: чем дольше событие живёт только на хосте атаки, тем легче его стереть. Отправляем/копируем в read-only-хранилище как можно раньше; «Store or copy log data to read-only media as soon as possible».
  • Защита при передаче: по недоверенной сети — RFC 5424 (syslog) поверх TLS (RFC 5425), или HTTPS-transport агента (Vector/Promtail/filebeat→indexer), плюс верификация источника события (откуда я знаю, что это с этого хоста).
  • Ап-тайм-проверка: активный мониторинг «логи перестали идти» — это отдельный алерт, иначе молчание вы примете за отсутствие атак («detect whether logging has stopped»).
  • Тампер-детект: цепочки/хеш-чек-суммы батчей, append-only, WORM/immutable bucket (S3 Object Lock и аналоги). Полную неизменяемость для файловых логов обеспечивает скорее вынос в объект с retention-lock, чем файловая система.
  • Отказ logging ≠ отказ приложения: при недоступном коллекторе приложение продолжает работать, но буферизует и сигналит о деградации; при этом нельзя полностью отключить журналирование ни из админки, ни из кода («should not be possible to completely deactivate application logging»), а смена уровня логирования ведётся по change management и проверяется периодически.
  • Валидация чужих данных до записи: данные из чужого trust-зоны (WAF-агент, клиентский JS, syslog-от «доверенного» партнёра) валидируются и эскейпятятся перед тем, как попасть в строку — это защита от log forging (§7).

19. Время: без NTP детект врёт

Корреляция по окнам (брутфорс за 5 минут, rate-limit по скользящему окну, «успех сразу после серии») полностью зависит от того, что часы на всех узлах согласованы. Разъезд в 2–3 минуты между web и auth-сервером превращает «пять подряд» в «три отдельно». Поэтому: chrony/ntp везде + контроль clock-skew (у Prometheus есть node_timex_offset_seconds). В distributed/офлайн-клиентах, где точное время невозможно (Note C), фиксируйте смещение или уверенность в метке времени события.

20. Retention и GDPR

  • Срок хранения диктуется «юридическим, регуляторным, договорным», а не «сколько диск тянет». Для self-hosted без комплаенс-обязательств рабочий ориентир: операционные логи — дни/недели, безопасностные — 6–13 месяцев (типичный коридор для расследований и «свежесть» доказательств).
  • Не хранить дольше, чем нужно — это такая же ошибка, как хранить меньше: вы умножаете поверхность утечки PII, которые случайно попали в логи (запросы с токеном в query, имена из $_POST). Обязательна проверка выборочных записей на «а не пишутся ли у нас токены» (см. §6).
  • DSAR / право на удаление: если у вас в журнале лежит идентифицируемый пользователь, по запросу на удаление вы должны его вычистить и из логов. На файловой/иммутабельной базе это больно — поэтому лучше держать идентификаторы псевдонимизированными (hmac/усечённый IP) с хранением ключа псевдонимизации отдельно: удалили ключ — записи обезличились.

Часть VII. Детект без плейбука — это просто уведомления

Алерт, на который нечего ответить, выключат за две недели. Минимальный плейбук под три уровня (§17):

  1. Confirm: открыли сырые события вокруг сигнала (тот же interaction_id/actor_hash/IP-корзина/временное окно), убедились, что не легитим (CI-скрипт, «бабушка забыла пароль», uptime-монитор, наш пентестер).
  2. Contain: если подтверждено — блокируем источник (rate-limit в API-защите, правило WAF On-блокировки из ModSecurity), инвалидируем сессии/токены (механика — в аутентификации: session_regenerate_id, смена AUTH_KEY/salt, ротация App Password), при компрометации сервера — не «чиним на живом», а изолируем и ставим чистый.
  3. Scope: что затронуто (учётки, данные, файлы). Здесь как раз платят за себя interaction_id (§5), correlation rid из nginx (§9) и FIM-история (Wazuh/Falco). Без них scope = «а вдруг всё».
  4. Fix + evidence: закрыть первопричину (не сам факт алерта — это симптом), сохранить артефакты инцидента в read-only хранилище до истечения retention, задокументировать timeline.
  5. Feedback loop: каждый ложняк/пропуск → правка порога/исключения/правила Sigma (см. §17 и §15). Детект живёт только итерациями.

Часть VIII. Чек-лист

Есть ли у вас журнал безопасности (а не дебаг)

  • Раздельные потоки: операционные логи vs безопасностные события.
  • Структурный формат (JSON-lines), severity по RFC 5424/PSR-3, `interaction_id`.
  • Логируются auth-failures и authz-failures, а не только успешные входы.
  • Логируются админ-действия, загрузка файлов, смены привилегий, ротация ключей, экспорт данных.
  • Логируется **само логирование**: «log collector disconnected» и «logging level changed».

Нечего лишнего

  • В логах нет паролей, токенов, session id, secrets (или они хеш/маска).
  • IP обрезан/hmac’нут под требования приватности; идентификаторы псевдонимизированы.
  • До записи эскейпится управляющие символы (CWE-117) либо лог структурный (JSON).
  • «Доверенные» источники (боты, CI, пентестеры) не исключены полностью — только помечены.

Агрегация

  • Логи мгновенно уходят с хоста приложения (stdout→agent→center), не лежат «до расследования».
  • Путь записи и путь чтения логов разведены по сетевым доступам.
  • Выбран стек под задачу: Loki (метки/дёшево) / OpenSearch (полнотекст/SIEM) / Wazuh (готовые детекты+MITRE), опционально Falco (syscall-детект).
  • Версии зафиксированы и в цикле обновления (Falco 0.45.0 raw-bytes breaking change учтён в правилах).

Детект и алерты

  • Снята baseline (2+ недели) перед выставлением порогов.
  • Правила брутфорс/spraying/stuffing описаны как Sigma/в ruleset, с MITRE-ID.
  • 3 уровня эскалации + cooldown + дедуп. Нет «page по одному failed login».
  • Каждое правило имеет назначенного «хозяина» и текст-плейбук, что делать при его срабатывании.

Защита журнала, приватность, время

  • Отдельный раздел/квота, права write-only, не в web-root.
  • Транспортировка по TLS (RFC 5424/5425), верификация источника.
  • Раннее копирование в read-only/immutable, тампер-детект.
  • NTP/chrony везде, контроль clock-skew.
  • Politika retention: не дольше необходимого; описан путь удаления PII при DSAR.

Инструмент под задачу

Соберите из своих nginx- и auth-логов единую картину «кто и откуда стучался»: анализатор вынесет IP, частоту и подозрительные payload и подскажет, что реально стоит закрыть, — вместо ручного чтения access.log. Попробуйте инструмент прямо здесь, на своих данных:

AI Log Analyzer
Гость
Токены: 0 / 50 000
Гостевой лимит — 50 000 токенов. Зарегистрируйтесь — для пользователей лимит + 200 000 токенов.
0 / 20 000 символов
ИИ анализирует логи...

Результат анализа:

Источники

Все версии приведены по релизам GitHub на 12 октября 2026. Перед деплоем правил/конфигов проверьте их на staging: детект-правила и Falco-макросы, помеченные experimental, по умолчанию идут в log-only, а не в page/contain.

👤 Зарегистрируйтесь, чтобы добавить материал в закладки.