Модуль 18. Расширенная безопасность и комплаенс
Это «боевой» набор практик и артефактов, чтобы довести слой витрин на ClickHouse до уровня аудита: классификация PII, маскирование и RLS, retention/TTL, аудит и алерты, SSO/LDAP/OIDC, шифрование в канале и на диске, и — главное — процессы, которые не разваливаются через месяц.
Цель и принципы
Цель: сделать так, чтобы доступ к данным был минимально необходимым, операции — прослеживаемыми, PII — защищена и удаляется/анонимизируется по регламенту, а любые отклонения ловились автоматически.
Принципы:
- Каталог и классы PII (что защищаем и как сильно).
- Изоляция чтения: только через vw_* (семантика), RLS и маскирование.
- Сдерживающие меры: профили/квоты/лимиты, rate-limit и кэш на шлюзе API.
- Retain/Erase: TTL/retention + процедуры обезличивания.
- Аудит: query_log + алерты «массовая выгрузка PII».
- Криптография: TLS повсюду, шифрование «на диске».
- SSO и роли: централизованная аутентификация и RBAC.
Классификация PII (P0/P1/P2) и реестр
Классы (пример):
- P0 — публично/внутри без ограничений: агрегаты, технические справочники.
- P1 — внутренняя служебная: идентификаторы без прямой связи с персоной (например, user_id), служебные поля, обезличенные хэши.
- P2 — персональные/чувствительные: ФИО, email/телефон, адрес, платёжные реквизиты, cookie/GAID/IDFA, IP (в части юрисдикций), а также квази-идентификаторы (дата рождения + пол + регион).
Реестр PII — таблица метаданных с владельцем и политиками:
CREATE TABLE sec_pii_registry ( database String, object String, -- таблица/вьюха/колонка object_type LowCardinality(String), -- table/view/column pii_class LowCardinality(String), -- P0/P1/P2 owner String, -- e-mail/группа retention_days UInt16, -- срок хранения mask_policy LowCardinality(String), -- none/hash/bin/suppress notes String ) ENGINE=MergeTree ORDER BY (database, object);
Практика: заполняем реестр для vw_* и ключевых таблиц. В CI — бот-проверка: обнаружен новый column в SQL → нет записи в реестре → no-merge (см. М17).
Маскирование и RLS: только через vw_*
Маскирование PII (хэш, бининг, suppression)
Хэш (с солью, чтобы не было «словарной атаки»):
CREATE OR REPLACE VIEW db_sem.vw_orders_masked AS
SELECT
day,
shop_id,
-- соль храните в защищённом месте, в SQL подставляйте из settings/секретов
hex(SHA256(concat(customer_email, currentSetting('mask_salt')))) AS email_hash,
NULL AS customer_phone, -- suppression
net_sales
FROM db_sem.vw_orders;
Бининг/огрубление (квази-идентификаторы):
SELECT toStartOfMonth(birth_dt) AS birth_month, -- вместо точной даты multiIf(age<18,'<18', age<=24,'18-24', age<=34,'25-34', age<=44,'35-44', '45+') AS age_band FROM ...
RLS (строковые политики) — аренда/регион/портфель
-- политику лучше вешать на базовые таблицы и/или vw_*
CREATE ROW POLICY rls_tenant ON db_sem.vw_orders
FOR SELECT USING tenant_id = currentSetting('tenant_id');
CREATE ROLE api_reader;
CREATE USER partner_ro IDENTIFIED BY '***';
GRANT api_reader TO partner_ro;
GRANT SELECT ON db_sem.vw_* TO api_reader;
ALTER USER partner_ro SETTINGS tenant_id=101; -- контекст RLS
Правило: BI/API пользователи имеют доступ только к db_sem.vw_*. На «сырые» таблицы права не выдаём.
k-анонимность и защита от деанонимизации
k-анонимность: любая комбинация квази-идентификаторов ({возраст, регион, пол} и т.п.) встречается не реже k раз.
Проверка k-анонимности (пример):
WITH qid AS (
SELECT
multiIf(age<18,'<18', age<=24,'18-24', age<=34,'25-34', age<=44,'35-44','45+') AS age_band,
region,
gender,
count() AS n
FROM vw_users_masked
WHERE day = yesterday()
GROUP BY age_band, region, gender
)
SELECT * FROM qid WHERE n < {k:UInt32}; -- алерт, если есть «узкие» клетки
Митигации:
- Огрубление (биннинг) и сокрытие редких комбинаций (suppression), например: не отдавать группы с n<k.
- Top-coding (всё, что выше порога, сводить в «>N»).
- Ограничители API: минимальное агрегирование (например, не отдавать «сырые» события наружу, только агрегаты).
Retention/TTL и «право на забвение»
TTL/Retention на уровне таблиц
- Удаление строк по истечении срока: table-level TTL DELETE.
- Тиражирование на S3 по сроку давности: TTL … TO VOLUME 'cold'.
ALTER TABLE db_marts.orders MODIFY TTL order_dt + INTERVAL 365 DAY DELETE, -- срок хранения order_dt + INTERVAL 90 DAY TO VOLUME 'warm', order_dt + INTERVAL 365 DAY TO VOLUME 'cold';
Важно: TTL DELETE — полностью удаляет строки. Частично стирать только PII-колонки штатным TTL нельзя — используйте плановые мутации/пересборку с занулением PII.
«Стирание PII» (обезличивание колонки)
Делаем плановую мутацию по партициям: «для записей старше N — занулить/захешировать PII-колонки».
ALTER TABLE db_marts.orders UPDATE customer_email = NULL, customer_phone = NULL WHERE order_dt < today() - INTERVAL 180 DAY;
Практика: запускать ночью, по партициям, с контролем нагрузки (см. М11).
Аудит и алерты: query_log как источник правды
Что логируем
В ClickHouse есть system.query_log / system.query_thread_log. Ключевые поля: event_time, user, address, query, read_rows, read_bytes, result_rows, result_bytes, query_duration_ms, log_comment, query_kind.
Поиск «массовых выгрузок PII»:
WITH pii_cols AS (
SELECT arrayDistinct(groupArray(object)) AS cols
FROM sec_pii_registry
WHERE object_type='column' AND pii_class='P2'
),
queries AS (
SELECT event_time, user, address, query, read_bytes, result_rows, result_bytes, query_duration_ms
FROM system.query_log
WHERE event_time >= now()-INTERVAL 1 HOUR AND type='QueryFinish'
)
SELECT *
FROM queries
WHERE result_bytes > 50*1024*1024 -- > 50MB
AND arrayExists(c -> like(lower(query), concat('%', lower(c), '%')), (SELECT cols FROM pii_cols))
AND user NOT IN ('svc_export_allowed'); -- белый список сервисов
Сигналы на алерт:
- result_bytes/read_bytes сверх порога по пользователю/роле.
- Частые запросы к PII-колонкам за короткий промежуток.
- Нет фильтра по времени/партиции (эвристика по WHERE day BETWEEN…).
Журналы доступа на шлюзе
Если отдаёте API (М15) — в Nginx логируйте X-Request-Id, токен/клиента (хэш), размер ответа, cache-status и коррелируйте с query_id в CH (log_comment).
SSO/LDAP/OIDC и управление доступом
Подходы:
- LDAP (встроенная аутентификация): мэппим группы LDAP → роли CH (RBAC).
- OIDC/SSO через шлюз (Nginx/Envoy/Kong) перед HTTP-интерфейсом CH: шлюз проверяет токен, раскладывает клеймы, подставляет технического пользователя CH + SETTINGS (например, tenant_id, mask_salt).
- SSO для BI: многие коннекторы поддерживают OAuth/OIDC к вашему шлюзу.
Роли и профили:
CREATE ROLE bi_reader, api_reader, data_steward, sec_admin; CREATE SETTINGS PROFILE api_limits SET max_execution_time=2, max_memory_usage='4G', max_rows_to_read=5e7, max_result_rows=1e6, result_overflow_mode='break'; ALTER USER api_ro SETTINGS PROFILE api_limits, tenant_id=101; GRANT SELECT ON db_sem.vw_* TO api_reader;
Шифрование: в канале и на диске
В канале
- TLS для HTTP/Native протокола (клиент↔сервер).
- interserver HTTPS между репликами (для fetch/replication).
- Для S3 — включить SSE (KMS).
На диске
- Шифрование томов (dm-crypt/LUKS, cloud-managed encryption) — базовая линия.
- Encrypted Disk в ClickHouse (политика хранения): быстрый «горячий» NVMe без шифрования на уровне CH можно закрыть томовым шифрованием; холодные тома/S3 — с SSE.
- Ключи храните в KMS; регламент ротации ключей.
Замечание: шифрование «на диске» не заменяет RLS/маскирование — это защита от «физического» доступа/утилизации носителей.
Процессы и регламенты (артефакты)
- Политика PII: классы (P0/P1/P2), владелец, retention, маскирование, обработка запросов субъектов (GDPR/152-ФЗ).
- Процедура доступа: запрос → обоснование → временная роль → аудит.
- DR и инциденты: как отзывать креденшелы/токены, как «заморозить» пользователей, как локализовать утечку (блок шлюза + revoke).
- Регламент TTL/retention: кто и когда запускает мутации «обнуления PII», как контролируем успех.
- Аудит отчёт (квартальный): выполненные запросы к PII, срез по ролям, инциденты, тесты «к-критериев».
Практика: внедряем пошагово
Внедрить PII-реестр
- Инвентаризация колонок в vw_* и ключевых таблицах.
- Заносим в sec_pii_registry, назначаем owner, retention_days, mask_policy.
- Включаем бот-проверку на PR (см. М17).
Включить RLS/маскирование
- Создаём маскированные vw_* для BI/API; оригинальные vw_* — только для доверенных ролей.
- Политика RLS по арендаторам/регионам.
- Профили/квоты для внешних пользователей и API.
Алерты «массовая выгрузка PII»
- Ежечасно гоняем запросы по system.query_log (см. §5.1).
- Интеграция с алертингом: Slack/Email/PagerDuty.
- Runbook «как действовать» (заморозка пользователя, расследование).
Кейсы
Кейс A. «Партнёр скачал 500МБ PII за 2 минуты»
- Симптомы: алерт по result_bytes; несколько вызовов /api/v1/orders без агрегации.
- Действия: мгновенный block токена через шлюз; revoke пользователя; анализ query_log по query_id; отчёт и ретро-ограничение эндпоинта (теперь только агрегаты или Parquet-экспорт раз в час по подписке).
Кейс B. «Деанонимизация через редкие сочетания»
- Симптомы: партнёр жалуется «узнаёт пользователя» по возраст+регион+редкая покупка.
- Фикс: увеличили k до 20; ввели suppression для клеток <k; огрубили возраст до 5 бинов; в API — минимальное агрегирование (день/регион, не «секунда/SKU»).
Кейс C. «Retention не сработал»
- Симптомы: аудит нашёл PII старше 1 года.
- Разбор: TTL DELETE стоял на новом кластере, старые партиции мигрировали без правила.
- Фикс: плановая мутация очистки PII-колонок; тест на наличие «старых» строк в CI; отчёт аудита.
Чек-лист публикации (безопасность/комплаенс)
- BI/API читают только db_sem.vw_*.
- RLS включён (tenant/region), контекст прокидывается (setting tenant_id).
- Маскирование PII в vw_* по политике (hash/bin/suppress).
- PII-реестр заполнен: владелец, retention, mask_policy.
- TTL/retention настроен (DELETE/TO VOLUME), есть ночные мутации для «обнуления PII».
- Профили/квоты применены (max_execution_time/memory/result_rows).
- TLS включён; interserver — HTTPS; S3 — SSE/KMS.
- Аудит: query_log хранится ≥ N дней; алерты на большие выгрузки.
- SSO/LDAP/OIDC подключён; роли маппятся; offboarding автоматизирован.
- Документы: политика PII, регламент retention, runbooks инцидентов/DR.
Риски и митигации (сводно)
|
Риск |
Проявление |
Митигация |
|---|---|---|
|
Деанонимизация редкими сочетаниями |
Пользователя «узнают» |
Бининг/огрубление, suppression <k, top-coding, минимальная агрегированность |
|
SQL-инъекции через API |
Странные запросы, утечки |
Только параметризация {param:Type}, whitelist параметров на шлюзе |
|
FULL SCAN PII |
Высокий read_bytes, p95 |
Обязательные фильтры по времени/тенанту, лимиты окна, линтеры/гвардейлы |
|
«Сырые» таблицы в BI |
Обход маскирования |
BI-ролям запрет на db_marts.*, только db_sem.vw_* |
|
Невыполненный retention |
Истёкшие PII остаются |
Table TTL DELETE + ночные мутации обнуления PII-колонок, аудит |
|
Утечка через кэш |
A видит данные B |
Ключ кэша включает auth-идентификатор; приватное и публичное — раздельно |
|
Слабая криптография |
MITM/сниффинг |
TLS везде, interserver HTTPS, управляемые ключи/KMS, регламенты ротации |
|
«Сироты» без владельца |
Некому чинить |
no-owner → no-merge (CI), RACI, каталог с владельцами (М17) |
Артефакты модуля
- Регламенты: Политика PII (классы, retention, маскирование), Процедура доступа/аудита, DR-инциденты.
- Политики: роли/профили/квоты (SQL), RLS (ROW POLICY), шифрование (инструкции).
- Отчёт аудита: выгрузки query_log с отчётами по PII, алерты и результаты DQ.
- Шаблоны: sec_pii_registry, маскированные vw_*, запросы на k-анонимность, алерты «mass export».
Итог
Безопасность витрин — это система, а не «маска однажды».
Сочетание каталога и классов PII, маскированных vw_* + RLS, TTL/retention, аудита с алертами, SSO/RBAC и криптографии обеспечивает проверяемую защиту «до аудита и после».
Arenadata QuickMarts (ADQM) — корпоративная платформа на базе ClickHouse для быстрого слоя витрин и near-real-time аналитики. Решает задачи «быстрых» дашбордов и API с низкой латентностью и высокой конкуррентностью, работает поверх вашего DWH/лейкхауса как serving-уровень. Даёт предсказуемую производительность на терабайтно-петабайтных объёмах за счёт колоночного хранения, компрессии и предагрегатов (Materialized Views, AggregatingMergeTree), подключается к Kafka/S3 и стандартным BI-инструментам по SQL/HTTP. Для корпоративных ИТ ADQM предлагает поддержку и SLA, отказоустойчивые кластеры (HA/DR), безопасность (RBAC, LDAP/OIDC, шифрование трафика и данных), мониторинг и резервное копирование. Платформа хорошо ложится на методологию курса: семантика vw_*, роллап-слои, NRT-ингест, SLO/наблюдаемость и «гвардейки» для BI/API. Итог — быстрый запуск витрин за недели, снижённые риски в проде и предсказуемая стоимость владения.



