Ограничения: юрисдикции, экспорт, персональные данные
Команда из шести человек, продукт работает, первые платящие клиенты есть. За одну неделю приходят четыре письма, и ни одно из них не про баги.
Первое — от отдела закупок крупного клиента: «подтвердите, что данные наших сотрудников не покидают территорию страны». Второе — автоматическое, от магазина приложений: сборка не принята, не заполнена декларация об использовании шифрования. Третье — от человека, который полгода назад удалил аккаунт: он требует подтвердить, что его данные стёрты, и упоминает срок в календарных днях. Четвёртое — от платёжного провайдера: выплаты по одному направлению приостановлены до выяснения.
Каждое письмо в отдельности выглядит как бумажная формальность. Вместе они означают одно: продукт технически готов и всё равно не может быть поставлен четырём разным покупателям по четырём разным причинам, и ни одну из этих причин нельзя починить хотфиксом в пятницу.
Ограничение — это условие, при котором работающий продукт нельзя поставить конкретному покупателю. Оно приходит не из вашего кода, но живёт в вашем коде. И в отличие от бага, его нельзя обнаружить в проде и исправить в следующем релизе: к моменту обнаружения обязательство уже возникло, а сделка уже сорвана.
Это последняя глава трека, и она собирает то, что в предыдущих главах всплывало на полях: региональные стеки в многотенантности, налоги и валюты в биллинге, анкеты площадок в магазинах приложений, возрастные рейтинги в дистрибуции игр, обязательства лицензий в главе про лицензии. Здесь мы разбираем не сами нормы — это работа юриста, — а механизм: как внешнее ограничение превращается в структуру данных, ветку кода, артефакт и срок.
Чем ограничение не является
Три подмены, из-за которых тема годами лежит в тумбочке. Это не безопасность. Безопасность отвечает на вопрос «что сделает злоумышленник». Соответствие требованиям отвечает на вопрос «что вы покажете третьей стороне через два года». Пересечение большое, но не совпадение: система может быть отлично защищена и полностью непригодна для продажи в регулируемый сектор, потому что у неё нет неизменяемого журнала доступа. И наоборот — можно иметь стопку сертификатов при дырявой авторизации. Инженерная часть безопасности разобрана в треке security, приватность с технической стороны — в отдельной главе.
Это не право. Юрист говорит, какая норма применима. Инженер говорит, каким механизмом она исполняется и чем это доказывается. Ни один из двоих не может сделать работу другого. Ошибка в обе стороны одинаково дорога: инженер, который решил «мы, наверное, подпадаем под исключение», и юрист, который написал «система должна обеспечить удаление данных» без понимания, что бэкапы восстанавливаются целиком.
Это не «документы». Политика обработки данных — это не решение, а описание того, что уже решено. Если политика написана раньше, чем сделан механизм, вы просто пообещали то, что не делаете. Живой пример на этом портале: раздел /legal — оферта, политика обработки персональных данных, согласие, правила возврата. Это не декоративные страницы, а формулировки обязательств, каждое из которых обязано иметь исполнителя в коде.
Четыре формы, в которые превращается любое ограничение
Практическая находка, которая экономит месяцы: какое бы ограничение вам ни назвали, в системе оно принимает одну из четырёх форм — или комбинацию.
- Атрибут. У какой-то сущности появляется поле: страна субъекта, класс данных, режим тенанта, возрастная категория, признак организации-покупателя. Атрибут можно отфильтровать, посчитать и проверить тестом.
- Ветка. Перед действием появляется проверка, которая может сказать «нет»: не выдать право, не принять регистрацию, не отправить письмо, не показать функцию.
- Доказательство. Действие оставляет запись, пригодную для показа третьей стороне: журнал, отчёт, подписанный артефакт, сохранённый ответ внешнего сервиса.
- Срок. Обязательство привязано ко времени: хранить столько-то лет, ответить за столько-то дней, пересмотреть раз в год, уведомить в течение стольких-то часов.
Отсюда рабочий приём: если требование не удаётся выразить ни в одной из четырёх форм, вы его ещё не поняли — и это повод вернуться к юристу с конкретным вопросом, а не с просьбой «объяснить закон». Вопрос звучит так: «какое поле мы должны хранить, какое действие обязаны запретить, что предъявить проверяющему и в какой срок».
от юриста, клиента,
площадки, регулятора"] --> Q{"В какую форму
оно ложится?"} Q -->|"нужно различать случаи"| A["Атрибут
поле в модели данных"] Q -->|"что-то нельзя делать"| B["Ветка
проверка перед действием"] Q -->|"надо будет показать"| C["Доказательство
журнал и артефакт"] Q -->|"привязано ко времени"| D["Срок
расписание и таймер"] Q -->|"ни во что не ложится"| X["Вопрос сформулирован
неточно — назад к юристу"] A --> M["Реестр обязательств:
владелец, механизм,
доказательство, дата пересмотра"] B --> M C --> M D --> M X -.->|"уточнить и вернуться"| Q
Откуда ограничения берутся: шесть независимых источников
Ошибка новичка — думать, что ограничения приходят «от государства». Государство — только один из шести источников, и обычно не самый быстрый.
Три наблюдения по картинке.
- Быстрее всех бьёт площадка. Регулятор появится через годы, если появится. Правила магазина остановят релиз завтра, а решение принимается автоматом и обжалуется неделями (магазины приложений).
- Дороже всех обходится отрасль клиента. Требования банка или больницы приезжают к вам через договор, и в этом договоре вы принимаете обязательства, которых в вашей собственной юрисдикции никто не предъявлял: добровольные до подписи, обязательные после.
- Тише всех работает содержимое артефакта. Копилефтная зависимость в проприетарной сборке не мешает ничему ровно до дня, когда кто-то попросит исходники. Живой случай портала — инстанс it-tools под GPL-3.0 на поддомене: сам факт публичной установки создал обязанность отдавать исходный код вместе с изменениями (лицензии, открытый код).
Юрисдикция — это четыре страны, а не одна
В любой сделке участвует минимум четыре страны, и путать их — самая частая архитектурная ошибка в этой теме.
| Страна | Что она определяет | Где живёт в системе |
|---|---|---|
| Поставщика | форма отчётности, валютные ограничения, экспортный режим | конфигурация организации, одна на инсталляцию |
| Покупателя | косвенный налог, права потребителя, требования к договору | поле в записи покупателя, доказательства места |
| Размещения данных | резидентность, трансграничная передача | атрибут тенанта и атрибут каждого хранилища |
| Канала и платежей | правила площадки, эквайера, банка-получателя | конфигурация канала, не покупателя |
Пятая появляется в B2B: страна пользователя не равна стране покупателя. Договор подписан юридическим лицом в одной стране, а работают в системе сотрудники из пяти. Резидентность требуется по данным сотрудников, налог считается по покупателю, а экспортные ограничения — по фактическому месту доступа.
Отсюда конкретное требование к схеме: не одно поле country. Минимальный набор, который позже не придётся мигрировать:
-- Юрисдикционные атрибуты разнесены по смыслу: каждый отвечает на свой вопрос и меняется независимо.
CREATE TABLE tenant (
id uuid PRIMARY KEY,
billing_country char(2) NOT NULL, -- налог и форма договора
data_region text NOT NULL, -- 'eu-central', 'ru-central': выбор стека
residency_level smallint NOT NULL DEFAULT 1 CHECK (residency_level BETWEEN 1 AND 5),
industry_regime text[] NOT NULL DEFAULT '{}', -- 'finance', 'health', 'public': журналы и сроки
channel text NOT NULL, -- у канала свои правила, не путать со страной
screening_checked_at timestamptz, -- когда сверяли стороны со списками
screening_result text NOT NULL DEFAULT 'pending'
);
-- Доказательства места покупателя лежат отдельно и не перезаписываются: через два года спросят,
-- на каком основании применена именно эта ставка и именно этот регион.
CREATE TABLE buyer_location_evidence (
tenant_id uuid NOT NULL REFERENCES tenant(id),
kind text NOT NULL, -- 'card_bin' | 'billing_address' | 'ip' | 'phone_cc' | 'declared'
value text NOT NULL,
observed_at timestamptz NOT NULL,
PRIMARY KEY (tenant_id, kind, observed_at) -- история, а не последнее значение
);
Механика налогов и валют разобрана в биллинге и здесь не повторяется; важно лишь, что колонка billing_country — это тот же атрибут, из которого растут и налог, и право потребителя на возврат, и язык договора.
Резидентность данных: пять уровней одного слова
«Данные должны находиться в стране» — фраза, за которой прячутся пять принципиально разных требований с разницей в стоимости в десятки раз. Выяснять, какой именно уровень имеется в виду, надо до проектирования, а не после подписи.
- Копия в стране. Достаточно, чтобы в стране лежала актуальная копия; обработка и первичное хранение могут быть где угодно. Дёшево: репликация плюс мониторинг отставания.
- Первичное хранение только в стране. Основная база и все резервные копии — внутри. Уже требует отдельного контура хранения и пересмотра всей схемы бэкапов (объектное хранилище).
- Обработка только в стране. Вычисления тоже внутри: значит, отдельный кластер приложения, отдельный конвейер выкладки, отдельные очереди и отдельная аналитика.
- Доступ только изнутри. Персонал поддержки и дежурные инженеры не имеют доступа из-за границы. Это ломает единое дежурство: либо нанимаете людей в стране, либо строите механику доступа через посредника с журналированием (дежурства).
- Суверенный контур. Ключи шифрования и административный контроль не должны находиться под юрисдикцией иностранного лица. Практически это отдельное юридическое лицо, отдельный оператор и почти отдельный продукт.
$$ C_{\text{регионы}} \approx C_{\text{база}} \cdot \left( 1 + k \cdot (n - 1) \right), \qquad k \in \lbrack 0{,}3;\ 0{,}9 \rbrack $$
Здесь $n$ — число регионов, а $k$ — доля, которая не переиспользуется. На первом уровне $k$ близко к 0,3: платите за железо и за трафик. На четвёртом-пятом $k$ подбирается к 0,9, потому что дублируется не инфраструктура, а организация: люди, процессы, дежурства, аудит. Именно поэтому второй регион почти никогда не стоит «плюс сервер», а стоит «плюс команда» — тот же эффект, что и в стоимости платформы.
Где данные пересекают границу, пока все смотрят на базу
Классический разговор: «база у нас в нужной стране, всё в порядке». База — самый заметный и обычно единственный корректно настроенный элемент. Утекает всё остальное.
Практический порядок работы с этим списком — ровно тот, что на схеме внизу, и он контринтуитивен:
- Выкинуть поток целиком, если он не нужен. Самая дешёвая резидентность — та, которой не потребовалось.
- Убрать из потока персональные данные. Трассировка без идентификатора пользователя, ошибка без тела запроса, аналитика на псевдонимах. Часто оказывается, что поток был нужен, а данные в нём — нет (качество и управление данными).
- И только потом переносить оставшееся в нужную юрисдикцию. Это самый дорогой шаг, и делать его первым — типичная ошибка.
Отдельная строка — ИИ-сервисы: отправка текста обращения во внешнюю модель есть передача данных внешнему обработчику, со всеми теми же последствиями — договор, перечень, право субъекта знать; то, что это выглядит как вызов библиотеки, ничего не меняет.
Персональные данные как инженерная задача
Регламенты пересказывать не будем — это в треке безопасности и у юриста. Инженерная часть сводится к четырём механикам, и все четыре — это код, а не текст.
1. Инвентаризация. Карта: какие категории данных, в каких хранилищах, с какой целью, на какой срок, кому передаются. Без неё не работает ничего остальное. Единственный способ, при котором карта не устаревает через месяц, — генерировать её из кода: аннотации на полях моделей, комментарии в схеме, теги в описании инфраструктуры.
# data-map.yaml — источник истины, из которого генерируются раздел политики обработки, анкета магазина
# и ответ на опросник клиента. Лежит в репозитории, проверяется в CI: новое поле без записи — красная сборка.
stores:
- id: primary-db
region: ru-central
categories:
- name: contact
fields: [email, phone]
purpose: "уведомления по договору" # цель, а не «на всякий случай»
retention: "3 года после расторжения"
legal_basis_owner: "юрист, вопрос закрыт 2026-03-11"
- name: usage_events
fields: [tenant_id, user_id, action, ts]
purpose: "тарификация по потреблению"
retention: "18 месяцев, затем агрегат без user_id"
- id: error-tracker
region: eu-west # ← поток пересекает границу
processor: "внешний сервис"
categories:
- name: diagnostics
fields: [stacktrace, url]
purpose: "диагностика сбоев"
retention: "90 дней"
controls: ["скраббер тела запроса", "запрет отправки заголовков авторизации"]
# Правило проверки: поле есть в схеме, но нет в карте → сборка красная, а не «замечание в бэклог».
2. Основание, цель и минимизация. У каждого поля есть причина существования; поле без цели — это не «на всякий случай», а обязательство, которое вы взяли бесплатно и содержите вечно. Каждое несобранное поле — минус строка в политике, минус вопрос в опроснике клиента, минус риск в инциденте. Проверка при код-ревью: «что сломается, если мы это не сохраним» — если ответ «ничего, но вдруг пригодится», не сохраняем. Приём из биллинга работает и здесь: удалять раньше дешевле, чем защищать дольше.
3. Сроки. Ретеншн — это задача в расписании, а не абзац в документе. Если в политике написано «90 дней», а джоба нет, вы не исполняете собственную политику, и это хуже, чем если бы срок не был объявлен.
4. Права субъекта. Доступ, копия, исправление, удаление, возражение против обработки. Здесь важен не список, а срок ответа: он измеряется календарными днями, а значит, ручной процесс на почте разваливается на десятом запросе.
Удаление: три уровня и то, что делать с бэкапами
Уровень 1 — мягкое удаление. Флаг deleted_at. Это не удаление, это скрытие. Полезно для отмены ошибки, бесполезно как ответ субъекту. Опасно тем, что выглядит как выполненная работа.
Уровень 2 — жёсткое удаление из продакшна. Строки удалены, файлы стёрты, кэши инвалидированы, поисковый индекс перестроен, события в аналитике обезличены. Уже требует знания всех хранилищ — то есть карты данных из пункта 1.
Уровень 3 — бэкапы. Точечно удалить строку из архивной копии невозможно, а если возможно — копия перестаёт быть копией. Два рабочих решения, оба честные:
- Ограниченный срок жизни копий плюс повтор удалений при восстановлении. В реестре записано: копии живут, скажем, 35 дней; список удалённых субъектов («надгробия») хранится дольше и применяется повторно сразу после любого восстановления. Тогда честная формулировка звучит так: из активных систем — немедленно, из резервных копий — по истечении срока их жизни, и данные не возвращаются в оборот даже при аварийном восстановлении.
- Криптографическое стирание. Данные субъекта шифруются отдельным ключом; удаление ключа делает нечитаемыми все копии сразу, включая те, до которых вы физически не дотянетесь.
"""Криптографическое стирание: уничтожаем ключ, а не строки.
Конвертное шифрование: у каждого субъекта свой ключ данных, обёрнутый на мастер-ключе в хранилище секретов.
Стирание субъекта — уничтожение его ключа: все копии и архивы, где лежит шифротекст, становятся
нерасшифровываемыми одновременно, включая те, до которых вы физически не дотянетесь. Цена, о которой надо
знать заранее: по зашифрованным полям нельзя искать без отдельного слоя, потеря ключа неотличима от
стирания, а ключей столько же, сколько субъектов.
"""
import os
from dataclasses import dataclass
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
@dataclass(frozen=True)
class SubjectCrypto:
kms: "KeyStore" # мастер-ключ и обёрнутые ключи субъектов
def encrypt(self, subject_id: str, plaintext: bytes, aad: bytes) -> bytes:
dek, nonce = self.kms.get_or_create_dek(subject_id), os.urandom(12)
return nonce + AESGCM(dek).encrypt(nonce, plaintext, aad)
def decrypt(self, subject_id: str, blob: bytes, aad: bytes) -> bytes | None:
dek = self.kms.get_dek(subject_id) # None — ключ уничтожен,
if dek is None: # это штатный ответ, а не ошибка
return None
return AESGCM(dek).decrypt(blob[:12], blob[12:], aad)
def erase(self, subject_id: str, ticket: str) -> None:
"""Необратимое стирание, сразу с доказательством: кто, когда, по какому обращению."""
self.kms.destroy_dek(subject_id)
self.kms.audit(action="dek_destroyed", subject_id=subject_id, ticket=ticket)
Сложность операции — константная по числу записей субъекта: одно удаление ключа против обхода всех хранилищ. Память — один ключ на субъекта, порядка 32 байт плюс метаданные; для миллиона субъектов это десятки мегабайт в хранилище ключей, что несопоставимо дешевле, чем перебор бэкапов. Управление самими ключами — в главе про секреты и прикладной криптографии.
Оператор и обработчик: одна строка договора, много кода
Роль определяет, кто решает, зачем обрабатываются данные. Если решаете вы — вы оператор. Если решает клиент, а вы выполняете его инструкции — вы обработчик. SaaS почти всегда одновременно и то и другое: оператор по данным своих пользователей (регистрация, биллинг, поддержка) и обработчик по данным, которые клиент загрузил в систему.
Что меняется в системе, если вы обработчик:
- Настраиваемые сроки хранения. Не ваша политика, а параметр тенанта: клиент вправе потребовать «удалять через 30 дней».
- Экспорт и удаление по команде клиента, а не только по запросу субъекта, — и с подтверждающим отчётом.
- Список субобработчиков публичный и версионируемый. Это ровно те двенадцать строк со схемы выше. Появился новый — клиента надо уведомить заранее, и у него может быть право возразить.
- Уведомление об инциденте в срок, измеряемый часами. Значит, вы обязаны уметь ответить «какие именно записи затронуты», а это требует журналов доступа, которые пишутся до инцидента, а не после (реакция на инциденты).
Формулировки договора — к юристу; шаблон разбора договорных обязательств есть в материале о контрактах. Ваша часть — сделать так, чтобы каждое обещание в договоре имело исполнителя в коде и способ доказать исполнение.
Экспортный контроль: почему инженера спрашивают про шифрование
Эта тема сваливается на разработчика внезапно — обычно в виде обязательного поля при загрузке сборки в магазин (магазины приложений, релизы мобильных). Три вещи ломают интуицию. Экспорт — не обязательно физическая отправка. Публикация файла, который можно скачать из другой страны, предоставление доступа к репозиторию иностранному сотруднику, демонстрация технологии — в разных режимах всё это может считаться передачей. «Мы ничего не отправляли, оно само скачалось» — не аргумент.
Классификация идёт по функции, а не по намерению. Мессенджер со сквозным шифрованием классифицируется по криптографии, даже если вы считаете его «просто чатом». Резервное копирование с шифрованием архива — тоже криптография. Функция определяет режим.
Исключение надо заявить, а не предположить. Для массового рынка почти везде существуют упрощённые режимы, но у них есть условия, а у условий — процедура: самоклассификация, уведомление, регистрационный номер. Разница между «мы попадаем под исключение» и «мы заявили исключение и у нас есть подтверждение» — это разница между спокойным релизом и остановленной сборкой.
Что от вас требуется конкретно, и это всё инженерные задачи:
- Знать всю криптографию в продукте, включая транзитивную. Не «мы используем TLS», а полный список: библиотеки, алгоритмы, длины ключей, где хранятся ключи. Источник истины — спецификация состава сборки (упаковка, цепочка поставки).
- Отличать «крипта для защиты канала» от «крипта как функция продукта». Это разные ответы в анкете и разные режимы.
- Не встраивать собственную криптографию. Помимо очевидной причины — своя реализация лишает вас права ссылаться на стандартные компоненты и превращает разговор в исследование.
- Хранить ответ анкеты рядом с версией сборки. Состав меняется — ответ может измениться, а ответ, данный полтора года назад, относится к той версии, для которой он был дан. Заодно держите наготове ответ на вопрос «есть ли обход или закладка»: он должен быть «нет» и подтверждаться процессом ревью, а не памятью тимлида.
Вопрос юристу формулируется так, а не иначе. Не «подпадаем ли мы под экспортный контроль», а: «вот список криптографических компонентов и их назначение, вот страны, где доступна загрузка, вот страны, где находятся наши сотрудники с доступом к репозиторию, — какая классификация применима, какая процедура требуется и кто её выполняет».
Списочные ограничения: скрининг сторон, а не блокировка по адресу
Отдельный класс — ограничения по конкретным лицам, организациям и направлениям. Инженерная суть проста и неприятна: обязанность привязана к стороне сделки, а не к её сетевому адресу.
Отсюда прямое следствие: блокировка по геолокации — гигиена, а не соответствие требованиям. Она отсекает случайных, не отсекает намеренных и не защищает вас, если сторона в списке пришла через соседнюю страну. Работающий механизм устроен иначе.
решение и основание в журнале G-->>S: снято либо отказ end Note over G,E: Списки меняются — чистая проверка
годичной давности не значит ничего.
Перепроверка по расписанию и перед продлением.
Четыре вещи отличают работающий скрининг от имитации:
- Точки проверки — три, а не одна. Регистрация, оплата, продление. Плюс регулярная перепроверка всей базы: списки обновляются, и вчера чистый контрагент сегодня может быть в списке.
- Нечёткое сравнение и человек в цикле. Сравнение имён даёт много ложных срабатываний — транслитерация, порядок частей имени, однофамильцы. Автоматический отказ по совпадению строк создаёт свою проблему; нужна очередь разбора с целевым сроком.
- Журнал решений — это и есть доказательство. Кто проверил, по какой версии списка, что решил, на каком основании. Без журнала проверка была, но доказать её нельзя, а значит, для проверяющего её не было. И отказ оформляется аккуратно: право не выдаётся, деньги не удерживаются, формулировка ответа пользователю согласована с юристом заранее.
- Открытый код — отдельный режим. Публикация исходных текстов во многих режимах регулируется иначе, чем поставка бинарного продукта. Это не значит «на open source ничего не распространяется» — это значит, что режим другой, и уточнять его надо до публикации репозитория (открытый код как способ поставки).
Отраслевые режимы: ограничения, которые приезжают договором
Самый частый способ получить жёсткие требования — продать клиенту из регулируемой отрасли. Никакой регулятор к вам не придёт; придёт отдел закупок с приложением к договору на сорок пунктов.
| Отрасль клиента | Типичное требование | Что меняется в системе | Чем доказываете |
|---|---|---|---|
| Финансы | журнал доступа, длительное хранение, право аудита, план выхода | неизменяемый журнал, ретеншн по годам, экспорт всех данных клиента | выгрузка журнала, отчёт о тестовом экспорте |
| Медицина | разграничение доступа к записям, шифрование, журнал обращений | атрибутный контроль доступа, шифрование на уровне полей | политика доступа в коде плюс тест |
| Госзакупки | резидентность, доступность интерфейса, иногда наличие в реестре | отдельный региональный контур, соответствие интерфейса стандартам | отчёт о проверке доступности, документы на контур |
| Дети и образование | ограничение профилирования и рекламы, возрастная проверка | режим без трекинга, флаг возраста как атрибут | конфигурация режима и её журнал изменений |
| Критическая инфраструктура | уведомление об инцидентах в срок, непрерывность | процедуры оповещения, регулярные учения | протоколы учений, записи оповещений |
Обратите внимание на последний столбец: доказательство — это отдельный артефакт, который надо производить, а не побочный эффект хорошей работы. Требование доступности интерфейса, кстати, всё чаще встречается именно в закупках, и там оно перестаёт быть этическим вопросом и становится условием допуска — см. зачем нужна доступность и какие есть стандарты.
Аудиты и сертификаты — это про продажи, а не про безопасность
Отраслевой аудит покупатель приобретает не ради вашей защищённости, а ради снятия собственной ответственности: у него в процедуре записано, что поставщик должен иметь такой отчёт. Отсюда трезвый взгляд:
- Сертификат не делает систему безопаснее — он фиксирует, что заявленные процессы существуют и выполняются. Безопасность делают практики из трека security.
- Стоимость — годовая, а не разовая. Подготовка, внешний аудитор, поддержание процессов, ежегодное подтверждение. По состоянию на середину 2026 года для маленькой команды это ощутимая доля годового бюджета: сам аудит — сотни тысяч рублей и выше, подготовка обычно дороже аудита, а постоянная нагрузка на команду измеряется человеко-неделями в квартал. Конкретные цифры запрашивайте у аудиторов на текущий год: рынок и требования меняются.
- Решение принимается арифметикой. Если сертификат открывает сегмент, в котором средний контракт кратно больше годовой стоимости соответствия, — считайте. Если нет — не начинайте (юнит-экономика).
Между «ничего нет» и «есть аудит» существует дешёвая промежуточная ступень — пакет доверия, подготовленный заранее: описание архитектуры и модели угроз, список субобработчиков, политика хранения и удаления, результаты последней проверки на уязвимости, спецификация состава сборки, порядок уведомления об инцидентах. Он не заменяет аудит, но сокращает цикл сделки с месяцев до недель, потому что закрывает восемьдесят процентов опросника без участия людей. Как это устроено на уровне платформы — в главе про безопасность и соответствие.
Соответствие как код: проверки в конвейере и реестр обязательств
Ограничения, живущие в головах, исчезают вместе с людьми: работает только то, что проверяется автоматически и хранится рядом с артефактом. Ворота в конвейере устроены так, что каждая проверка даёт машиночитаемый результат, и этот результат живёт столько же, сколько само обязательство (конвейер и безопасность, тесты в CI).
#!/usr/bin/env bash
# Ворота соответствия в конвейере. Ни одна проверка не «предупреждает»: предупреждение никто не читает.
set -euo pipefail
OUT="artifacts/compliance/${VERSION}"; mkdir -p "$OUT"
# 1. Состав сборки: без него невозможны ни лицензии, ни разговор про криптографию.
syft packages "dist/app:${VERSION}" -o cyclonedx-json > "$OUT/sbom.cdx.json"
# 2. Политика лицензий: копилефт в проприетарной сборке останавливает выпуск.
license-check --sbom "$OUT/sbom.cdx.json" --policy policy/licenses.yaml --report "$OUT/licenses.json"
# 3. Криптографические компоненты отдельным списком — он идёт прямо в анкету площадки.
jq '[.components[] | select(.name | test("openssl|libsodium|bouncycastle|boringssl"; "i"))]' \
"$OUT/sbom.cdx.json" > "$OUT/crypto-components.json"
# 4. Карта данных: новое поле с персональными данными без записи в data-map.yaml — ошибка.
datamap-lint --schema db/schema.sql --map data-map.yaml --report "$OUT/datamap.json"
# 5. Реестр обязательств: просроченный пересмотр — тоже красная сборка.
python tools/check_obligations.py obligations.yaml --report "$OUT/obligations.json"
# Доказательство без подписи — просто файл, поэтому подписываем вместе с артефактом.
cosign sign-blob --yes "$OUT/sbom.cdx.json" > "$OUT/sbom.sig"
Реестр обязательств — единственный документ, который в этой теме действительно нужен. Не политика на двадцать страниц, а таблица, где у каждого обязательства есть источник, владелец, механизм, доказательство и дата пересмотра.
Модель читается просто: обязательство без механизма — это обещание; механизм без доказательства — это надежда; доказательство без даты пересмотра — это архив. Проверка такого реестра пишется за час и вешается в те же ворота (шаг 5 в скрипте выше): пройти по записям и упасть, если у обязательства нет владельца, нет механизма, доказательство не производится, доказательство старше своего допустимого возраста или дата пересмотра уже в прошлом. Сложность линейная по числу обязательств, а их у нормального продукта десятки, не тысячи. Дорого не выполнять проверку — дорого впервые составить список: обычно это две-три недели работы одного человека с юристом на подхвате.
Считать стоимость соответствия до выхода на рынок
Рынок — это не «ещё одна локаль». Стоимость входа складывается из трёх слагаемых с разным поведением:
$$ C_{\text{рынок}} = C_{\text{вход}} ;+; C_{\text{год}} \cdot T ;+; c_{\text{сделка}} \cdot N $$
- $C_{\text{вход}}$ — разовое: регистрации, юридическая проработка, региональный контур, локализация договора.
- $C_{\text{год}}$ — ежегодное и самое коварное: отчётность, продление аудита, поддержание отдельного стека, дежурство внутри юрисдикции. Оно не уменьшается от роста выручки.
- $c_{\text{сделка}} \cdot N$ — на каждую сделку: опросники, проверки контрагента, индивидуальные приложения к договору.
Правило принятия решения простое: выходить, если ожидаемая валовая маржа за два года превышает сумму трёх слагаемых с запасом, а не впритык, потому что $C_{\text{год}}$ имеет свойство расти. И второе правило, которое редко проговаривают вслух: отказ от рынка — это нормальное инженерное решение, а не поражение. Половина проблем этой главы решается фразой «мы не продаём в эту юрисдикцию», записанной честно и реализованной в коде, а не подразумеваемой.
Квадрант в правом нижнем углу — самый опасный не деньгами, а вниманием: небольшой рынок с дорогим соответствием съедает время тех же людей, которые могли бы закрывать сделки в левом верхнем (приоритизация).
Что спросить у юриста
Юрист отвечает точно на точный вопрос. Вот формулировки, после которых ответ можно превратить в задачу в трекере.
- При нашей структуре продаж — в каких юрисдикциях у нас возникает обязанность регистрации, и какая именно?
- Мы оператор или обработчик по каждой из этих категорий данных? Вот карта данных, пройдите по строкам.
- Какие признаки места нахождения покупателя мы обязаны хранить, в каком виде и сколько лет?
- Требование клиента «данные не покидают страну» — это какой из пяти уровней? Что именно мы обязаны подтвердить?
- Каков предельный срок ответа на запрос субъекта и что считается надлежащим подтверждением исполнения?
- Наша формулировка про бэкапы — «из активных систем немедленно, из копий по истечении срока их жизни» — приемлема? Если нет, какая приемлема?
- Вот список криптографических компонентов и их назначение: какая экспортная классификация применима, какая процедура нужна и меняется ли что-то от публикации нашего репозитория в открытом доступе?
- По каким спискам мы обязаны проверять контрагентов, с какой периодичностью, что фиксировать в журнале и что при отказе вправе сообщить пользователю?
- Какие обязательства переживают расторжение договора, на какой срок, и кого надо уведомить, если мы добавим подрядчика в другой стране?
И зеркальное правило: ни один пункт этой главы не является юридической консультацией. Здесь описан механизм — как требование превращается в поле, ветку, артефакт и срок. Содержание требования устанавливает юрист вашей юрисдикции применительно к вашей ситуации.
Типичные ошибки
- Одно поле
countryна всё. Через год выясняется, что налог, резидентность и правила канала разошлись, а миграция трогает каждую таблицу. - «База в нужной стране, значит, резидентность есть». Двенадцать потоков со схемы выше говорят обратное.
- Политика написана раньше механизма. Опубликованное обещание, которое вы не исполняете, хуже отсутствия обещания.
- Мягкое удаление вместо удаления. Флаг
deleted_at— это скрытие; субъекту отвечено «удалено», данные на месте. - Ретеншн живёт в документе, а не в расписании. Срок объявлен, джобы нет, данные копятся годами.
- Ответ на анкету об экспорте «по памяти». Состав сборки изменился, ответ остался прежним — и относится он к другой версии.
- Геоблок вместо скрининга. Отсекает случайных, не отсекает намеренных, доказательством не является; а скрининг без человека в цикле, наоборот, отказывает по совпадению строк и создаёт поток конфликтов на пустом месте.
- Доказательство не производится. Проверка выполняется, результат нигде не сохраняется — для проверяющего проверки не было.
- Требования собирают под конкретную сделку и забывают. Обязательство осталось, знание о нём ушло вместе с менеджером, а пересмотр никто не запланировал — реестр без даты пересмотра гниёт молча.
- Внешний ИИ-сервис не считают обработчиком. Вызов библиотеки выглядит невинно, передача данных от этого не исчезает.
Мини-итог
Ограничение — условие, при котором работающий продукт нельзя поставить конкретному покупателю. В системе оно принимает одну из четырёх форм: атрибут, ветка, доказательство, срок; если требование не ложится ни в одну, оно ещё не понято. Источников шесть, и быстрее всех бьёт площадка, дороже всех обходится отрасль клиента, тише всех работает содержимое артефакта. Юрисдикций в сделке четыре, и в схеме им нужны четыре разных поля. Резидентность имеет пять уровней с разницей в стоимости в десятки раз, и нарушается она не в базе, а у самого незаметного подрядчика. Персональные данные — это карта, основание, минимизация, срок и исполнимая процедура удаления, включая честный ответ про бэкапы. Экспортный контроль требует знать собственную криптографию и заявлять исключение, а не предполагать его. Списочные ограничения проверяются по стороне сделки, а не по адресу, и доказательством служит журнал решений. Всё вместе живёт в реестре обязательств: источник, владелец, механизм, доказательство, дата пересмотра — и проверяется теми же воротами, что и сборка. А где кончается механизм и начинается норма, ваша работа — задать юристу вопрос, на который можно ответить одним предложением.
Источники
- Регламент ЕС 2016/679 (GDPR) — https://eur-lex.europa.eu/eli/reg/2016/679/oj; руководства Европейского совета по защите данных — https://www.edpb.europa.eu/our-work-tools/general-guidance/guidelines-recommendations-best-practices_en.
- Федеральный закон № 152-ФЗ «О персональных данных» — https://www.consultant.ru/document/cons_doc_LAW_61801/; реестр операторов Роскомнадзора — https://pd.rkn.gov.ru/.
- Стандартные договорные условия ЕС для трансграничной передачи — https://commission.europa.eu/law/law-topic/data-protection/international-dimension-data-protection/standard-contractual-clauses-scc_en.
- NIST Privacy Framework — https://www.nist.gov/privacy-framework: словарь и структура, удобные для инвентаризации данных.
- Bureau of Industry and Security: Encryption — https://www.bis.gov/policy-guidance/encryption; Вассенаарские договорённости — https://www.wassenaar.org/.
- Apple: соответствие правилам экспорта шифрования — https://developer.apple.com/documentation/security/complying-with-encryption-export-regulations; Google Play Data safety — https://support.google.com/googleplay/android-developer/answer/10787469.
- OFAC SDN List — https://ofac.treasury.gov/specially-designated-nationals-and-blocked-persons-list-sdn-human-readable-lists; EU Sanctions Map — https://www.sanctionsmap.eu/.
- CycloneDX — https://cyclonedx.org/, SPDX — https://spdx.dev/: форматы спецификации состава сборки, из которой растут и лицензионная политика, и список криптографических компонентов. WCAG 2.2 — https://www.w3.org/TR/WCAG22/: то, на что ссылаются требования доступности в закупках.
- ISO/IEC 27001 — https://www.iso.org/standard/27001; SOC 2 (AICPA) — https://www.aicpa-cima.com/topic/audit-assurance/audit-and-assurance-greater-than-soc-2; CSA Consensus Assessments — https://cloudsecurityalliance.org/research/working-groups/consensus-assessments.
- Документы портала — оферта, политика обработки персональных данных, согласие, правила возврата: живой пример того, как обязательства формулируются и во что превращаются в коде.
Что дальше
Трек «Поставка софта» на этом закончен. От карты трека и моделей поставки мы дошли до упаковки и ограничений: теперь у вас есть язык, на котором обсуждают цену владения моделью, обязательства лицензии, механику денег и границы, за которые продукт не едет. Куда идти дальше — в зависимости от того, что оказалось узким местом:
- Техническая база доставки — DevOps про конвейеры и окружения, Platform Engineering про то, как сделать доставку самообслуживаемой, и SRE про безопасные релизы и откаты.
- Защита и доказательства — Security, особенно цепочка поставки и приватность: всё, что в этой главе названо доказательством, там разбирается по существу.
- Деньги вокруг работы инженера — Бизнес: собственный продукт, юнит-экономика, деньги и риски.
- Продуктовые и аналитические решения — Product Management: метрики и цена и рост; рядом — Системный анализ про нефункциональные требования и Техническое письмо про то, как зафиксировать решение так, чтобы через год его можно было пересмотреть осознанно.
Если непонятно, какой трек брать следующим, посмотрите общую карту курсов — там треки расставлены по уровням и связям между собой.