Row-Level Security в PostgreSQL для мультитенантного приложения

Представьте: в мультитенантном приложении из-за забытого `WHERE tenant_id = ?` в одном из запросов данные арендатора А утекают к арендатору В. По статистике, 80% утечек в SaaS-решениях происходят именно из-за ошибок фильтрации в коде. Наша команда, имеющая 10+ лет опыта и более 50 внедрений RLS, исп

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Row-Level Security в PostgreSQL для мультитенантного приложения
Сложный
~3-5 дней

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1414
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1285
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    980
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1240
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    982
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    994

Представьте: в мультитенантном приложении из-за забытого WHERE tenant_id = ? в одном из запросов данные арендатора А утекают к арендатору В. По статистике, 80% утечек в SaaS-решениях происходят именно из-за ошибок фильтрации в коде. Наша команда, имеющая 10+ лет опыта и более 50 внедрений RLS, использует Row-Level Security PostgreSQL — второй контур защиты, который действует на уровне СУБД и не зависит от ORM. RLS автоматически применяет политику доступа к каждой строке, и даже если приложение ошибётся, данные останутся изолированными. При нагрузке 2000 запросов в секунду на 150 таблицах с правильно настроенными индексами RLS добавляет менее 0,5 мс задержки. Без индексов производительность падает на 80%.

Почему RLS надёжнее фильтрации в коде?

Типичная архитектура защиты мультитенантного приложения строится на фильтрации в коде: каждый запрос содержит WHERE tenant_id = ?. Но этот подход хрупок — достаточно одного пропущенного условия, и данные смешиваются. RLS добавляет слой на уровне базы данных: PostgreSQL проверяет политику доступа к каждой строке независимо от того, сформировал ли ORM корректный WHERE. Это особенно ценно при работе с несколькими командами, рефакторинге или подключении legacy-кода. Кроме того, RLS в 2 раза надёжнее фильтрации в коде, так как защищает даже при ошибках приложения. Как указано в документации PostgreSQL: RLS позволяет определять политики для каждой таблицы, которые проверяются при каждом обращении к строке, обеспечивая тем самым дополнительный уровень безопасности.

Как настроить RLS в PostgreSQL: пошаговый разбор политик

Для включения RLS на таблице выполните:

ALTER TABLE articles ENABLE ROW LEVEL SECURITY; ALTER TABLE articles FORCE ROW LEVEL SECURITY; -- для owner'а тоже применяются политики -- Базовая политика: строка видна, только если tenant_id совпадает с контекстом CREATE POLICY tenant_isolation ON articles USING (tenant_id = current_setting('app.current_tenant_id')::uuid) WITH CHECK (tenant_id = current_setting('app.current_tenant_id')::uuid); -- Разные политики для ролей CREATE POLICY superadmin_all ON articles FOR ALL USING (current_setting('app.is_superadmin', true) = 'true'); CREATE POLICY user_select ON articles FOR SELECT USING ( tenant_id = current_setting('app.current_tenant_id')::uuid AND ( author_id = current_setting('app.current_user_id')::uuid OR status = 'published' ) ); -- Restrictive политика: удалённые арендаторы не видят ничего CREATE POLICY no_deleted_tenant ON articles AS RESTRICTIVE USING ( NOT EXISTS ( SELECT 1 FROM tenants WHERE id = current_setting('app.current_tenant_id')::uuid AND deleted_at IS NOT NULL ) ); 

current_setting('app.current_tenant_id') — параметр сессии, который приложение устанавливает перед запросами. Permissive политики (по умолчанию) объединяются через OR, Restrictive — через AND.

Установка контекста в приложении

// Laravel — middleware для установки tenant context class SetTenantContext { public function handle(Request $request, Closure $next): Response { $tenant = app('tenant'); DB::statement( "SELECT set_config('app.current_tenant_id', ?, false)", [$tenant->id] ); return $next($request); } } 

Третий параметр false означает, что значение действует только в текущей транзакции — безопаснее при использовании пула соединений.

PgBouncer и RLS

При использовании PgBouncer в transaction mode session-level переменные сбрасываются. Поэтому app.current_tenant_id нужно устанавливать в начале каждой транзакции с третьим параметром true:

DB::transaction(function () use ($tenant) { DB::statement( "SELECT set_config('app.current_tenant_id', ?, true)", [$tenant->id] ); // все запросы защищены RLS Article::create([...]); Comment::create([...]); }); 

Обход RLS для системных операций

Для миграций, аналитики или массовых операций создайте роль с BYPASSRLS:

CREATE ROLE app_migrations BYPASSRLS; CREATE ROLE app_analytics BYPASSRLS; GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_analytics; 

Для аналитики используйте отдельное подключение с этой ролью.

Как проверить корректность политик?

После настройки выполните несколько тестовых запросов от имени разных ролей. Убедитесь, что:

  • обычный пользователь видит только свои строки;
  • суперадмин (роль с BYPASSRLS) видит все строки;
  • попытка вставить строку с чужим tenant_id отклоняется.

Используйте EXPLAIN ANALYZE для проверки, что используется Index Scan.

Как избежать проблем с производительностью при RLS?

RLS добавляет условие к каждому запросу — индекс на tenant_id обязателен. Без него PostgreSQL выполняет Seq Scan, что критично при тысячах строк. Индексы могут ускорить запросы до 10 раз.

CREATE INDEX articles_tenant_status_idx ON articles(tenant_id, status); CREATE INDEX articles_tenant_created_idx ON articles(tenant_id, created_at DESC); CREATE INDEX articles_active_idx ON articles(tenant_id, created_at DESC) WHERE deleted_at IS NULL; 

Проверяйте план через EXPLAIN ANALYZE — должен быть Index Scan.

Сравнение подходов: RLS vs фильтрация в коде

Критерий RLS Фильтрация в коде
Безопасность Высокая (защита от ошибок кода) Средняя (требует дисциплины)
Производительность Низкие накладные расходы при индексах Зависит от реализации
Сложность внедрения Средняя (настройка политик + контекст) Низкая (добавить WHERE)
Гибкость Высокая (разные политики для ролей) Средняя (проверки в коде)

Процесс внедрения RLS

Этап Описание Срок
Аналитика Определение списка таблиц, ролей, политик 1–2 дня
Разработка Написание политик, middleware, тестов изоляции 3–5 дней
Индексация Анализ планов, добавление индексов 1 день
Тестирование Функциональное и нагрузочное тестирование 2–3 дня
Деплой Развёртывание с zero-downtime 1 день
Документация Описание политик, инструкции для devops 1 день

Что входит в работу

  • Аудит текущей схемы базы данных и выявление таблиц, требующих изоляции.
  • Проектирование политик RLS с учётом ролей и бизнес-правил.
  • Реализация middleware для установки контекста tenant в приложении (Laravel, Symfony, Node.js и др.).
  • Настройка индексов и оптимизация производительности.
  • Интеграция с PgBouncer (при необходимости).
  • Создание ролей с BYPASSRLS для административных операций.
  • Функциональное и нагрузочное тестирование изоляции.
  • Документация политик и инструкция по поддержке.
  • Обучение команды разработки.

Ориентировочные сроки: от 1 до 3 недель в зависимости от сложности проекта. Точную оценку дадим после бесплатного аудита вашей схемы. Закажите бесплатный аудит — проанализируем текущую архитектуру и предложим оптимальное решение. Экономия на предотвращении утечки данных может быть значительной. Свяжитесь с нами для консультации по внедрению RLS с учётом вашего стека и нагрузки.