Row-Level Security (RLS) — это технология безопасности на уровне строк данных, позволяющая ограничивать доступ пользователей к конкретным строкам таблиц в зависимости от контекста их сессии или роли․ В экосистеме Oracle эта технология реализуется через Virtual Private Database (VPD) и пакет DBMS_RLS․ Статья подробно рассматривает условия срабатывания политик, необходимые привилегии, архитектуру предикатов и нюансы администрирования․
Архитектура и Основные Компоненты
Механизм RLS (VPD) работает на уровне ядра базы данных, прозрачно модифицируя SQL-запросы пользователей путем добавления динамически генерируемых предикатов (условий WHERE)․
Ключевые элементы:
- Application Context (Контекст приложения): Набор пар «имя-значение», доступных в сессии․ Используется политиками для принятия решений (например,
CLIENT_IDENTIFIER,USER_ID,DEPARTMENT_ID)․ - Policy Function (Функция политики): PL/SQL-функция, возвращающая строку-предикат (например,
'DEPT_ID = SYS_CONTEXT(''APP_CTX'', ''DEPT_ID'')')․ - DBMS_RLS Package: Административный интерфейс для привязки политик к таблицам/представлениям․ Доступен только в Enterprise Edition․
Условия Срабатывания Политик (Policy Conditions)
Политики RLS не применяются ко всем пользователям и операциям безусловно․ Существует четкая иерархия проверок:
- Проверка привилегий на объект: Сначала Oracle проверяет стандартные привилегии (
SELECT,INSERT,UPDATE,DELETE) на таблицу․ Если их нет — запрос завершается ошибкой до вызова RLS․ - Проверка владельца и BYPASSRLS: По умолчанию политики не применяются к:
- Владельцу таблицы (схеме-владельцу объекта)․
- Пользователям с системной привилегией
EXEMPT ACCESS POLICY(ранее известной какBYPASSRLS)․ - Пользователям с ролью
DBA(если им енаEXEMPT ACCESS POLICY)․
- Тип операции (Statement Types): Политика может быть назначена на конкретные операции:
FOR SELECTFOR INSERTFOR UPDATEFOR DELETEFOR ALL(по умолчанию, если не указано иное)․
- Вычисление предиката: Если проверки пройдены, для каждой применимой политики вызывается функция-политика․ Возвращаемый предикат конкатенируется к исходному запросу через
AND․
Пример сложного предиката
Функция политики может возвращать выражения произвольной сложности, включая подзапросы:
AND (
512 IN (SELECT id_user FROM v_admin_users WHERE id_department = 255)
OR
512 IN (SELECT id_user FROM v_bypass_rls)
) Примечание: Предикаты должны быть детерминированными в рамках одного выполнения запроса для корректной работы кэша курсоров (если используется STATIC или CONTEXT_SENSITIVE типы политик)․
Привилегии и Роли для Администрирования RLS
Управление политиками требует высоких привилегий, разделенных на уровни ответственности․
Системные привилегии
| Привилегия | Описание | Кому назначается |
|---|---|---|
EXECUTE ON DBMS_RLS | Базовая привилегия для вызова процедур добавления/удаления/включения политик (ADD_POLICY, DROP_POLICY, ENABLE_POLICY)․ | Разработчикам БД, админам безопасности приложений․ |
EXEMPT ACCESS POLICY | Полный обход всех политик VPD/RLS для всех таблиц во всех схемах․ Опасно! | Только супер-администраторам (SYS, SYSTEM), утилитам экспорта/импорта (Data Pump), DBA для аварийного доступа․ |
ALTER ANY SECURITY POLICY | Разрешает изменение политик безопасности в любой схеме․ Предназначена для Security Policy Administrator (Диспетчера политик безопасности)․ | Специализированной роли безопасности (Security Officer), не DBA․ |
Объектные привилегии
- Для создания политики на чужой таблице требуется
SELECTна эту таблицу (для чтения метаданных) +EXECUTE ON DBMS_RLS․ - Функция-политика выполняется с правами создателя политики (Definer’s Rights), а не вызывающего пользователя․ Поэтому создателю политики нужны права на объекты, используемые внутри функции (доступ к контекстам, таблицам справочникам и т․д․)․
Типы Политик и Производительность (Policy Types)
Выбор типа политики критически влияет на производительность, так как определяет частоту вызова функции-политики․
- DYNAMIC (По умолчанию): Функция вызывается при каждом выполнении запроса (parse/execute)․ Максимальная гибкость, накладные расходы на CPU․
- STATIC: Предикат вычисляется один раз при первом парсинге и кэшируется в SGA․ Подходит, если предикат зависит только от неизменяемых в рамках сессии атрибутов (например, фиксированная роль пользователя)․ Не подходит, если контекст меняется часто․
- CONTEXT_SENSITIVE: Предикат кэшируется до изменения контекста приложения (Namespace/Attribute)․ Оптимальный баланс для большинства приложений с
DBMS_SESSION․SET_CONTEXT․ - SHARED_CONTEXT_SENSITIVE / SHARED_STATIC: Варианты для совместного использования курсоров между пользователями с одинаковым контекстом (для пулов соединений)․
Временное Отключение RLS (Runtime Control)
В некоторых сценариях (пакетная загрузка данных, отчеты администратора, отладка) требуется отключить RLS для текущей сессии без выдачи привилегии EXEMPT ACCESS POLICY․
Стандартные подходы:
- Пакетная переменная-флаг (Application Level): В коде приложения заводится пакет (например,
PRVD_PKG) с переменнойoutRLS NUMBER := 0․-- В функции политики: IF PRVD_PKG․outRLS = 1 THEN RETURN NULL; -- Предикат не добавляется END IF;Переменная устанавливается в
1перед критической операцией и сбрасывается в0в процедурах-оберткахStartPrvd92/FinishPrvd(или черезDBMS_SESSION․SET_CONTEXT)․ - DBMS_RLS․ENABLE_POLICY / DISABLE_POLICY: Административное отключение политики для всех пользователей․ Требует привилегий
EXECUTE ON DBMS_RLS․ Используется для обслуживания (maintenance windows)․ - ALTER SESSION SET «_vpd_policy_optimization» = FALSE: (Скрытый параметр, не рекомендуется без поддержки Oracle Support)․
Особенности в Мультитенантной Среде (CDB/PDB)
В архитектуре Multitenant (12c+):
- Политики RLS создаются локально внутри PDB (Pluggable Database)․
- Контексты приложений могут быть LOCAL (видимы только в текущем PDB) или GLOBAL (видимы во всем CDB, создаются в CDB$ROOT)․
- Привилегия
ALTER ANY SECURITY POLICYможет быть предоставлена как локально (в PDB), так и глобально (в CDB$ROOT сCONTAINER=ALL)․ - Дополнительное разделение политик для разных контейнеров требуется только при сложных схемах изоляции данных между PDB, а не как стандарт для каждого случая․
Типичные Ошибки и Лучшие Практики
Ошибки:
- Использование SYSDATE / USERENV в предикате без CONTEXT_SENSITIVE: Приводит к некорректному кэшированию куроров (предкат «застывает» на момент первого парсинга)․
- Слишком сложные предикаты: Подзапросы к большим таблицам внутри функции политики убивают производительность (Full Scan на каждый вызов)․ Решение: материализованные представления, кэширование в PGA/Package State, индексы․
- Циклические зависимости: Функция политики запрашивает таблицу, на которой висит эта же политика (или другая, вызывающая первую)․ Приводит к ORA-04091 (mutating table) или рекурсии․
- Игнорирование INSERT/UPDATE: Политика только на
SELECTпозволяет пользователю вставить/обновить строку, которую он потом не увидит (или увидит чужую)․ Всегда используйтеFOR ALLили явный перечень операций․
Лучшие практики:
- Используйте Application Contexts для передачи параметров безопасности, а не параметры функции (функция политики не принимает пользовательских аргументов)․
- Тип политики по умолчанию, CONTEXT_SENSITIVE․
- Выносите логику проверки прав в отдельные представления (Views) или таблицы конфигурации, чтобы функция политики была легкой:
RETURN 'SEC_ID IN (SELECT sec_id FROM user_sec_v WHERE user_id = SYS_CONTEXT(․․․))'․ - Аудируйте назначение
EXEMPT ACCESS POLICY— это «мастер-ключ» от всех данных․ - Тестируйте планы выполнения запросов с включенным RLS (
EXPLAIN PLANпоказывает добавленные предикаты в секцииPREDICATE INFORMATION)․
Пример Полного Цикла Создания Политики
-- 1․ Создание контекста
CREATE OR REPLACE CONTEXT app_security_ctx USING pkg_security_ctx;
-- 2․ Пакет установки контекста (вызывается при логине/смене роли)
CREATE OR REPLACE PACKAGE BODY pkg_security_ctx AS
PROCEDURE set_dept(p_dept_id NUMBER) IS
BEGIN
DBMS_SESSION․SET_CONTEXT('app_security_ctx', 'dept_id', p_dept_id);
END;
END;
-- 3․ Функция-политика (возвращает VARCHAR2)
CREATE OR REPLACE FUNCTION f_rls_dept_pred (
p_schema VARCHAR2, p_object VARCHAR2
) RETURN VARCHAR2 AS
v_pred VARCHAR2(4000);
BEGIN
-- Если контекст не установлен (админ/тех․ юзер) ⎼ доступ закрыт или открыт по стратегии
IF SYS_CONTEXT('app_security_ctx', 'dept_id') IS NULL THEN
RETURN '1=0'; -- Запрет всего
END IF;
v_pred := 'department_id = ' || SYS_CONTEXT('app_security_ctx', 'dept_id');
RETURN v_pred;
END;
-- 4․ Привязка политики (CONTEXT_SENSITIVE ⎻ кэш до смены dept_id)
BEGIN
DBMS_RLS․ADD_POLICY(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'SEC_DEPT_POLICY',
function_schema => 'SEC_MGR',
policy_function => 'F_RLS_DEPT_PRED',
statement_types => 'SELECT, INSERT, UPDATE, DELETE',
policy_type => DBMS_RLS․CONTEXT_SENSITIVE,
sec_relevant_cols => 'DEPARTMENT_ID', -- Опционально: только для колонок
update_check => TRUE -- Проверка предиката при UPDATE/INSERT
);
END;
/
Технология RLS (VPD) в Oracle предоставляет мощный, централизованный и прозрачный для приложений механизм реализации Fine-Grained Access Control (FGAC)․ Ключевыми факторами успеха являются:
- Правильный выбор типа политики (Dynamic vs Context Sensitive) для баланса безопасности и производительности․
- Строгое разграничение привилегий администрирования (
EXECUTE ON DBMS_RLSvsALTER ANY SECURITY POLICYvsEXEMPT ACCESS POLICY)․ - Использование Application Context как единственного легального канала передачи контекста в предикаты․
- Обязательное покрытие всеми типами DML-операций (
FOR ALL) и включениеUPDATE_CHECK => TRUE․
Грамотная реализация RLS позволяет вынести логику безопасности из кода приложений (Java, ․NET, PL/SQL API) непосредственно в ядро БД, гарантируя защиту данных даже при прямом доступе через SQL*Plus, BI-инструменты или уязвимости приложения (SQL Injection)․
Статья подготовлена на основе документации Oracle Database Security Guide, пакета DBMS_RLS и лучших практик внедрения VPD․ Дата актуализации: ․