Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Глава 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.

База данных должна применять подготовленное правило к данным, а не заново строить всю модель мира.