DeepEdit!

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

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

Повышение безопасности

Несомненно, основной целью RLS является повышение уровня безопасности внутри компании. Для многих компаний RLS обеспечивает соответствие новым правилам и инструкциям по обеспечению безопасности и конфиденциальности (например, Sarbanes-Oxley, HIPAA, Visa Cardholder Information Security Program), которые они обязаны выполнять. В наши дни безопасность - это не второстепенный вопрос, интересующий лишь затерянных где-то в глубине корпоративных джунглей аудиторов. Теперь это важная составляющая общего процесса проектирования и разработки системы. Сегодня каждый, начиная с самого младшего разработчика и заканчивая самым маститым администратором базы данных, должен быть хорошо знаком с инструментами и технологиями обеспечения безопасности. Oracle предоставляет множество дополнительных передовых возможностей и опций обеспечения безопасности, но технология RLS встроена в сервер базы данных Oracle и является первым средством, которое следует использовать для реализации политик безопасности. И новичок администрирования баз данных, и ветеран, за плечами которого годы разработки на PL/SQL, быстро поймут, что близкое знакомство с RLS поможет аккуратно интегрировать функции обеспечения безопасности в их базу данных.
Упрощение разработки и поддержки
RLS позволяет собрать всю логику политики безопасности в набор пакетов, содержащих хорошо структурированные функции PL/ SQL. Даже если бы вы смогли реализовать имеющиеся требования безопасности на уровне строк при помощи представлений, хотели бы вы так поступить? Чтобы удовлетворить сложные бизнес-условия, требуются весьма витиеватые SQL-конструкции. По мере введения вашей компанией новых политик безопасности или усовершенствования старых, а также при вступлении в силу новых постановлений правительства вам нужно будет как-то переводить их на язык SQL для соответствующего изменения ваших представлений. Гораздо проще внести изменения в PL/SQL-функции, собранные
в небольшом количестве пакетов, и тем самым позволить Oracle автоматически применять ваши правила к определенным таблицам (вне зависимости от способа доступа). Упрощение «коробочных» приложений
Простота разработки влечет за собой простоту адаптации «коробочных» приложений, разработанных третьими фирмами. Даже если бы задача изменения каждого запроса в приложении представлялась осуществимой, вам все равно не удалось бы проделать это для «коробочных» приложений в связи с отсутствием их исходных текстов. Потребовалась бы помощь поставщика программного обеспечения. Эта проблема особенно касается унаследованных систем: большинство компаний боится что-то менять в таких системах, даже если речь идет просто о добавлении дополнительного предиката. На помощь приходит технология RLS, не требующая внесения изменений в код. Вы можете проникнуть под код стороннего приложения, полностью минуя его логику, и добавить собственные политики для таблиц, с которыми работает этот код. Управление доступом на запись
RLS обеспечивает гибкий быстрый и простой способ изменения уровня доступа к таблицам и представлениям с «только чтение» на «чтение и запись» на основании мандата пользователя. Встроенные команды администрирования Oracle позволяют определить табличные пространства в целом как доступные только для чтения или для чтения/записи. Технология RLS заполняет этот пробел, позволяя применить такие же правила к отдельным таблицам.
Простой пример
Давайте рассмотрим простой пример использования RLS. Будем работать с таблицей EMP в схеме HR, созданной при помощи сценария, поставляемого в составе программного обеспечения Oracle в файле $ORA- CLE_HOME/sqlplus/demo/demobld.sql.


Зададим очень простое требование. Пусть необходимо, чтобы пользователи могли видеть данные только тех сотрудников, чья заработная плата не превышает 1500 долларов. Предположим, что пользователь вводит такой запрос:

Хотелось бы, чтобы средства RLS прозрачно преобразовывали этот за прос в такой:

То есть при запросе пользователем данных из таблицы EMP Oracle (используя механизм RLS) будет автоматически применять необходимое ограничение. Чтобы все было именно так, надо сообщить Oracle об этих требованиях.
Сначала необходимо написать функцию, которая создает и возвращает такой предикат в виде строки. Простота данного требования позволяет использовать автономную функцию. В реальных приложениях вам придется определять функции предикатов и связанную с ними функциональность в пакетах. От имени пользователя HR создадим функцию authorized emps.

Обратите внимание, что оба аргумента (имя схемы и объекта) не используются внутри функции. Их наличие является требованием архитектуры RLS. Другими словами, каждая функция предиката должна получать эти два аргумента (ниже мы рассмотрим этот вопрос более подробно).
Вывод будет таким:
При выполнении функция возвращает наш предикат: SAL <= 1500. Проверим это, используя тестовый сценарий:
Имея функцию, возвращающую предикат, можно перейти к следующему шагу: созданию политики безопасности, также называемой политикой RLS или просто политикой. Эта политика определяет, когда и как предикат будет применяться к командам SQL. Для определения безопасности на уровне строк для таблицы EMP используем такой код:

Давайте внимательно посмотрим на то, что здесь происходит. Добавляется политика EMP_POLICY (строка 4) для таблицы EMP (строка 3), принадлежащей схеме HR (строка 2). Эта политика будет применять фильтр, задаваемый функцией AUTHORIZED_EMPS (строка 6), принадлежащей схеме HR (строка 5), при выполнении любым пользователем операции INSERT, UPDATE, DELETE или SELECT (строка 7).
Определив политику, мы можем сразу протестировать ее, выполнив запрос к таблице EMP:

Как видите, выбрано только 7 строк, а не все 14. Присмотревшись, вы заметите, что во всех выбранных строках значение столбца SAL или равно 1500, то есть соответствует функции предиката.

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

Oracle применяет такую фильтрацию на самом нижнем уровне, поэтому пользователи о ней даже не подозревают. Фактически они не знают о том, что им что-то недоступно, и это еще одно ценное свойство RLS с точки зрения безопасности.
Основной поток данных и реализуемая механизмом RLS фильтрация изображены на рис. 5.1.
Когда пользователь обращается к таблице, находящейся под контролем RLS, оператор SQL перехватывается и переписывается сервером базы данных с добавлением предиката, полученного от функции политики безопасности. Если функция политики безопасности возвращает корректное предложение-предикат, то он применяется к исходному оператору пользователя.
Политики не являются объектами схемы базы данных. Другими словами, они не принадлежат никакому пользователю. Любой пользователь, обладающий привилегией EXECUTE на пакет DBMS_RLS, может создать политику. Аналогично любой пользователь с привилегией EXECUTE может удалить любую политику. Поэтому необходимо очень внимательно подходить к выдаче прав
на работу с пакетом DBMS_RLS. Если кто-то выдаст привилегию EXECUTE на пакет для PUBLIC, ее надо немедленно отозвать.
Вы можете создавать функции политики безопасности любой сложности, описывающие практически любые требования к приложению. Однако все эти функции должны следовать нескольким правилам:
Функция политики безопасности должна быть самостоятельной функцией в схеме или в составе пакета, но ни в коем случае не процедурой.
Она должна возвращать значение типа VARCHAR2, которое будет использоваться как предикат.
Функция должна иметь ровно два входных параметра, следующих в определенном порядке:
имя схемы, которой принадлежит таблица, для которой определена политика;
имя объекта (таблицы или представления), к которому применяется политика.
Для просмотра политик, определенных для таблицы, можно обратиться к представлению словаря данных DBA_POLICIES, которое отображает имя политики, имя объекта, для которого она определена (и его владельца), имя функции политики (и ее владельца) и многое другое. Полный перечень столбцов данного представления приведен в приложении A.
Если вы хотите удалить существующую политику RLS, то можете использовать программу DROP_POLICY из пакета DBMS_RLS. Примеры такого использования будут приведены в главе далее.
Кратко о политиках RLS
Политика безопасности - это набор инструкций, которые применяются для обеспечения контроля над таблицей на уровне строк. Политика не является объектом схемы и не принадлежит ни одному из пользователей.
Oracle использует политику для определения того, когда и как следует применять предикат ко всем операторам SQL, ссылающимся на таблицу.
Предикат создается и возвращается функцией политики безопасности.
Использование RLS
После того как мы на примере познакомились с основами применения RLS, давайте перейдем к примерам, иллюстрирующим достоинства различных аспектов технологии RLS.
Проверка перед обновлением
Давайте немного изменим наш предыдущий пример. Вместо обновления столбца COMM пользователь теперь хочет изменить столбец SAL. SAL - это столбец, который используется в предикате, поэтому интересно будет посмотреть на результат.

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

Обратите внимание на последнюю операцию. Почему ни одна строка не была обновлена?
Все дело в первой операции обновления. Значения столбца SAL изменяются на 1600, и это новое значение не удовлетворяет условию предиката SAL <= 1500. То есть после первого обновления все строки становятся невидимыми для пользователя.
Так может возникнуть путаница: пользователь может выполнить для строк некоторый оператор SQL, в результате чего доступ к этим строкам будет изменен. При разработке приложения такая неустойчивость данных может вызвать ошибки или, по крайней мере, внести некий элемент непредсказуемости, усложняющий отладку. Чтобы решить проблему, используем еще один параметр процедуры ADD_POLICY, up- date_check. Давайте посмотрим, как установка этого параметра в значение TRUE повлияет на создание политики для таблицы.

Если пользователь попытается выполнить то же самое обновление после того, как для таблицы создана эта новая политика, будет выдана ошибка:

Генерируется ошибка ORA-28115, потому что политика теперь предотвращает любые изменения значений тех столбцов, которые могли бы привести к изменению видимости строк для заданного предиката. Пользователи могут выполнять изменения значений других столбцов, так как они не влияют на видимость строк:

Я бы рекомендовал устанавливать параметр update_check в значение TRUE при каждом объявлении политики, с тем чтобы избежать непредсказуемого и, возможно, нежелательного поведения приложения в дальнейшем.
Статические политики RLS
Во всех рассмотренных ранее примерах использовалась статическая политика (значение, возвращаемое функцией предиката, не изменяется при изменении условий вызова функции).
Постоянство возвращаемого значения означает, что нет необходимости в выполнении функции для каждого запроса к таблице. Значение можно вычислить всего один раз, кэшировать его и затем использовать повторно столько раз, сколько потребуется. Чтобы воспользоваться этой возможностью, следует определить политику для RLS как статическую, передав значение TRUE аргументу static_policy:

По умолчанию параметр static_policy установлен в FALSE: в этом случае политика воспринимается как динамическая и вызывается для каждой операции над таблицей. Динамические политики будут описаны далее в разделе «Определение динамической политики». В Oracle 10^ помимо статических и динамических поддерживаются и другие типы политик (подробнее поговорим об этом в разделе «Типы политик»).
Во многих ситуациях нужны именно статические политики. Возьмем, например, склад товаров, обслуживающий нескольких клиентов. В данном случае предикат может использоваться для предоставления доступа только к тем записям, которые относятся к данному клиенту. Например, в таблице BUILDINGS может быть столбец CUSTOMER_ID. К запросам будет присоединяться предикат CUSTOMER_ID = customer_id, где customer_id определяет пользователя, вошедшего в систему. При регистрации пользователя его клиентский идентификатор может быть извлечен специальным LOGON-триггером, а политика RLS может использовать этот идентификатор для определения того, какие строки следует показывать пользователю. Значение такого предиката не будет меняться на протяжении сеанса, поэтому имеет смысл установить параметр static_policy в значение TRUE.
Недостатки статических политик
Статические политики могут улучшить производительность, но могут и внести в приложение ошибки. Если предикат получается из или зависит от изменяющегося значения, такого как время, IP-адрес, идентификатор клиента или что-то еще, то разумнее задать динамическую политику. Покажем на примере, почему это именно так.
Давайте вернемся к нашей исходной функции политики, но предположим, что предикат зависит от изменяющегося значения, такого как секундная составляющая текущей временной метки (возможно, это не слишком реалистичный пример, но он удобен для пояснения).


В этом примере функция берет секундную составляющую текущего времени (строка 10), умножает ее на 100 и возвращает предикат, который отображает значение столбца SAL, не превышающее полученное число. Секундная составляющая со временем изменяется, поэтому последующие исполнения данной функции приведут к получению различных результатов.
Определим для таблицы EMP политику, используя созданную функцию в качестве функции политики. Т. к. политика уже существует, то начинаем с ее удаления.

Теперь проверим политику. Пусть пользователь Ленни попытается определить количество служащих в таблице.

Таблица находится под контролем RLS, поэтому вызывается функция политики, возвращающая предикат, который применяется к запросу. Предикат зависит от секундной составляющей текущей временной метки, так что его значение лежит в диапазоне от 0 до 60. В данном конкретном случае значение оказалось таким, что ни одна из строк не соответствовала условию предиката.
Функция политики обновляет столбец VAL таблицы TRIGGER_FIRE, поэтому я могу определить, сколько раз функция была вызвана. От имени пользователя HR выбираем значение VAL из таблицы TRIGGER_FIRE.

Функция политики была вызвана дважды: один раз на этапе синтаксического разбора, а второй - на этапе выполнения, поэтому значение увеличилось на 2 единицы (с начального значения 1). Ленни еще раз выполняет запрос, желая узнать количество служащих.

На этот раз функция политики возвращает предикат, которому удовлетворяют 10 записей таблицы. Снова проверяем значение VAL в таблице TRIGGER FIRE.

Значение увеличилось на 2 (с 3), это означает, что функция политики выполнялась несколько раз. Вы можете повторить проверку сколько угодно раз, чтобы убедиться в том, что функция политики выполняется каждый раз при выполнении операции над таблицей.
Теперь объявим политику как статическую и повторим тест. Операции изменения политики ни в RLS, ни в API не существует, поэтому удалим и пересоздадим ее.
Посмотрим, что изменится.

От имени пользователя Ленни выберем количество строк таблицы:
Теперь проверим значение столбца VAL таблицы TRIGGER_FIRE от имени HR

Значение увеличилось на единицу, потому что функция политики была выполнена единожды, а не дважды, как в прошлый раз. Пусть пользователь Ленни еще несколько раз повторит выборку из таблицы EMP.

Все время возвращается одно и то же число. Почему? Потому что функция политики была выполнена только один раз, затем предикат был кэширован. Больше функция политики ни разу не исполнялась, и предикат не менялся. Подтвердим наше предположение проверкой значения VAL в таблице TRIGGER FIRE от имени пользователя HR.

Имеем все то же значение 2; никаких изменений с момента первого вызова функции. Полученные выходные данные подтверждают, что функция политики не вызывалась при последующих выборках из таблицы EMP.
Объявив политику как статическую, мы фактически указали, что функция политики должна быть выполнена только один раз, затем политика должна повторно использовать изначально созданный предикат, даже если его значение могло измениться с течением времени. Такое поведение может привести к неожиданным последствиям для вашего приложения, поэтому следует использовать статические политики с большой осторожностью. Единственный случай, в котором вы, вероятно, захотите использовать именно статические политики, - когда функция возвращает определенный предикат, не зависящий от каких бы то ни было переменных, за исключением тех, которые были установлены с началом сеанса и больше не изменялись (например, имена пользователей).
Использование прагмы
Еще один способ обеспечить исполнение необходимой нам логики - использовать пакетную функцию с объявлением прагмы для подавления некоторого набора операций над базой данных. Рассмотрим спецификацию пакета.


В этой спецификации пакета определена прагма, вводящая следующие уровни строгости для данной функции:
WNDS
Write No Database State - не записывать состояние базы данных.
RNDS
Read No Database State - не читать состояние базы данных.
WNPS
Write No Package State - не записывать состояние пакета.
RNPS
Read No Package State - не читать состояние пакета.
При компиляции тела данного пакета прагма будет нарушена, и компиляция будет прервана с выдачей сообщения об ошибке.

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









jAntivirus