Изоляция держится на фильтре в коде
Отчёт, экспорт или админ-скрипт забыл условие по tenant_id — и выборка вернула строки чужого клиента.
Выберите тему — соберём контекст
Строим изоляцию данных так, чтобы она не зависела от внимательности разработчика: политики RLS в базе, обязательный tenant-контекст в приложении, шифрование полей ключами тенанта и аудит каждого доступа.
Или сразу: +7 (962) 368-39-51 Telegram
Механизмы утечки почти всегда одни и те же, и почти никогда они не связаны со взломом извне.
Отчёт, экспорт или админ-скрипт забыл условие по tenant_id — и выборка вернула строки чужого клиента.
Политики есть, но владелец таблицы и роль приложения обходят их, а соединение из пула сохраняет контекст предыдущего запроса.
Ключ в Redis без тенанта, общий поисковый индекс и логи с персональными данными сводят на нет изоляцию основной таблицы.
Паспорт, телефон и адрес хранятся в plaintext, один ключ шифрования на всю систему, ротации нет — дамп базы равен утечке.
На вопрос регулятора, кто и когда читал карточку клиента, ответить нечем: журналы приложения их не содержат.
Право на удаление и экспорт выполняются SQL-скриптом по памяти: часть данных остаётся в архивах, кэше и бэкапах.
Изоляция проектируется как защита в глубину: ошибка в одном слое не должна открывать данные другого тенанта.
Переносим правило «вижу только своё» внутрь СУБД, где его нельзя забыть в новом запросе.
Приложение не доверяет себе: контекст обязателен, а запросы вне репозиториев запрещены соглашениями и проверками.
Персональные данные шифруются на уровне модели, ключ у каждого тенанта свой, а мастер-ключ живёт в KMS.
Каждое обращение к персональным данным фиксируется, а изоляция проверяется негативными тестами, а не только ревью.
Начинаем с инвентаризации данных и требований регуляторов, затем строим политики и только потом трогаем код продукта.
Составляем карту данных по тенантам, помечаем персональные и чувствительные поля, фиксируем роли и требования 152-ФЗ и GDPR.
Описываем политики RLS, схему tenant-контекста, иерархию ключей шифрования, формат журнала аудита и негативные тесты.
Включаем RLS и разделяемые роли, переводим доступ к данным в единый слой, шифруем существующие поля без простоя.
Прогоняем сценарии утечки между тенантами, проверяем аудит и удаление по запросу, настраиваем регулярный пересмотр прав.
Фильтр в приложении защищает ровно до того момента, пока разработчик о нём помнит. Один отчёт, экспорт в CSV, административный скрипт или задача в очереди, собранная без условия по тенанту, и данные уходят чужому клиенту. Ошибка при этом не проявляется в обычных тестах, потому что на тестовом наборе все строки принадлежат одному тенанту.
Поэтому правило «вижу только своё» мы переносим внутрь СУБД через row-level security. Политики проверяются на каждой строке независимо от того, каким способом построен запрос, и включены принудительно, в том числе для владельца таблицы. Роль приложения не имеет права обходить политики, а контекст тенанта устанавливается в начале транзакции, чтобы он не «переехал» между запросами в общем пуле соединений.
Изоляция тенантов не отменяет требования к персональным данным. Мы классифицируем поля и шифруем чувствительные значения на уровне модели, используя конвертное шифрование: у каждого тенанта свой ключ данных, а мастер-ключ хранится в KMS и никогда не попадает в базу. Для поиска по зашифрованному полю используется детерминированный хеш, поэтому фильтры и уникальные индексы продолжают работать.
Такой контур закрывает и 152-ФЗ, и GDPR: видно, где лежат данные, кто к ним обращался и как выполнено требование об удалении.
Нет, это только один рубеж. Фильтр в коде легко забыть в новом отчёте, экспорте или фоновой задаче, и ошибка не проявится на тестах с одним тенантом. Поэтому мы дублируем правило внутри СУБД политиками row-level security, включёнными принудительно, и дополнительно запрещаем сырые запросы вне слоя доступа к данным.
Контекст тенанта устанавливается в начале транзакции и сбрасывается при её завершении, поэтому соединение из пула не переносит чужой контекст в следующий запрос. Роль приложения не должна иметь права обходить политики и не должна владеть таблицами. Если соединения кэшируются на уровне воркеров, контекст задаётся заново для каждой единицы работы.
Мы используем конвертное шифрование: у каждого тенанта свой ключ данных, зашифрованный мастер-ключом из KMS. Это разграничивает доступ и упрощает ротацию. При уходе клиента достаточно уничтожить его ключ данных — зашифрованные значения остаются нечитаемыми, что и называют криптографическим удалением, а физическая очистка бэкапов идёт по политике хранения.
Мы заранее описываем карту данных тенанта и субъекта, включая кэш, поисковый индекс, файловое хранилище и очереди. На её основе строятся процедуры экспорта в машиночитаемом виде и удаления с подтверждением по каждому хранилищу. Журнал аудита фиксирует обращения к персональным данным, поэтому факт и объём обработки можно подтвердить документально.
Смежные технические темы, с которыми эта задача обычно решается в одном проекте.
Разберём текущие политики доступа и шифрование, покажем слабые места и предложим план устранения без остановки продукта.
Cookie и аналитика
Мы используем необходимые cookie для работы сайта и форм. Аналитические cookie (Яндекс.Метрика, включая запись сессий) подключаем только с вашего согласия — от него можно отказаться. Подробнее: Политика использования cookie и обработка персональных данных.