Buff Development
Журнал аудита

Выгрузка и целостность

Экспорт журнала в CSV и JSON Lines по текущему фильтру, формат файла и проверка цепочки хэшей своими средствами.

Журнал выгружается кнопками «Экспорт CSV» и «Экспорт JSON» в разделе. Выгружается всё, что подходит под текущие фильтры, — без ограничения объёма: сервис проходит журнал страница за страницей до конца, а не отдаёт «первые 10 000 строк». Файл собирается в браузере и скачивается сразу; никаких ссылок «действует 24 часа» и писем на почту.

Сама выгрузка — тоже событие: в журнале появляется auditlog.export с форматом, фильтрами и числом строк.

Раскрытая запись: все поля, детали в JSON, хэш записи и хэш предыдущей. Сверху — результат «Проверить целостность».

CSV

Для таблиц и отчётов. Кодировка UTF-8 с BOM (Excel открывает кириллицу без настроек), разделитель — запятая, строки — от старой к новой. Столбцы:

СтолбецСодержимое
IDномер записи
Время (UTC)ISO 8601 с микросекундами, 2026-09-06T01:22:33.482911+00:00
Время (местное)то же в вашем часовом поясе — для чтения
Организация, Проектгде произошло
Вид исполнителя, Исполнительучастник / коннектор / оператор платформы / система; логин или идентификатор
Сессияидентификатор сессии участника
IP, Браузерадрес и User-Agent подключения
Действие, Названиекод события и его описание из каталога
Операция, Важностькласс действия и важность
Тип объекта, Объектна что направлено действие
Описаниестрока, которую видит человек
Результат, Причина отказауспех / отказ и причина
Детали (JSON)структурированные параметры
Хэш предыдущей записи, Хэш записизвенья цепочки целостности

JSON Lines

Для SIEM, скриптов и архивов: одна запись — одна строка JSON, ровно в том виде, как её хранит сервер, от старой к новой. Расширение файла — .jsonl. Поля:

{"id":12,"at":"2026-09-06T01:22:34.101532+00:00","org":"buff-demo","project":null,
 "actor_kind":"user","actor":"demo-lead","session":"c2a5…","gid":"9821…","ip":"147.45.197.19",
 "user_agent":"Mozilla/5.0 …","action":"auditlog.export","operation":"access","severity":"notice",
 "target_kind":null,"target":null,"summary":"Выгружен журнал аудита (json)",
 "detail":{"op":"auditlog_list","format":"json","filters":{},"rows":9},"ok":true,"error":null,
 "prev_hash":"e5ac…","hash":"81df…"}

actor_kind принимает значения user, connector, operator, system; severityinfo, notice, warning, critical; operationcreate, access, modify, remove, authentication, transfer, execute.

Целостность

Записи каждой организации образуют цепочку: хэш записи считается от хэша предыдущей записи и содержимого текущей (SHA-256). Изменение, вставка или удаление любой записи в середине разрывает цепочку — все последующие хэши перестают сходиться.

В приложении. Кнопка «Проверить целостность» пересчитывает цепочку на сервере и отвечает: сколько записей проверено и цела ли цепочка; если нет — номер первой записи, которая не сходится. Проверка тоже записывается в журнал (auditlog.verify).

Своими средствами. Выгруженный JSON Lines содержит всё, что нужно, чтобы проверить цепочку без нас. Хэш записи считается так:

  1. Взять поля записи at, org, project, actor_kind, actor, session, ip, user_agent, action, operation, severity, target_kind, target, summary, detail, ok, errordetail — объект как есть).
  2. Сериализовать их в JSON с ключами по алфавиту, без пробелов, без экранирования не-ASCII (ensure_ascii=False в Python).
  3. Хэш = SHA-256 от строки prev_hash + "\n" + сериализация (для первой записи организации prev_hash пустой).

Скрипт проверки для файла audit-log.jsonl:

import hashlib, json

FIELDS = ["at", "org", "project", "actor_kind", "actor", "session", "ip", "user_agent",
          "action", "operation", "severity", "target_kind", "target", "summary", "detail",
          "ok", "error"]

prev = None
for line in open("audit-log.jsonl", encoding="utf-8"):
    e = json.loads(line)
    body = json.dumps({k: e[k] for k in FIELDS}, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
    h = hashlib.sha256(((prev or "") + "\n" + body).encode("utf-8")).hexdigest()
    if e["prev_hash"] != prev or e["hash"] != h:
        raise SystemExit(f"цепочка нарушена на записи #{e['id']}")
    prev = e["hash"]
print("цепочка цела, последний хэш", prev)

Скрипт ожидает полную выгрузку организации от первой записи («Всё время», без фильтров): цепочка идёт по всем записям подряд, и выгрузка с фильтром проверяется только начиная с той записи, чей prev_hash вам известен. Храните последний хэш каждой выгрузки: следующая выгрузка должна на него опираться — так вы убедитесь, что между ними ничего не исчезло.

Что стоит знать

  • Адрес — как его увидел периметр. IP берётся из соединения с нашим сервером (заголовок пограничного узла), не со слов клиента. Пользователь за корпоративным прокси или VPN будет виден адресом этого прокси.
  • Часовой пояс. В файле время в UTC; в интерфейсе — в часовом поясе вашего браузера. Сравнивайте по столбцу «Время (UTC)».
  • Объём. Выгрузка идёт страницами по 500 записей до конца; для журнала в десятки тысяч записей это несколько секунд. Кнопка блокируется на время выгрузки, по завершении показывается число строк.

On this page