Документация

К списку статей
02. Персональные данные, ФЗ-152 и Identity Vault

Аудитория: DPO (ответственный за ПДн), ИБ-служба, юрист, суперадминистратор. Закрывает вопросы: №2 («что вы делаете с ПДн сотрудников?»), №4 («это не слежка?»). Сценарии: S2 — сотрудник требует показать/удалить свои данные; защита перед проверкой.


Короткий ответ

В рабочих таблицах «Лицензиара» нет ФИО и табельных номеров — только непрозрачные идентификаторы hex(64). Связь «идентификатор ↔ человек» хранится в отдельном изолированном сейфе (Identity Vault), который виден только роли DPO и только при явном раскрытии по основанию (номер приказа). Каждое раскрытие пишется в аудит-журнал ФЗ-152. Поэтому продукт — это аудит лицензий и эффективности ПО, а не слежка за людьми: он оперирует обезличенными агрегатами, а персонификация — исключение, протоколируемое и обоснованное.


Поток персональных данных (где сырьё, где обезличенное)
источник (KSC/1С/syslog)            ПРИЁМ (api)                      БД
┌──────────────────┐  сырой табельный  ┌──────────────────┐  obezlich.  ┌─────────────────┐
│ tabel_no=«12345» │ ───────────────►  │ anonymize(tenant,│ ──────────► │ user_id = a3f9… │
│ raw_product=…    │   по сети,        │   tabel_no)      │  hex(64)    │ (ФИО/табельных  │
└──────────────────┘   только транзит  │  Стрибог-256     │             │   в фактах НЕТ) │
                                        └──────────────────┘             └─────────────────┘
            СЫРЬЁ НИГДЕ НЕ ХРАНИТСЯ ─────────────┘                              │
                                                                                ▼
                                              ┌───────────────────────────────────────────┐
                                              │ vault.user_identity  (изолят, RLS dpo-only) │
                                              │ user_id hex(64) ↔ табельный/ФИО             │
                                              └───────────────────────────────────────────┘

Ключевой принцип: сырой табельный номер существует только в момент приёма и сразу превращается в user_id. Обезличивает именно приём (функция ingest_activity), а не коллектор: по сети сырьё проходит транзитом, в таблицу фактов sam.activity_facts попадает только hex(64). То же касается «сырого имени ПО» (raw_product) — оно используется для распознавания системы и не сохраняется.

Обезличивание (псевдонимизация по ст. 3 п. 9 ФЗ-152)
  • Алгоритм — ГОСТ Р 34.11-2012 «Стрибог-256». Это не СКЗИ (см. статью 01).
  • Вход: ANON_SALT | tenant_id | табельный → выход hex(64). Соль из env, не в БД и не в логах.
  • Изоляция арендаторов встроена в id: tenant_id входит в хеш, поэтому один и тот же

табельный у разных арендаторов даёт разные идентификаторы — пространства не пересекаются.

Identity Vault — сейф соответствия
Точная позиция перед аттестатором. Механизм обратим при наличии сейфа — это псевдонимизация (ст. 3 п. 9), поэтому система в целом остаётся ИСПДн, а сейф — её носитель ПДн. Операционная же плоскость (sam) обезличена для всех без доступа к сейфу. Заявление «портал вообще не ИСПДн» — некорректно и не делается. Сегодня сейф — это логический изолят (отдельная схема + RLS в той же СУБД); физический вынос сейфа в отдельный сервис — запланированное усиление границы аттестации.

Сейф — это отдельная схема `vault` (таблица vault.user_identity), заводимая первой же миграцией схемы БД и доступная только через серверный слой под RLS-политикой.

  • Хранит связь user_id (hex64) ↔ табельный/ФИО в отдельной схеме `vault`, а не в sam.
  • RLS-политика `vault_dpo_only`: строки видны только при роли dpo. Любая другая роль

(включая viewer, it_admin, superadmin) видит в сейфе 0 строк — это проверяется тестом приёмки.

  • Раскрытие — POST /api/{tenant}/vault/resolve с обязательным полем basis (например,

номер и дата приказа/служебной записки). Без основания раскрытия нет.

Аудит-журнал ФЗ-152 (каждое раскрытие протоколируется)

Каждый вызов vault/resolve пишет запись `VAULT_RESOLVE` в аудит-лог: кто (роль/логин), когда, по какому основанию. Журнал виден на экране /audit (роль dpo/superadmin). Это и есть доказательство для проверки: персонификация всегда оставляет след с обоснованием.


Что отвечать в типовых ситуациях

S2a. Сотрудник: «покажите, какие мои данные вы собираете». В фактах активности его данные представлены непрозрачным user_id; ФИО/табельный — в сейфе Vault. DPO раскрывает соответствие по основанию (приказ/запрос субъекта ПДн) через vault/resolve; факт раскрытия фиксируется в аудите. Сотруднику предъявляются его агрегаты активности (дни/транзакции по системам), а не «слежка».

S2b. Сотрудник: «удалите мои данные». Удаляется связь в vault.user_identity (соответствие id↔человек). После этого обезличенные факты перестают быть персональными данными — субъект по ним не определяется. Операция — ответственность DPO, под основание и запись в аудит.

S2c. Профсоюз/работник: «это система слежки за людьми?». Нет. Продукт считает утилизацию лицензий и эффективность ПО на обезличенных агрегатах. Он не пишет содержимое экранов/нажатия/переписку — только факт «в системе X в день D было N транзакций и M минут сессии». Персонификация технически отделена, ограничена ролью DPO и требует основания.

Что показать проверяющему
  1. vault.user_identity под ролью viewer0 строк (RLS-политика vault_dpo_only;

проверяется тестом приёмки «viewer видит 0»).

  1. Пример записи VAULT_RESOLVE на экране /audit с заполненным basis.
  2. Исходник приёма: в таблицу фактов пишется только user_id = anonymize(...),

сырой табельный никуда не сохраняется.

Исходник приёма телеметрии: обезличивание на месте, сырьё не хранится
for rec in records:
    sys_id = code_to_id.get(rec.sys_code)
    if sys_id is None:
        # Фолбэк: распознаём систему по сырому имени ПО (Recognition Parser).
        code = recognition.recognize(getattr(rec, "raw_product", "") or "")
        sys_id = code_to_id.get(code) if code else None
        ...
    user_id = anonymize(tenant_id, rec.tabel_no)   # <- сырой табельный НЕ хранится
    cur.execute(
        """INSERT INTO sam.activity_facts
             (tenant_id, sys_id, user_id, activity_date, tx_count, session_minutes, is_active_day)
           VALUES (%s,%s,%s,%s,%s,%s,%s)
           ON CONFLICT (tenant_id, sys_id, user_id, activity_date) DO UPDATE ...""",
        (tenant_id, sys_id, user_id, rec.activity_date, rec.tx_count, rec.session_minutes, is_active),
    )

В INSERT уходит user_id (hex64), а rec.tabel_no и rec.raw_product существуют только как локальные переменные на время обработки записи — в таблицу они не попадают.

  1. Заготовка заключения для аттестатора ИСПДн — готовый пакет фактов о защите ПДн

(его подписывает аттестатор/орган заказчика).

Фрагмент заключения: защита ПДн и журналирование (ФЗ-152)
ТребованиеРеализацияДоказательство
Связь идентификатор↔субъект изолированаIdentity Vault в отдельной схеме, доступ только роли DPORLS vault_dpo_only, тест test_vault_blocked_for_non_dpo
Раскрытие — только по основанию, с фиксациейрезолв идентичности пишет запись VAULT_RESOLVE с номером приказатест test_vault_resolve_dpo_writes_audit
Журнал доступа (ФЗ-152)партиционированная таблица аудита действий fz152_auditпроверка наличия и заполнения журнала
Изоляция данных арендаторовRLS tenant_isolation (FORCE) на всех таблицах; роль приложения — не суперюзертесты test_rggu_isolated_from_artek, test_rls_tenant_isolation
Грабли
  • Vault-операции требуют одинаковую `ANON_SALT` на приёме и при сопоставлении. Если соль

на сиде/приёме отличается от текущей — посчитанные user_id не совпадут с теми, что в сейфе, и raw→id «не найдётся» (на стенде поэтому везде ANON_SALT=test-salt). Это не дефект, а следствие того, что соль — часть личности инсталляции.

  • Раскрытие без `basis` невозможно — это by design. Не обходите: запись в аудит и

основание и есть ваша защита при проверке ФЗ-152.

  • demo_public — публичный обезличенный арендатор. Там нет реальных людей и он доступен

только на чтение; не используйте его как площадку для реальных ПДн.


Связанные статьи: 01 — Не СКЗИ · 04 — Механизм сбора · 07 — Роли и доступ.