Глава 22. Row-Level Security
22.1. RLS как последняя граница
RLS полезен там, где модель безопасности должна быть применена непосредственно к физическим строкам.
Архитектурная цепочка:
Security model
↓
Effective authorization
↓
RLS policy
↓
Physical rows
Здесь RLS находится в самом низу.
Он не должен заново строить всю модель отношений.
22.2. Почему RLS не должен вычислять весь граф
Предположим, доступ зависит от:
User
→ Group
→ Parent Group
→ Project
→ Role
→ Publication
→ Object
Можно попытаться построить весь этот граф внутри каждой RLS policy.
Но тогда база данных становится одновременно:
-
хранилищем;
-
движком отношений;
-
движком проекций;
-
движком авторизации.
Это резко усложняет систему.
Гораздо устойчивее подготовить необходимые security facts заранее:
Relationships
↓
Security projections
↓
RLS
22.3. RLS использует подготовленное состояние
RLS должен получать достаточно информации, чтобы ответить на физический вопрос.
Например:
current_company_id
current_user_id
и связанные с ними производные данные.
Но вычисление сложных отношений желательно вынести выше.
Таким образом:
Projection layer
подготавливает модель,
а:
RLS
применяет её к данным.
22.4. Контекст запроса должен быть доверенным
Если RLS использует:
current_user_id
current_company_id
то возникает вопрос:
Кто устанавливает эти значения?
Они не должны зависеть от произвольного пользовательского SQL.
Контекст должен формироваться доверенным приложением или другим контролируемым механизмом.
В противном случае RLS лишь формально существует, но не является надёжной границей.
22.5. Транзакция и соединение
Контекст безопасности должен относиться к конкретному SQL-сеансу или транзакции.
Иначе connection pool может привести к опасному сценарию:
Request A
↓
connection
↓
security context A
connection returned to pool
Request B
↓
same connection
↓
old context A
Поэтому безопасность контекста соединения является частью архитектуры RLS.
22.6. Как это реализовано в Guardian
В текущей реализации Guardian используется RlsDataSourceProxy.
Архитектура разделяет:
physicalDataSource
и:
RLS-aware application DataSource
Физический источник используется инфраструктурными операциями, в частности Flyway.
Приложение получает прокси над физическим источником.
Прокси применяет RLS-контекст лениво перед первым бизнес-SQL на том же физическом соединении.
То есть принципиальная схема:
Hikari physical connection
↓
RlsDataSourceProxy
↓
business SQL
↓
PostgreSQL RLS
22.7. Почему RLS применяется лениво
Установка security context непосредственно в getConnection() может оказаться слишком ранней.
Соединение могло быть получено, но бизнес-запрос ещё не начался.
Поэтому текущая реализация откладывает применение RLS-контекста до момента, когда действительно выполняется бизнес-SQL.
Важно, что SET LOCAL выполняется на том же физическом соединении, на котором будет выполняться запрос.
Это связывает security context с конкретной транзакцией.
22.8. RLS и миграции
Инфраструктурные операции не должны случайно оказаться внутри обычной пользовательской модели доступа.
Поэтому в Guardian:
Flyway
↓
physicalDataSource
а:
Hibernate / repositories
↓
RlsDataSourceProxy
Это отдельные пути.
Так сохраняется различие между:
infrastructure context
и:
business request context
22.9. Разные операции требуют разных политик
Чтение и изменение данных не обязаны иметь одинаковую политику.
Например:
SELECT
может требовать READ.
А:
UPDATE
может требовать:
UPDATE
+
additional state condition
Поэтому RLS-политики должны соответствовать конкретной операции.
22.10. RLS не заменяет объектную авторизацию
Можно построить систему, где RLS полностью ограничивает строки.
Но это не означает, что RLS должен отвечать на все вопросы бизнес-авторизации.
Например:
Может ли пользователь согласовать объект?
Это может зависеть от роли, состояния, проекта и других отношений.
А вопрос:
Какие строки таблицы доступны этому SQL-запросу?
естественно решается на уровне RLS.
Разделение ответственности остаётся:
Authorization model
↓
Object authorization
↓
Data enforcement
22.11. Главный вывод
RLS — это не модель доступа.
Это механизм, который превращает решение модели безопасности в физическое ограничение данных.
Полная схема:
Source facts
↓
Security projections
↓
Effective authorization
↓
Object authorization
↓
RLS
↓
Physical rows
Чем сложнее отношения в модели, тем важнее не пытаться перенести их целиком в RLS.
База данных должна применять подготовленное правило к данным, а не заново строить всю модель мира.