Журнал действий пользователей ведут так: сначала записывают вопросы, на которые он должен отвечать, затем делают запись в одном месте кода, при успехе и при отказе, хранят её отдельно от рабочих данных без права правки и назначают человека, который журнал читает. Журнал ошибок для этого не годится: он отвечает на вопрос «что сломалось», а не «кто что сделал».
Что именно писать и чего писать нельзя, если в системе есть персональные данные, разобрано в статье «Защита персональных данных в веб-приложении». Здесь о другом: как построить сам журнал, чтобы он выдержал расследование. Описание услуги — на странице «Логирование и аудит действий пользователей».
Три журнала, которые путают
В приложении обычно уже есть «логи», и на этом основании журнал действий считают сделанным. На деле это три разных вещи с разными читателями, сроками и правами доступа.
| Журнал | Для кого | Что внутри | Срок и доступ |
|---|---|---|---|
| Технический | разработчик | Ошибки, предупреждения, время ответов, трассировка запроса | Короткий срок; доступ у команды разработки; данные пользователей маскируются |
| История объекта | пользователь и менеджер | Что менялось в заказе, договоре, карточке: «статус изменён», «сумма изменена», автор и время | Живёт столько же, сколько объект; показывается в интерфейсе тем, кому разрешено |
| Журнал аудита | владелец системы, безопасность, внешний проверяющий | Входы, смена прав, чтение защищённых данных, выгрузки, удаления, действия поддержки, отказы в доступе | Дольше технического; доступ у узкого круга; правка записей закрыта |
Смешивать их опасно по двум причинам. Технический журнал чистят и маскируют, а журнал аудита не должен терять записи. И технический журнал читают все разработчики, поэтому записи из аудита, попавшие в него, видят лишние люди. Связь между журналами одна: сквозной номер запроса, по нему из записи аудита можно перейти к технической трассе, если нужны подробности.
С чего начать: шесть вопросов, на которые журнал должен отвечать
Журнал без вопросов превращается в свалку событий, где нужное не находится. Начните со списка, который можно показать владельцу системы и подписать:
- Кто менял условия договора или цену? Нужны автор, время, объект и значения до и после.
- Кто выгрузил список клиентов? Нужны автор, фильтр, число записей и способ получения файла.
- Откуда заходил пользователь, чей доступ использовали? Нужны адрес, устройство, способ входа и неудачные попытки до успеха.
- Почему у сотрудника есть право, которого быть не должно? Нужны смена ролей: кто выдал, когда, по какой заявке.
- Что сотрудник поддержки делал от имени клиента? Нужны сам факт входа, причина, перечень открытых разделов.
- Что показать клиенту или проверяющему за квартал? Нужен отчёт по организации или пользователю за период.
У каждого вопроса есть свой набор событий. Если вопрос нельзя сформулировать, событие в журнал не идёт: лишние записи увеличивают объём и затрудняют поиск нужных.
Из чего состоит запись
Запись должна быть самодостаточной: через год, когда пользователь уволен, а справочник изменился, по ней должно быть понятно, что произошло. Достаточно девяти полей.
| Поле | Пример | Ловушка |
|---|---|---|
| Время | момент события в единой временной зоне | Разные часы на серверах: события выстраиваются в неверном порядке |
| Субъект | идентификатор пользователя и организации | Запись по имени или почте: после смены имени связь теряется |
| От чьего имени | сотрудник поддержки действует как клиент | Записан только клиент, и поддержка остаётся невидимой |
| Действие | код из справочника: заказ.цена.изменение | Свободный текст: поиск и отчёты невозможны |
| Объект | тип и идентификатор: заказ, номер | Название вместо идентификатора, которое потом меняется |
| Результат | успех, отказ в доступе, ошибка | Пишут только успехи, и попытки обхода не видны |
| Источник | адрес, приложение, версия клиента | Адрес прокси вместо адреса пользователя |
| Изменение | какие поля и значения до и после для обычных данных | Значения чувствительных полей попадают в журнал, и он становится утечкой |
| Сквозной номер | идентификатор запроса | Нет связи с технической трассой, подробности приходится искать вручную |
Справочник действий важнее, чем кажется. Он превращает журнал из текста в данные: «все выгрузки за месяц» или «все смены прав у этой роли» становятся запросом, а не чтением длинных списков. Справочник ведут как код: новое событие появляется вместе с функцией, которая его порождает, и проходит ревью.
Где в коде писать и что делать, если запись не удалась
Самая частая ошибка — раскидать вызовы журнала по обработчикам запросов. Часть операций забывают, часть дублируют, а при переносе функции запись теряется. Надёжнее выбрать одно из мест.
| Место | Что ловит | Что пропускает | Когда выбирать |
|---|---|---|---|
| Слой бизнес-операций | Смысловые события: «согласовано», «цена изменена», «роль выдана» | Прямые правки в базе в обход приложения | Основной вариант для нового приложения |
| Промежуточный слой запросов | Чтение защищённых страниц, отказы в доступе, входы | Смысл действия: видно «PUT на адрес», но не «что изменили» | Дополнение к слою операций для событий доступа |
| Триггеры базы данных | Любое изменение таблицы, в том числе из скрипта | Кто из пользователей приложения это сделал, если не передавать его в сеанс | Старые системы без возможности менять код; защита от правок в обход |
В одной транзакции с операцией или отдельно
Если запись об изменении цены сохраняется в одной транзакции с самим изменением, они появятся вместе или не появятся вообще. Цена — задержка на запись и риск: сбой журнала останавливает операцию. Для событий с последствиями (деньги, права, удаление, выгрузка) это нужная цена: лучше отказать, чем сделать невидимо. Для менее важных событий, например просмотра справочника, допустима очередь: запись уходит в журнал асинхронно, а при остановке очереди события копятся в промежуточном хранилище, и потеря допускается только в заранее оговорённом объёме.
Решение «важное событие — в транзакции, остальное — через очередь» записывается в документе и проверяется тестом: отключаем хранилище журнала и смотрим, что делает приложение. Ответ «ничего не заметило» означает, что события теряются молча.
Как защитить журнал от правки
Журнал, который можно поправить, ничего не доказывает. Защита строится слоями, и каждый слой закрывает свою угрозу.
- Отдельная учётная запись для записи. У приложения право только добавлять строки в таблицу журнала, без обновления и удаления. Это закрывает правку через ошибку в приложении.
- Отдельное хранилище. Журнал лежит вне основной базы, с отдельным доступом. Читать его может узкий круг, и это не те, чьи действия записаны.
- Копия вовне по расписанию. Записи регулярно выгружаются в хранилище, где их нельзя перезаписать. Если оригинал изменили, расхождение видно.
- Цепочка контрольных значений. Каждая запись содержит контрольное значение предыдущей, и подмена записи в середине ломает цепочку. Это защищает от тихой правки, но не от удаления последних записей, поэтому дополняет копию вовне, а не заменяет её.
- Единое время. Часы серверов синхронизируются с одним источником, иначе порядок событий оспорим.
Честная граница: от администратора базы данных с полным доступом журнал, лежащий в той же базе, не защищает. Поэтому в чувствительных системах журнал выносят в хранилище, администратор которого не совпадает с администратором основной базы. Защита журнала не заменяет разграничение прав в самом приложении, о котором см. статью о технических мерах защиты.
Как читать журнал, чтобы он не лежал мёртвым грузом
Запись, которую никто не открывает, имеет ценность только после происшествия, когда читать приходится в спешке. Заранее делаются три способа чтения.
| Способ чтения | Кто пользуется | Как устроено | Пример |
|---|---|---|---|
| История объекта | менеджер, руководитель | Вкладка в карточке заказа или договора со списком изменений | «Кто и когда изменил срок поставки» |
| Расследование | ответственный за безопасность | Поиск по пользователю, действию, объекту и периоду, выгрузка результата | «Что делал этот логин в день инцидента» |
| Оповещение | ответственный за безопасность | Правило на порог: массовая выгрузка, много отказов подряд, выдача административной роли | Письмо или сообщение в рабочий чат |
| Просмотр клиентом | администратор организации-клиента | Раздел «Журнал активности» с фильтром по своим сотрудникам | «Кто из наших сотрудников скачал документы» |
Объём и срок хранения
Журнал растёт быстрее остальных таблиц, поэтому с первого дня его делят по периодам: свежие записи лежат там, где поиск быстрый, старые уходят в архив и читаются медленнее. Срок хранения записывается в регламенте, и удаление по сроку тоже оставляет запись. Срок определяют требования ваших клиентов и юриста: мы называем технический вариант, а не нормативное значение.
Приёмка и типичные ошибки
Журнал проверяют так же, как права: не по наличию, а по поведению. Восемь проверок, которые стоит пройти до запуска и повторять после крупных изменений.
- Выполнить каждое действие из справочника и найти его запись по номеру запроса.
- Выполнить действие с отказом (чужой объект, нет роли) и убедиться, что отказ записан.
- Попробовать изменить и удалить запись под учётной записью приложения: операция должна быть отклонена.
- Отключить хранилище журнала: важные операции останавливаются, остальные копят события в очереди.
- Проверить, что в записях нет паролей, токенов, содержимого форм и значений чувствительных полей.
- Войти от имени клиента из-под поддержки: в записи видны оба — клиент и сотрудник.
- Ответить на каждый из шести вопросов, заданных в начале, только по журналу, не открывая код.
- Восстановить журнал из копии и сравнить с оригиналом.
Типичные ошибки видны по этому списку: пишут только успехи, хранят свободный текст, вызывают журнал из обработчиков в сотне мест, держат журнал рядом с рабочими данными под тем же администратором, никогда не открывают его до инцидента. Последнее самое дорогое: первое чтение в день происшествия показывает, что нужного поля нет.
Что делаем мы и с чего начать
Начинаем с обследования: 200 000 ₽ и 2–3 недели за одно-два приложения, 350 000 ₽ и 3–4 недели, если систем несколько. Записываем вопросы к журналу, список событий и справочник действий, проверяем, что уже пишется и где, выбираем место записи и хранилище. Обследование вычитается из первого этапа, если продолжаете с нами, а список вопросов и справочником пользуются и без нас.
Дальше лёгкий пилот (150 000–400 000 ₽) добавляет журнал к одному приложению: запись в сервисном слое, отдельное хранение, поиск и просмотр по объекту. Сопровождение стоит 20 000 / 60 000 / 120 000 ₽ в месяц. Мы не поставляем SIEM, не дежурим круглосуточно, не проводим аттестацию, лицензии ФСТЭК у нас нет; сроки хранения и состав обязательных записей определяет юрист заказчика. Код и журнал остаются в вашем репозитории и на ваших серверах. Цены и сроки — на странице «Тарифы», описание услуги — на странице «Логирование и аудит действий пользователей».
Частые вопросы
Как вести журнал действий пользователей в простом приложении?
Одна таблица и один сервис записи: время, пользователь, действие из справочника, объект, результат, адрес и номер запроса. Запись вставляется в той же транзакции, что и операция, а у приложения нет права её менять. Этого достаточно для небольшой системы; остальное добавляется по мере роста требований.
Чем журнал аудита отличается от журнала ошибок?
Журнал ошибок отвечает на вопрос, что сломалось, и нужен разработчику. Журнал аудита отвечает на вопрос, кто что сделал, и нужен владельцу системы и проверяющему. У них разные сроки хранения, доступ и требования к сохранности.
Можно ли хранить журнал в той же базе, что и данные?
Можно на старте, если у приложения есть право только вставки и доступ к таблице у узкого круга. В чувствительных системах журнал выносят в отдельное хранилище с другим администратором, иначе тот, кто управляет базой, может изменить и записи о своих действиях.
Нужно ли писать в журнал просмотры?
Только просмотры защищённых данных: карточка человека с персональными данными, документы, финансовые показатели. Запись каждого открытия справочника увеличивает объём и скрывает нужное. Список просматриваемых объектов определяется вопросами к журналу.
Что делать, если запись в журнал не удалась?
Для операций с последствиями (деньги, права, удаление, выгрузка) операция отклоняется: невидимое действие хуже недоступной функции. Для остальных событий допустима очередь с промежуточным хранилищем и заранее оговорённым объёмом возможной потери.
Помогает ли цепочка контрольных значений?
Она делает тихую подмену записи в середине журнала заметной. Но она не защищает от удаления последних записей и не заменяет отдельное хранилище и копию вовне. Это дополнительный слой, а не основная защита.
Как показать журнал клиенту в B2B-сервисе?
Отдельным разделом «Журнал активности» для администратора организации: фильтр по сотрудникам, действиям и периоду, выгрузка за период. Важно, чтобы клиент видел только записи своей организации, и это проверяется отдельным тестом изоляции.
Сколько стоит добавить журнал аудита?
Обследование стоит 200 000 ₽ (350 000 ₽ для нескольких систем) и вычитается из первого этапа, если продолжаете с нами. Лёгкий пилот на одном приложении — 150 000–400 000 ₽. Точная цена зависит от числа событий, места записи и требований к хранению.