Управление доступом
Доступ на уровне строк
16
Авторские права
© Postgres Professional, 2017–2026
Авторы: Егор Рогов, Павел Лузанов, Илья Баштанов, Алексей Береснев
Фото: Олег Бартунов (монастырь Пху и пик Бхрикути, Непал)
Использование материалов курса
Некоммерческое использование материалов курса (презентации,
демонстрации) разрешается без ограничений. Коммерческое
использование возможно только с письменного разрешения компании
Postgres Professional. Запрещается внесение изменений в материалы
курса.
Обратная связь
Отзывы, замечания и предложения направляйте по адресу:
edu@postgrespro.ru
Отказ от ответственности
Компания Postgres Professional не несет никакой ответственности за
любые повреждения и убытки, включая потерю дохода, нанесенные
прямым или непрямым, специальным или случайным использованием
материалов курса. Компания Postgres Professional не предоставляет
каких-либо гарантий на материалы курса. Материалы курса
предоставляются на основе принципа «как есть» и компания Postgres
Professional не обязана предоставлять сопровождение, поддержку,
обновления, расширения и изменения.
2
Темы
Задача доступа на уровне строк
Реализация с помощью функций
Реализация с помощью представлений
Политики защиты строк
3
Доступ на уровне строк
Управление доступом на основе ролей
разграничение на уровне отношений (столбцов)
роли и привилегии, базовый механизм
Управление доступом на уровне строк
разграничение на уровне отдельных строк отношений
требуется доверенный контекст
различные способы реализации
Роли и привилегии реализуют ролевую модель доступа (Role-Based
Access Control, RBAC), базовый механизм управления доступом
PostgreSQL. Эта модель регламентирует доступ к отношениям
(таблицам, представлениям), возможно к отдельным их столбцам.
Более детальное разграничение информации на уровне отдельных
строк как правило реализуется в приложениях, но может возникнуть
потребность ограничить доступ и на уровне базы данных. Такое
разграничение требует учета детальных условий (Fine-Grained Access
Control, FGAC).
Критерии доступа должны быть основаны на информации, которой
можно доверять. Очевидно, что нельзя полагаться ни на какие данные,
передаваемые самим пользователем (параметры функций, условия в
запросах и т. п.), поскольку злоумышленник может их подменить. По
сути, единственное, чему можно доверять — это имя пользователя,
поскольку СУБД проводит его проверку (аутентификацию). Если же
аутентификация клиентов выполняется приложением, задача
осложняется (см. задание Практики+).
Существуют разные подходы к решению задачи, основные из которых
рассмотрены дальше в этой теме.
4
Функции
Подход к решению
Характеристика безопасности
5
Табличные функции для чтения данных
Обычные функции или процедуры для изменения данных
Подход к решению
EXECUTE
SELECT …
FROM …
WHERE условия
проверки
INSERT INTO …
EXECUTE
Одно из возможных решений состоит в предоставлении пользователю
API, реализованного с помощью функций. Для чтения данных
применяется табличная функция (возвращающая множество строк).
К такой функции можно обращаться в предложении FROM как
к таблице, а при необходимости можно и передавать параметры
(которые функция может использовать, в том числе, для фильтрации
данных).
Для изменения данных можно использовать обычные функции; перед
собственно изменением в них можно выполнить все необходимые
проверки.
Привилегий на базовую таблицу пользователь иметь не должен, ему
необходима только привилегия EXECUTE на интерфейсные функции
(такая привилегия есть у псевдороли PUBLIC по умолчанию).
7
Права для подпрограмм
SECURITY INVOKER (по умолчанию)
выполняется с правами вызывающего
тело функции может подставляться в запрос
SECURITY DEFINER
выполняется с правами владельца
тело функции никогда не подставляется в запрос
Как мы уже знаем, для подпрограмм (функций и процедур) есть
единственная привилегия EXECUTE, разрешающая выполнение этой
подпрограммы.
При этом важно, от имени какого пользователя будет выполняться
подпрограмма. Если подпрограмма объявлена как SECURITY INVOKER
(по умолчанию), она выполняется с правами вызывающего
пользователя. В этом случае операторы внутри подпрограммы смогут
обращаться только к тем объектам, к которым у вызывающего
пользователя есть доступ.
Если же указать фразу SECURITY DEFINER, подпрограмма работает
с правами ее владельца. Так можно позволить другим пользователям
выполнять определенные действия над объектами, к которым у них нет
непосредственного доступа.
Тонкий момент состоит в том, что при определенных условиях тело
SQL-функции с характеристикой безопасности SECURITY INVOKER
может подставляться в основной запрос (рассматривается подробно
в курсах QPT и DEV2) — обычно это хорошо, поскольку планировщик
получает больше свободы и может найти более оптимальный план.
Но тело функции с характеристикой SECURITY DEFINER никогда не
подставляется. Таким образом, сначала функция полностью
вычисляется, и только затем к полученному результату применяются
дополнительные условия. Это может быть менее эффективно.
9
Представления
Подход к решению
Барьер безопасности и герметичные функции
10
Подход к решению
Представления для чтения
Обновляемые представления или триггеры для изменения
SELECT, INSERT, ...
проверки
INSERT INTO …
SELECT …
FROM …
WHERE условия
Другое решение состоит в том, чтобы предоставить пользователю
доступ к представлению, фильтрующему данные базовой таблицы, но
не к самой таблице.
В простом случае, когда представление строится на одной таблице,
не использует группировку и т. д., представление будет обновляемым
и для него будут работать команды INSERT, UPDATE, DELETE, MERGE.
В более сложном случае для обновления придется создать триггеры
INSTEAD OF (триггеры подробно обсуждаются в одноименной теме
курса DEV1).
12
Защита от потенциально опасных (негерметичных) функций
Барьер безопасности
SELECT …
FROM view
WHERE условие1 AND условие2
SELECT …
FROM ...
WHERE фильтр
AND условие1 AND условие2
SELECT …
FROM (
SELECT …
FROM ...
WHERE фильтр
AND условие2
)
WHERE условие1
барьер
безопасности
не содержит
негерметичных
функций
Как мы только что видели, фильтрация строк в представлении
ненадежна, если пользователь может создавать функции. Проблема
в том, что представление прозрачно для планировщика: по сути, текст
представления подставляется в запрос пользователя и получившийся
запрос оптимизируется целиком. Поэтому дополнительное условие
может быть вычислено раньше, чем условие из представления.
С функциями такой проблемы не возникает, они непрозрачны: сначала
вычисляется результат, потом к получившемуся набору строк
применяются дополнительные условия. Исключение составляет
подстановка тела SQL-функций в запрос (inlining), но для функций
с характеристикой SECURITY DEFINER подстановка никогда не
выполняется.
Для борьбы с потенциально опасными функциями представление
надо объявить с барьером безопасности. Барьер гарантирует
безопасный порядок вычислений: условия самого представления будут
применены до любых условий, добавленных пользователем. При этом
через барьер проникают условия, содержащие простые вычисления,
функции, не получающие данные из представления, а также функции,
которые специально помечены как герметичные (через которые
секретная информация гарантированно не утечет). Это дает больше
свободы планировщику и увеличивает шансы найти оптимальный план
выполнения.
14
Политики защиты строк
Подход к решению
Условия применения политик
Комбинирование политик
15
Доступ к базовой таблице со включенной защитой строк
Подход к решению
SELECT, INSERT, ...
USING условия
вычисляются
с правами
вызывающего
Третий подход заключается в использовании специально созданного
механизма — политик защиты строк (row-level security, RLS). В этом
случае пользователю предоставляется доступ к самой таблице,
а проверка условий, заданных в политике, происходит автоматически.
По сути, политика работает так же, как представление с барьером
безопасности: гарантируется, что дополнительные условия на выборку
из таблицы будут вычислены после того, как будут отфильтрованы
строки, не удовлетворяющие политике (за исключением безопасных
выражений с герметичными функциями). Как и в представлении,
условия, описанные в политике, вычисляются с правами вызывающего.
Однако то, что у пользователя есть доступ к самой таблице, добавляет
определенные нюансы.
16
Условия применения
Политика применяется
к таблице, для которой включена защита
для указанных ролей и для указанных операторов
(SELECT, INSERT, UPDATE, DELETE)
Политика не применяется
при проверке ограничений целостности
для суперпользователей и ролей с атрибутом BYPASSRLS
для владельца (если не включить принудительно)
Применение приводит к ошибке
при создании логической копии утилитой pg_dump
row_security = off
Чтобы политики защиты строк начали работать, нужно явно включить
этот механизм для каждой таблицы.
При создании политики можно также задать, для каких ролей она будет
работать (по умолчанию — для всех) и для каких операторов
(по умолчанию — также для всех).
Политики не применяются при проверке ограничений целостности —
независимо от настроенных политик СУБД гарантирует целостность
данных.
Политики не применяются для суперпользователей (для них, как
обычно, никакие проверки безопасности не выполняются) и для ролей
с атрибутом BYPASSRLS.
Для владельца таблицы политики не работают по умолчанию, но
специальной командой можно включить защиту и для владельца.
При выполнении логической резервной копии утилита pg_dump
устанавливает параметр row_security в значение off. Это не отключает
политику защиты строк, но фиксирует ошибку, если хотя бы одна строка
была отфильтрована. Таким образом гарантируется, что созданная
копия будет полной.
18
Комбинирование политик
Разрешительные политики
видимость должна предоставить хотя бы одна разрешительная политика
если не определена ни одна политика, строка не видима
Ограничительные политики
видимость должны предоставить все ограничительные политики,
если они определены
На одной таблице можно определить несколько политик. В этом случае
будут учитываться условия всех политик.
По умолчанию создаются разрешительные (permissive) политики.
Чтобы строка была доступна, достаточно, чтобы хотя бы одно из
условий таких политик оказалось истинным.
Но если на таблице включена защита строк, и при этом не определено
ни одной разрешительной политики, не будет доступна ни одна строка.
Дополнительно можно создать и ограничительные (restrictive)
политики. Если они заданы, то все их предикаты должны быть истинны.
Иными словами, если определены только разрешительные политики
с условиями P
1
,…, P
N
, то для каждой строки вычисляется выражение
P
1
OR … OR P
N
.
А если к тому же определены ограничительные политики с предикатами
R
1
,…, R
M
, то для каждой строки будет вычисляться выражение
(P
1
OR … OR P
N
) AND R
1
AND … AND R
M
.
То есть чтобы строка была видна, доступ к ней должны предоставить
хотя бы одна разрешительная и все ограничительные политики.
20
Итоги
Ограничения доступа на уровне строк реализуют
с помощью
табличных функций, вызываемых с правами владельца
представлений с барьером безопасности
политик защиты строк
Каждый способ реализации доступа по строкам имеет
свои особенности и ограничения
21
Практика
1. Дополните пример из демонстрации таблицей staff
с распределением сотрудников по отделам. Настройте
разграничение доступа на уровне строк с помощью функции
так, чтобы пользователи видели суммы только сотрудников
своего отдела.
2. Решите ту же задачу с помощью представления.
3. Решите ту же задачу с помощью политик защиты строк.
22
Практика+
1. Выполните настройку политик защиты строк, как описано
в задании 3 Практики.
2. Создайте представление, показывающее итоговые суммы
по отделам, и выдайте доступ к нему пользователям.
3. Убедитесь, что запрос к представлению не учитывает
политики: любой сотрудник может видеть итоги по всем
отделам.
4. Измените представление так, чтобы любой сотрудник видел
итоги только своего отдела.
4. Установите у представления параметр security_invoker.