Контроль доступа на основе правил (исходники)

Барри Брахман

Введение

Вы, возможно, знакомы с функциями контроля доступа, предоставляемыми операционной системой. Они обычно выполняют авторизационные проверки для системных вызовов и операционных запросов к ресурсам, которым в файловой системе присвоено имя. Программистам обычно не требуется проверять, имеет ли пользователь, запустивший программу, право на чтение файла данных или установку системного времени. Об этом позаботится операционная система, и она же даст знать через возвращаемое значение, разрешена операция или запрещена. Например, UNIX выполняет требования, задаваемые ID владельца файла, ID группы и правами доступа, связанными с реальной или фактической идентификацией пользователя. Хотя такое построение и является простым, оно может быть недостаточно эффективным, заставляя некоторые UNIX-системы расширять его с помощью списков контроля доступа и других механизмов. Другие операционные системы используют более сложные модели безопасности. Всё же модель безопасности обычно применяется к файловым объектам и пользовательским учётным записям, известным системе (тем, которые появляются в /etc/passwd или в NIS).

Для некоторых, функционирующих на сервере, приложений эта модель эффективна. Сервер изначально выполняется с расширенными привилегиями, так что он может выполнять любые операции. Действуя от имени конкретного пользователя, он фактически становится этим пользователем. В противном случае сервер может использовать системные вызовы, чтобы построить модель безопасности, которая ограничивает операции до разрешённых данному пользователю. Перед выполнением этой операции программа должна проверить авторизацию: позволено ли действующему пользователю выполнять операцию?

Контроль авторизации для Web-сервисов

Программы должны иногда усиливать собственные требования по контролю доступа к тем ресурсам, за которые они отвечают, и к учётным записям пользователей, не распознаваемым операционной системой. Ярчайшим примером тому является Web-сервер Apache, который может быть настроен так, чтобы разрешать или отклонять HTTP-запросы к отдельным своим ресурсам. Записи в секретном файле паролей создают учётные записи Web-сервера, которые полностью отделены от записей, известных операционной системе. Аналогично, списки членства в группах создаются специально для использования Web-сервером. Администратор Apache может использовать эти имена с директивами Require, Allow и Deny сервера, чтобы описать, кто может (или не может) иметь доступ к ресурсу. Так как операционная система здесь мало чем может помочь, программисты Apache реализовали свою собственную подсистему авторизации.

Хотя список пользователей Web-сервера и операционной системы может быть единым, обычно их держат раздельно для повышения безопасности, производительности и для удобства реализации.

Разновидность авторизации, обеспечиваемая таким Web-сервером, как Apache, можно назвать грубым контролем доступа, так как она обеспечивает только внешний уровень безопасности. В результате проверки авторизации определяется, пропускать запрос или нет. Если доступ запрещён к Web-сервису, например, Web-сервис не выполняется Web-сервером, и, следовательно, запрос даже не видим для Web-сервиса.

Тем не менее, многим серверным приложениям, в том числе и Web-приложениям, требуются возможности по проверке авторизации, которые выходят далеко за рамки описанных возможностей. Рассмотрим приложение на базе Web-сервиса, доступное для выполнения любому, но "осознающее", что пользователи имеют различные возможности или привилегии. Возможно, самый простой пример - это приложение, в котором только определённые пользователи могут иметь доступ к административным функциям, - все остальные пользователи не только не могут выполнять эти функции, но даже не должны иметь возможности их видеть (их не должно быть в меню или, по крайней мере, они не должны быть доступны для выбора). Другой типичный пример - это приложение с профилями для каждого пользователя, которые может изменять только его владелец или, возможно, администратор приложения. Часто приложение должно ограничивать доступ к своим данным для определенных пользователей. Этот вид проверки авторизации может быть назван тонким контролем доступа, так как он применяется запущенной программой практически к любым видам ресурсов, с которыми программа работает.

В некоторых случаях программист с творческим подходом может применить некоторые уловки, чтобы обойти эти проблемы. Например, приложение может использовать URI для обозначения своих ресурсов, настроить контроли доступа этих URI к Web-серверу, и затем вызвать HTTP-запрос, чтобы определить, имеет ли пользователь авторизацию. Этот подход усилит механизмы проверки авторизации Web-сервера, но не будет практичным.

Большинство языков программирования, в том числе и Perl, в этом отношении не сильно помогают. Они предоставляют простой уровень, лежащий поверх того, что предоставляет система, лежащая в основе. Язык Java™ включает среду авторизации, но, конечно, это не поможет программисту Perl или C/C++.

Проблема, с которой сталкивается программист, двоякая: во-первых, пользователям этих приложений не нужно иметь учётную запись в системе, лежащей в основе, и, во-вторых, типы объектов и то, как осуществляется доступ к ним, могут отличаться от того, для чего была спроектирована модель безопасности операционной системы. Вследствие этого, программисты вынуждены писать много кода для поддержки специфичного в данном приложении тестирования авторизации, иногда заново реализуя, по существу, ту же среду на разных языках. Системы управления базами данных, такие, как Oracle и MySQL, управляют своими собственными пользовательскими учётными записями, ролями и привилегиями для операций на уровне системы и на уровне объектов.

Тонкий контроль авторизации

Тонкий контроль доступа выходит за рамки разрешения или отказа в праве на выполнение программы или на чтение файла данных. Рассмотрим Wiki-сервер, реализованный в виде CGI-программы. Он содержит набор Web-страниц, по одной на пользователя. У любого пользователя есть доступ на чтение любой Web-страницы. Любой пользователь может добавлять содержимое или управлять своей собственной страницей, но не чужими страницами. Любой пользователь, назначенный администратором, может обновлять и управлять любой страницей (например, удалять оскорбительный или запрещённый контент). Во время показа Web-страницы владельцу или администратору все операции должны быть доступны через меню или ссылки; другие же не должны видеть этих операций или не иметь возможности их выбрать. Более того, приложение должно гарантировать, что любой пользователь может видеть Web-страницу, но только её владелец или администратор могут выполнять операцию ограниченного доступа, такую, как обновление контента страницы.

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

При создании меню может появиться код, схожий с Листингом 1.

Листинг 1. Построение меню допустимых операций (неверный способ)

                
for (op = 0; op < n_operations; op++) {
  if (op == CREATE_OP && is_admin(current_user))
      add_operation_to_menu(menu, op);
  else if ((op == UPDATE_OP // op == DELETE_OP)
    && (is_owner(current_page, current_user) // is_admin(current_user)))
      add_operation_to_menu(menu, op);
  else
      add_operation_to_menu(menu, op);
}

Хотя, на первый взгляд, Листинг 1 может показаться верным, он ошибочен. Тот код заставил пройти несколько версий этого документа, прежде чем проблема была обнаружена, показывая, насколько легко совершить ошибки по невнимательности во время написания такого вида проверки. Листинг 2 является правильным:

Листинг 2. Построение меню допустимых операций (верный способ)

                
for (op = 0; op < n_operations; op++) {
  if (op == CREATE_OP && is_admin(current_user))
      add_operation_to_menu(menu, op);
  else if ((op == UPDATE_OP // op == DELETE_OP)
    && (is_owner(current_page, current_user) // is_admin(current_user)))
      add_operation_to_menu(menu, op);
  else if (op != CREATE_OP && op != UPDATE_OP && op != DELETE_OP)
      add_operation_to_menu(menu, op);
}

В другой точке кода, возможно там, где распределяются операции, или в начале каждой операции выполняется аналогичная проверка.

Листинг 3. Проверка для операций с ограниченным доступом

                
if (op == CREATE_OP && !is_admin(current_user))
  raise_exception("Operation not permitted")
else if ((op == UPDATE_OP // op == DELETE_OP)
  && !(is_owner(current_page, current_user) // is_admin(current_user)))
    raise_exception("Operation not permitted")

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

Проверка авторизации на основе правил

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

Мы предлагаем среду авторизации на основе правил (управляемую данными), со следующими возможностями:

  • Она вызывается из командной строки, поэтому она может быть вызвана практически любым скриптом или выполнена любой программой
  • Она представляет простой API-интерфейс на языке С, поэтому она может быть вызвана непосредственно и интегрирована как расширение во многих языках скриптов
  • Она использует типовой синтаксис именования пользователей, поэтому она может взаимодействовать с большинством методов аутентификации
  • Она поддерживает правила, которые могут быть применены к любому ресурсу
  • Она представляет правила контроля доступа как простые XML-документы
  • Она предоставляет переменные, выражения и управляющий поток, чтобы обеспечить принятие более сложных решений
  • Она включает богатый набор функций для тестирования требований контроля доступа, такие как IP-адрес пользователя, время и дата, или встречается ли имя пользователя в данном списке

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

  • Правила, используемые приложением, могут быть изменены кем угодно, имеющим соответствующие права, без изменения или даже без перекомпиляции приложения. Администрирование политик безопасности может быть передано непрограммисту или кому-то, незнакомому с языком реализации приложения.
  • Если правило изменяется, то все случаи употребления по всему приложению или комплексу связанных приложений автоматически используют обновленное правило - нет необходимости вносить изменения в код, и все случаи употребления синхронизированы.
  • Так как правила не зависят от какого-либо языка программирования, одни и те же правила могут использоваться разными приложениями.
  • Усовершенствования и исправления ошибок, сделанные в среде, могут принести пользу приложениям, которые её используют - без изменений или перекомпиляции.

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

DACScheck: среда авторизации

Было бы правильно, если бы перечисленные наблюдения инициировали разработку и внедрение dacscheck, но реальная история её происхождения не такова. Скорее, многофункциональная замена для модуля авторизации в Apache привела к разработке DACS, облегчённой системы с однократным вводом пароля. Понимание того, что среда авторизации может быть обобщена для использования почти любым приложением, как на основе Web, так и нет, пришло намного позднее.

Чтобы продемонстрировать, как использовать dacscheck, давайте снова обратимся к псевдокоду, который выполняет построение меню, представленное в Листинге 2, на этот раз на языке Perl.

Листинг 4. Использование DACScheck на Perl

                 use DACScheck.pm;

# Сообщаем dacscheck, какие правила следует использовать
dacscheck_rules("/usr/local/wiki_app/acls");

# Строим список операций для меню.
for ($op = 0; $op < $n_operations; $op++) {
  # Предметом интереса, используемым для отыскания подходящего правила,
  # является произвольно именуемое "/<username>/menu".
  my $object = "/$ENV{'DOCUMENT_ROOT'}/menu";
  my $result = dacscheck_cgi($object);

  # Доступ к объекту предоставляется, только если возвращено значение 1.
  if ($result == 1) {
      add_operation_to_menu($menu, $op);
  }
}

Модуль на Perl с названием DACScheck.pm обеспечивает упрощённый интерфейс для dacscheck, который включает в себя dacscheck_cgi(). Помимо присвоения имени объекту функция указывает dacscheck, что она выполняется в рамках CGI-контекста и должна использовать значение REMOTE_USER в качестве обозначения для пользователя, делающего запрос. Apache автоматически присваивает REMOTE_USER , если пользователь прошёл аутентификацию.

Каждый ресурс, управляемый dacscheck, имеет соответствующее правило. Хотя правила могут храниться и быть доступны разными способами, каждое правило обычно хранится в обычном текстовом файле, а правила для взаимосвязанных ресурсов обычно хранятся в каталогах с общим корнем. Правило выражается XML-документом, который может включать С-подобные выражения свободного формата. Выражения могут ссылаться на переменные, для которых экземпляр создаётся из контекста выполнения, включая переменные окружения и аргументы Web-сервиса, а также широкий спектр встроенных функций, ориентированных на обработку строк и тесты на идентификацию высокого уровня. От существующего языка расширений, такого как Tool Command Language (Tcl), было решено отказаться, чтобы сохранить эту способность без усложнений. Более сложный тест может быть выполнен с помощью отдельной программы, даже будучи вызванным посредством HTTP. Правило также может указывать на другое правило, давая администратору возможность делегировать ответственность за правила.

Давайте теперь взглянем на то, как может выглядеть правило. Правило сообщает dacscheck как определить, следует ли разрешить или отказать запросу. Мы предполагаем, что пользователь уже вошёл в систему и, следовательно, REMOTE_USER присвоено значение. Мы также предполагаем, что OP - это аргумент, который выбирает операцию, а PAGE - это аргумент, который идентифицирует владельца страницы Wiki.

Листинг 5. Пример файла правил dacscheck

                
<acl_rule>
  <services>
    <service url_expr="/${Args::PAGE}/menu"/>
  </services>

  <rule order="allow,deny">
  <precondition>
    <predicate>
     ${Args::OP} eq "CREATE_OP"
    </predicate>
  </precondition>

  <allow>
     dacs_admin()
  </allow>
  </rule>

  <rule order="allow,deny">
  <precondition>
    <predicate>
     ${Args::OP} eq "UPDATE_OP" or ${Args::OP} eq "DELETE_OP"
    </predicate>
  </precondition>

  <allow>
     dacs_admin() or ${Args::PAGE} eq ${Env::REMOTE_USER}
  </allow>
  </rule>

  <rule order="allow,deny">
  <allow>
     user("any")
  </allow>
  </rule>

 </acl_rule>

Это правило описано столь подробно, чтобы его было легче объяснять, а также потому, что если вам знаком синтаксис, то вам будет легко понимать и изменять политику безопасности.

Элемент service указывает dacscheck, к какому ресурсу применимо правило. Этой строке сопоставляется аргумент из dacscheck_cgi().

Это правило контроля доступа состоит из трёх элементов rule: первый выбирается, только если аргумент OP равен CREATE_OP; второй выбирается, только если аргумент OP равен UPDATE_OP или DELETE_OP; а третий выбирается, только если остальные два не выбраны. Атрибут order служит тем же целям, что и директива Order в Apache: он устанавливает режим работы по умолчанию и порядок, в котором обрабатываются элементы allow и deny.

В этом примере, первое правило разрешает доступ, только если функция dacs_admin() возвращает True. Эта функция ищет идентификацию пользователя. Второе правило разрешает доступ для администратора и владельца страницы Wiki. Третье правило разрешает доступ любому, так как была запрошена операция без ограничения доступа.

Если политику контроля доступа для Wiki нужно будет изменить, вам нужно будет только изменить правило. Результат достигается немедленно и применяется ко всем ссылкам на правило. Если администратору нужно запретить доступ к странице для запросов, исходящих из определенного диапазона IP-адресов, следует добавить приблизительно такой код:

Листинг 6. Дополнительное правило, для запрещения доступа

                
<rule order="allow,deny">
  <precondition>
    <predicate>
      from("10.0.0/24")
    </predicate>
  </precondition>
</rule>

Упорядочение allow,deny по умолчанию запрещает доступ, поэтому любой запрос, исходящий с IP-адреса в заданном диапазоне, будет запрещён.

Существует множество ситуаций, когда может применяться такая форма проверки авторизации. Например, ftp-демон (ftpd) может с успехом воспользоваться этой возможностью. Он может быть доработан, чтобы применять правила для разрешения или запрета операций (put, get, cd, и т.д.), на основании идентификации пользователя или контекстно зависимой информации, которую можно проверить с помощью правил. Например, закачка данных может быть разрешена только в определённые часы или доступ к каталогу может быть ограничен только для определённых пользователей либо пользователей, соединяющихся с определённых IP-адресов.

Выводы

Гибкая среда авторизации может повысить безопасность и облегчить труд программистов. Скрипты и скомпилированные программы могут быть меньше по размеру и проще, а время и усилия на разработку приложения могут быть сокращены. И пусть остаются отдельные проблемы, dacscheck уже доказала, что является полезным инструментом. Не смотря на то, что её эффективности уделялось мало внимания, при обычном использовании dacscheck существенно не повлияла на производительность.

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

Еще одна родственная программа - dacstransform, это утилита для преобразования документа на основе правил. Используя те же самые правила, что и dacscheck, она может редактировать, вставлять и заменять текст в документе с разметкой. Каждое преобразование зависит от правила, которое вычисляется в момент выполнения, позволяя получить из размеченного новый документ, который предназначен для определённого пользователя или контекста.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=7978