Pull to refresh

Oracle. Безопасность на уровне строк

Reading time 5 min
Views 23K

Введение в проблему


В настоящее время в любой организации действует разграничение доступа к информации на основе определенных знаний о пользователе. Такими знаниями могут служить роль пользователя в организации, его должность либо структурное подразделение, в котором работает пользователь. Многим известно, что проблема ограничения доступа может быть решена с помощью простейших механизмов на основе имени пользователя, таблиц, представлений и триггеров.

Рассмотрим пример:
Предоставить менеджеру информацию о клиентах организации. При этом, менеджер может видеть только клиентов своего структурного подразделения, но не всей компании:

create table clients (
	clientid integer,
	clientname varchar2(30),
	clientphone varchar2(7),
	clientoffice integer
);


Решением «в лоб» является создание отдельного представления для каждого отдела компании. Например:

create or replace view clients_10 as
	select clientid, clientname, clientphone
	from clients
	where clientoffice = 10;


Таким образом, нам придется поддерживать количество представлений равное
<количество_фильтруемых_таблиц>*<количество_структурных_подразделений>,
а также каждого пользователя «направить» на нужные данные. Глупое и нудное занятие.
Сделаем попытку усовершенствовать предыдущий пример:

create or replace view v_clients as 
	select clientid, clientname, clietnphone 
	from office 
	where clientoffice = (select useroffice 
				from users 
				where username = user);


Решение, бесспорно, более интересное, но что же делать, когда одному пользователю необходимо «видеть» клиентов нескольких подразделений? Также часто возникает ситуация, когда одному пользователю разрешается только запрашивать данные из одной таблицы, в другой он может еще редактировать, но не удалять, а в третьей просматривать и удалять. Городить несметное количество представлений, триггеров и настоечных таблиц становится уж очень неудобно…

Технология RLS


Технология RLS (row-level security или безопасность на уровне строк) предоставляет возможность создания политик безопасности, которые ограничивают доступ пользователям к информации в БД. Как уже упоминалось выше, политики безопасности позволяют либо «закрыть» информацию полностью или частично, либо разрешить лишь определенные операции над ней. Технология была впервые представлена в Oracle 8i, но в последующих версиях ее возможности были значительно расширены.
При связи объекта БД с политикой безопасности контроль доступа осуществляется через логику, занесенную в специальную PL/SQL-функцию. Согласитесь, что гораздо проще поддерживать несколько программных функций, нежели десятки представлений, разнесенных по N-ому количеству схем.
При непосредственном или косвенном доступе пользователя к объекту БД (таблице, представлению) сервер динамически модифицирует оператор SQL, добавляя к нему предикат WHERE, который возвращается функцией безопасности.

Функция безопасности


Итак, теперь пришло время рассмотреть пример простейшей функции безопасности. Как уже отмечалось, задача функции – формирование предиката, который будет автоматически добавлен к запросу пользователя.

create or replace function policy_func (p_schema varchar2, p_object varchar2) 
return varchar2 is
begin
   if (user = 'MGR_10_20_30 ') then
      return 'clientoffice in (10, 20, 30)';
   elseif
      return 'clientoffice = (select useroffice from users where username = user)';
   end if;
end;


Приведенная выше функция будет добавлять предикат WHERE clientoffice = office_no к SQL запросу пользователя, где office_no – номер подразделения, в котором работает пользователь. Для пользователя с логином MGR_10_20_30 будет добавляться предикат WHERE clientoffice in (10, 20, 30). Таким образом, этот пользователь будет иметь доступ к информации о клиентах трех подразделений.

Процедуры пакета DBMS_RLS


Add_policy

Ну что ж, к данному моменту мы проделали всю необходимую подготовительную работу, и нам осталось лишь добавить политику безопасности в нашу БД. Политика безопасности регистрируется процедурой пакета DBMS_RLS add_policy:

DBMS_RLS.ADD_POLICY (
   object_schema IN VARCHAR2 NULL,
   object_name	IN VARCHAR2,
   policy_name	IN VARCHAR2,
   function_schema IN VARCHAR2 NULL,
   policy_function	IN VARCHAR2,
   statement_types IN VARCHAR2 NULL,
   update_check	IN BOOLEAN  FALSE,
   enable		IN BOOLEAN  TRUE,
   static_policy	IN BOOLEAN  FALSE,
   policy_type	IN BINARY_INTEGER NULL,
   long_predicate	IN BOOLEAN  FALSE,
   sec_relevant_cols IN VARCHAR2 NULL,
   sec_relevant_cols_opt IN BINARY_INTEGER NULL
);


Добавим политику безопасности для таблицы clients:

Begin
DBMS_RLS.ADD_POLICY (
   object_schema => 'myuser', 
   object_name	=> 'clients',
   policy_name	=> 'clients_policy',
   function_schema => 'myuser',
   policy_function	=> 'policy_func',
   statement_types => 'select, insert, update, delete',
   update_check	=> true
);
End;


Теперь вкратце об использованных нами параметрах процедуры:
object_schema и object_name – таблица, представление или синоним, для которого добавляется политика, и схема БД, в которой находится объект.
policy_name – имя добавляемой политики безопасности. Имя должно быть уникально для каждой таблицы или представления в отдельности.
function_schema и policy_function – имя функции безопасности, которая генерирует предикат, и схема БД, в которой находится функция.
statement_types – операторы, к которым применяется политика безопасности.
update_check – опция предотвращает операции INSERT или UPDATE, если INSERT или UPDATE нарушает условия поиска определенные в предикате.
Теперь мы можем посмотреть политику безопасности в действии:

select username, useroffice from users;

image


Для пользователя MGR_10:

select user from dual;

image


select * from clients;

image


Для пользователя MGR_20:

select user from dual;

image


select * from clients;

image


Для пользователя MGR_10_20_30:

select user from dual;

image


select * from clients;

image


Информация о представлениях в БД хранится в представлениях user_policies, all_policies, dba_policies. Выполним запрос из-под пользователя с правами DBA:

select * from dba_policies;

image


Drop_policy

Соответственно, раз есть процедура создания политики безопасности, то есть и процедура удаления ее:
DBMS_RLS.DROP_POLICY (
   object_schema IN VARCHAR2 NULL,
   object_name	IN VARCHAR2,
   policy_name	IN VARCHAR2
);


Попробуем удалить нашу политику безопасности:

Begin
DBMS_RLS.DROP_POLICY (
   object_schema => 'myuser',
   object_name	=> 'clients',
   policy_name	=> 'clients_policy'
);
End;


Проверим теперь наличие политик в БД юзером с правами DBA:

select * from dba_policies;

image


Enable_policy

Кроме операций добавления/удаления политик безопасности существует возможность временно приостановить действие существующих политик. Для этого служит процедура enable_policy, которая переводит политику в неактивное состояние (enable => false) или возобновляет действие политики (enable => true):

DBMS_RLS.ENABLE_POLICY (
   object_schema IN VARCHAR2 NULL,
   object_name	IN VARCHAR2,
   policy_name	IN VARCHAR2,
   enable		IN BOOLEAN TRUE)
);


select * from dba_policies;

image


Begin
DBMS_RLS.ENABLE_POLICY (
   object_schema => 'myuser',
   object_name	=> 'clients',
   policy_name	=> 'clients_policy',
   enable		=> false
);
End;


select * from dba_policies;

image



Begin
DBMS_RLS.ENABLE_POLICY (
   object_schema => 'myuser',
   object_name	=> 'clients',
   policy_name	=> 'clients_policy'
);
End;


select * from dba_policies;

image

Самые внимательные, я думаю, заметили параметр enable процедуры add_policy пакета dbms_rls. Этот параметр указывает на то, будет ли политика активной или неактивной сразу после создания. По умолчанию значение true.

Refresh_policy

Процедура позволяет обновить предикат политики RLS. Если политика безопасности определена с типом, отличным от DYNAMIC, предикат политики может некоторое время не обновляться, т.к. он кэширован в памяти. Поэтому если есть необходимость обновить политику немедленно, то нужно выполнить процедуру REFRESH_POLICY, которая заново выполнит функцию политики и обновит в кэше предикат.

DBMS_RLS.REFRESH_POLICY (
   object_schema IN VARCHAR2 NULL,
   object_name	IN VARCHAR2 NULL,
   policy_name	IN VARCHAR2 NULL
);


Заключение


В настоящей статье рассмотрен простейший пример, как говорится, «на пальцах». В следующей статье мы рассмотрим безопасность с использованием контекстов и групп политик.

Be secure! Commit!
Tags:
Hubs:
+29
Comments 20
Comments Comments 20

Articles