DeepEdit!

Программирование баз данных на Oracle, техническая документация, литература, статьи и публикации

  • Увеличить размер шрифта
  • Размер шрифта по умолчанию
  • Уменьшить размер шрифта

Тиражирование

При симметричном тиражировании (multi-master replication) схемам получателя и распространителя разрешено выбирать данные из таблиц без ограничений. Следовательно, вам придется или изменить функцию политики так, чтобы возвращать для таких пользователей предикат NULL, или предоставить им системную привилегию EXEMPT ACCESS POLICY.
Материализованные представления
При определении материализованных представлений необходимо убедиться в том, что владелец схемы материализованных представлений имеет неограниченный доступ к базовым таблицам. В противном случае запрос, определяющий материализованное представление, вернет только строки, удовлетворяющие предикату, что неверно. Как и в случае с тиражированием, следует изменить функцию политики так, чтобы возвращать для такого пользователя предикат NULL или предоставить ему системную привилегию EXEMPT ACCESS POLICY.
Контексты приложения
До этого момента все разговоры о безопасности на уровне строк велись в предположении, что предикат (то есть условие, ограничивающее доступ к строкам таблицы) не изменяется с момента входа пользователя в систему. Введем новое требование: пользователи должны видеть записи о сотрудниках в зависимости не от фиксированных номеров отделов, а от списка привилегий, специально поддерживаемых для этих целей. Таблица EMP_ACCESS хранит сведения о том, какому пользователю какая информация о сотрудниках доступна.


Пользователь Martin может видеть данные об отделах 10 и 20, а пользователь King - 10, 20, 30 и 40. Если имени пользователя в таблице нет, ему не должны быть видны никакие записи. По новым требованиям пользовательские привилегии могут меняться динамически посредством обновления таблицы EMP_ACCESS. Новые привилегии должны вступать в силу сразу, не требуя выхода пользователя из системы и его повторной регистрации.
Новые требования не позволяют полагаться на триггер LOGON при установке значений, используемых функцией политики.
Для соответствия новым условиям можно было бы создать пакет, переменная которого будет хранить предикат, а пользователя снабдить PL/ SQL-программой, которая присваивает значение этой переменной. Функция политики тогда могла бы использовать значение, кэширован- ное в пакете. Допустим ли такой подход? Давайте рассмотрим все внимательно. Если пользователь может изменить значение переменной пакета, что помешает ему присвоить ей значение высокого уровня доступа, как для пользователя King? Martin может войти в систему, задать значение переменной, обеспечивающее ему доступ к сведениям по всем отделам, и осуществить выборку всех записей таблицы. Исчезла конфиденциальность, что неприемлемо. Ведь именно чтобы защититься от подобных действий пользователя, мы обычно помещаем код установки значений, используемых функцией политики, в триггер LOGON.
Возможность динамического изменения пользователем значения переменной пакета требует от нас пересмотра стратегии. Необходимо задавать глобальную переменную каким-то безопасным способом, не допускающим неавторизованного изменения. К счастью, Oracle предоставляет нам такую возможность: следует использовать контексты приложения. Контекст приложения является аналогом глобальной переменной пакета: будучи единожды заданным, он доступен на протяжении всего сеанса и может быть задан повторно.
Контекст приложения также напоминает структуру языка C (struct) или запись языка PL/SQL. Он состоит из последовательности атрибутов, каждый из которых представляет собой пару имя-значение. Од нако, в отличие от своих аналогов в C и PL/SQL, атрибуты не именуются при создании контекста. Они получают имена и значения в процессе выполнения. Контексты приложений хранятся в глобальной области процесса (Process Global Area - PGA).
Механизм задания контекста приложения делает его использование более надежным, чем применение переменной пакета. Изменить значение контекста приложения можно только вызовом специальной программы PL/SQL. Разрешая изменение контекста приложения только специальной процедуре, вы можете обеспечить безопасность, необходимую для реализации политик, значения которых динамически меняются в течение одного сеанса.
Простой пример
Используем команду CREATE CONTEXT для определения нового контекста DEPT_CTX. Любой пользователь, обладающий системной привилегией
CREATE ANY CONTEXT и привилегией EXECUTE для пакета DBMS_SESSION, может создавать и настраивать контексты.

Обратите внимание на предложение USING set_dept_ctx. Оно указывает на то, что атрибут контекста dept_ctx может задаваться или изменяться только через вызов процедуры set_dept_ctx.
Теперь, если мы еще находимся в том же сеансе, которому принадлежит данная процедура, можно вызвать ее напрямую для установки атрибута DEPTNO в значение 10 следующим образом:
Пока мы еще не задали никаких атрибутов контекста, а только определили его в целом (имя и надежный механизм его изменения). Теперь создадим процедуру. Внутри нее мы будем присваивать значения атрибутам контекста при помощи функции SET_CONTEXT встроенного пакета DBMS_SESSION:
Для получения текущего значения атрибута вызываем функцию SYS_CONTEXT, которая принимает два параметра: имя контекста и имя атрибута, например:


Возможно, вы помните, что функция SYS_CONTEXT уже использовалась в этой главе для получения IP-адреса и имени терминала клиента.
Безопасность контекстов приложения
Процедура set_dept_ctx фактически инкапсулирует вызов функции SET_CONTEXT с определенными параметрами. Почему бы не вызывать встроенную функцию напрямую? Давайте посмотрим, что произойдет, если пользователь вызовет тот же самый фрагмент кода для установки значения атрибута DEPTNO в 10.

Обратите внимание на сообщение об ошибке: «ORA-01031: insufficient privileges» (недостаточно привилегий). Оно может привести в замешательство, так как пользователь как раз обладает необходимой привилегией EXECUTE на пакет DBMS_SESSION (без этой привилегии не удалось бы скомпилировать set_dept_ctx).
Недостаточность привилегий относится не к использованию DBMS_SESSI- ON, а к попытке задания значения контекста вне процедуры set_dept_ctx.
Как видите, Oracle «доверяет» задание значений контекста приложения только процедуре set_dept_ctx. В Oracle процедура, которая указана в предложении USING оператора CREATE CONTEXT, называется доверенной (trusted).
Выполнять доверенную процедуру могут только следующие схемы:
Схема, которой принадлежит данная процедура.
Любая схема, которой выдана привилегия EXECUTE для данной доверенной процедуры.
Таким образом, аккуратное распределение привилегий EXECUTE может обеспечить полный контроль над заданием значений данного контекста.
Доверенная процедура должна быть указана в момент создания контекста приложения. Только доверенная процедура сможет устанавливать значения контекста.

Контексты как предикаты RLS

Мы узнали о том, что для задания значения контекста должна использоваться процедура, а это аналогично использованию глобальной переменной пакета. У вас может возникнуть вопрос о разумности такого подхода: не создаем ли мы дополнительные сложности, ничего определенного при этом не добиваясь.


Нет. Доверенная процедура - это единственное средство задания значения атрибута контекста, и потому ее можно использовать для контроля выполнения. Внутрь доверенной процедуры можно поместить любые проверки корректности присвоения значения переменной. Можно даже полностью устранить передачу параметров и устанавливать значения из предопределенных значений, без ввода (и соответственно воздействия) со стороны пользователя. Например, возвращаясь к требованиям по управлению доступом к данным о сотрудниках, список номеров отделов для передачи в контекст приложения можно получать из таблицы EMP_ACCESS, а не от пользователя.
Будем использовать контекст приложения внутри самой функции политики. Начнем с изменения функции политики.

Функция политики ожидает передачи номеров отделов от атрибута DEPTNO_LIST контекста DEPT_CTX (строка 14). Для задания этого значения необходимо изменить доверенную процедуру контекста:


Давайте протестируем функцию. Сначала пользователь Martin входит в систему и вычисляет количество сотрудников. Перед выдачей запроса ему необходимо задать контекст.

Martin видит только данные сотрудников отделов 10 и 20, как и предусмотрено таблицей EMP_ACCESS.
Предположим теперь, что права доступа Martin изменены: теперь ему будут доступны записи об отделе 30, для чего выполнены соответствующие изменения в таблице EMP_ACCESS:

Когда Martin попытается выполнить тот же запрос, что и раньше, он получит другие результаты. Сначала выполняется хранимая процедура, задающая атрибут контекста.
SQL> EXEC rlsowner.set_dept_ctx
PL/SQL procedure successfully completed.
SQL> SELECT sys_context ('DEPT_CTX','DEPTNO_LIST') FROM dual;
SYS_CONTEXT('DEPT_CTX', 'DEPTNO_LIST')
DEPTNO IN (30)
SQL> SELECT DISTINCT deptno FROM hr.emp;
DEPTNO
30
Изменения вступают в силу автоматически. Как видите, Martin не указывает, какие отделы ему разрешено видеть, а просто вызывает хранимую процедуру set_dept_ctx, которая автоматически задает атрибуты контекста. Пользователь не может самостоятельно задать атрибуты контекста, что делает данный метод более надежным, чем использование глобальной переменной пакета (которую Martin мог бы напрямую установить в любое значение).
Что будет, если Martin не выполнит процедуру set_dept_ctx перед выдачей запроса SELECT? На момент выполнения запроса атрибут DEPT- NO_LIST контекста приложения DEPT_CTX будет содержать значение NULL, следовательно, предикат политики не будет включать в себя ни одного номера отдела. В результате Martin не сможет видеть данные ни об одном сотруднике.
Давайте внимательно проанализируем ситуацию. Мы создали предикат политики (другими словами, условие WHERE), который должен применяться к пользовательскому запросу. Мы решили, что будем сначала задавать атрибут контекста приложения, а функция политики будет обращаться к атрибуту контекста, а не к таблице EMP_ACCESS. Можно было бы сделать и так, чтобы функция политики обращалась напрямую к таблице EMP_ACCESS и создавала предикат: это значительно упростило бы написание функции политики. В этом случае пользователю не пришлось бы выполнять функцию политики при каждом входе в систему.
Однако функция политики, осуществляющая выборку из контекста приложения, а не напрямую из таблицы, имеет свои преимущества. Давайте сравним два подхода, используя псевдокод для представления базовой логики.
Сначала поместим все необходимые действия в функцию политики: осуществляем выборку из таблицы EMP_ACCESS и возвращаем строку предиката.
Получить имя пользователя
Цикл
Выбрать из таблицы EMP_ACCESS номера отделовкоторые доступны для данного имени пользователя
Составить список номеров отделов
Конец цикла
Вернуть список в качестве предиката
Теперь сделаем то же самое в процедуре set_dept_ctx:
Получить имя пользователя
Цикл
Выбрать из таблицы EMP_ACCESS номера отделовкоторые доступны для данного имени пользователя
Составить список номеров отделов
Конец цикла
Установить атрибут DEPTNO_LIST в значение полученного списка Тогда в функции политики будет выполняться только следующее:
Найти атрибут контекста DEPTNO_LIST
Вернуть его в качестве предиката политики
Обратите внимание на различия двух подходов. После входа пользователя в систему его имя в течение сеанса не изменяется. Поэтому функция set_dept_ctx может быть выполнена единожды - при начале сеанса, для задания атрибута контекста. Функция политики, созданная вокруг этого атрибута контекста, тем самым избегает обращения к базовой таблице EMP_ACCESS и полагается исключительно на память сеанса.
Если использовать ту версию функции политики, которая осуществляет выборку из таблицы, то операторам SQL, запускающим функцию политики, придется делать гораздо больше работы. То есть политики, которые обращаются ко всем необходимым данным через контексты приложения, могут значительно улучшить производительность операторов SQL, на которые наложены политики RLS.
В Oracle 10^ использование контекстов имеет дополнительное преимущество. Вы можете определить политику как контекстно-зависимую (см. раздел «RLS в Oracle 10g»), - это означает, что функция политики будет выполняться только при изменении контекста. Для нашего примера, в таком случае, функция политики будет выполнена всего один раз (когда пользователь входит в систему и задает контекст), поэтому политика будет применяться очень быстро. При изменении условий предоставления доступа пользователь повторно выполняет процедуру set_dept_ctx, которая повторно выполнит функцию политики.
Идентификация пользователей, не зарегистрированных в базе данных
Контексты приложения могут использоваться далеко за пределами тех ситуаций, которые были нами рассмотрены. Основное предназна чение контекстов приложения в том, чтобы различать пользователей, которых невозможно идентифицировать на основе уникальности сеансов. Веб-приложения регулярно используют пул соединений с базой данных, когда соединение осуществляется через некоторого пользователя, например CONNPOOL. Веб-пользователи подключаются к серверу приложений, который в свою очередь использует одно из соединений пула для обращения к базе данных (рис. 5.2).
Пользователи Martin и King не являются пользователями базы данных. Это веб-пользователи, и база данных не обладает никакими специфическими сведениями о них. Пул соединений подключается к базе данных через пользователя с идентификатором CONNPOOL, который зарегистрирован в базе данных. Когда Martin запрашивает что-то из базы данных, пул может решить использовать соединение, помеченное номером 1, для получения данных. После выполнения запроса соединение переходит в режим ожидания. Если в этот момент пользователь King захочет выполнить запрос, пул вполне может решить использовать то же самое соединение (помеченное 1). С точки зрения базы данных сеанс (который на самом деле является соединением из пула) относится к пользователю CONNPOOL. Поэтому в рассмотренном ранее примере (где функция USER идентифицирует реальное имя схемы) невозможно будет добиться уникальной идентификации пользователя, выполняющего вызовы. Функция USER будет всегда возвращать CONN- POOL, так как к базе данных подключен именно этот пользователь.
Тут в дело вступает контекст приложения. Предположим, что существует контекст WEB_CTX с атрибутом WEBUSER. При получении запроса от клиента это значение устанавливается пулом соединений в имя реального пользователя (например, Martin). Политика RLS может основываться на данном значении, а не на имени пользователя базы данных.

Посмотрим, как это будет работать. Пусть у нас есть банковское приложение, в котором доступ к записям клиентов осуществляют несколько менеджеров. Необходимо построить политику RLS так, чтобы каждый менеджер видел счета только собственных клиентов. Столбец ACC_MGR хранит имя пользователя для менеджера, работающего с соответствующим счетом. Тогда предикат политики может быть таким:

где AccountManagerUserName - это идентификатор пользователя Windows- менеджера (то есть информация, не известная базе данных). Данное значение должно быть передано пулом соединений базе данных посредством контекстов.
Основная процедура, задающая контекст, будет выглядеть следующим образом:
Начнем с создания контекста:
Процедура принимает один параметр, имя реального пользователя (веб- пользователя). Именно эти данные будут использованы приложением для задания контекста WEB_CTX. Проверим, что процедура работает:

Приведенная процедура задания контекста (set_web_ctx) очень проста. Она лишь задает атрибут контекста. В реальной жизни вам придется писать множество строк кода, выполняющих различные проверки на наличие у вызывающего соответствующих прав, и т. д. Например, сервер приложений под управлением Windows может напрямую извлекать имя пользователя с клиентского компьютера и передавать его в контекст, используя описанную выше процедуру.
Задав контекст, используем его для построения функции политики:


Функция политики возвращает предикат acc_mgr = 'имя_пользовате- ля, который применяется к пользовательским запросам. Пользователь автоматически получает доступ только к собственным записям.
Заключение
Средства RLS чрезвычайно важны для обеспечения безопасности базы данных на уровне строк. Область применения RLS (учитывая всю полезность этой технологии для требующих защиты приложений и баз данных) выходит за рамки обеспечения безопасности. Средства RLS могут использоваться для ограничения доступа к некоторым строкам таблицы, избавляя от необходимости модифицировать приложения при изменении условий запроса; возможен также выборочный перевод таблиц в режим доступа только для чтения. Используя комбинации переменных внутри функции политики, вы можете создать настраиваемое представление данных внутри таблицы, что позволит удовлетворить потребности пользователей и создать удобное для поддержки приложение.
 









jAntivirus