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

Ошибка фильтрации в коде — и данные одного арендатора могут оказаться у другого. Мы внедряем Row-Level Security в PostgreSQL, создавая второй контур защиты на уровне базы данных, который не зависит от ORM и приложений. Наша команда реализует проект под ключ, обеспечивая надёжную изоляцию данных и последующую поддержку.

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

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

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

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

Представьте: в мультитенантном приложении из-за забытого 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 с учётом вашего стека и нагрузки.

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

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

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060