программирование, создание программ, учебник Delphi, учебник по программированию, бейсек, делфи, си, паскаль
 
 
 

 

Защита

Защита, или безопасность — это одно из основных требований к приложениям и при разработке ее следует учитывать не в последнюю очередь Однако из педагогических соображений говорить о защите легче тогда, когда уже состоялось знакомство с прикладной моделью NET, а также с ASP NET и Web-службами Эта глава должна познакомить вас с основными концепциями защиты NET.
Защита не дает пользователю или коду делать то, что им не позволено Традиционно основная задача защиты состояла в том, чтобы ограничивать действия пользователей Платформа NET дает возможность накладывать ограничения и на выполнение кода Например, вы можете запрещать некоторым секциям кода получать доступ к некоторым файлам Это особенно полезно, когда у вас есть общедоступные Web-узлы и Web-службы, где нереально иметь учетные записи, файлы для различных блокировок или другие ресурсы, предназначенные для неизвестного числа пользователей Все это критично тогда, когда приходится выполнять код, созданный независимыми производителями
Важно понимать, что защита платформы NET (или, проще говоря, защита NET) строится поверх системы защиты операционной системы компьютера В данной главе предполагается, что такой операционной системой является Windows 2000 И когда мы будем обсуждать некоторые вопросы защиты, связанные с инфраструктурой операционной системы, в том числе с информационным сервером Internet (Internet Information Server, US), разработанным фирмой Microsoft, то в подробности будем углубляться только в тех местах, которые имеют отношение к NET
Вот пример взаимодействия защиты NET и операционной системы код всегда запускается под какими-нибудь "персональными настройками", или, иными словами, под каким-нибудь идентификатором пользователя Или вот еще пример если список доступа (Access Control List, ACL) не дает вам права на создание файла, то вы не сможете его создать, несмотря на все разрешения, предоставленные вам защитой .NET.
Взаимоотношения с системой защиты начинаются с того, что приходится отвечать на два вопроса. Первый из них относится к опознаванию: "Кто вы?" Второй же вопрос о разрешении: "Имеете ли вы право делать то, что пытаетесь сделать?" В .NET эти вопросы могут относиться к двум видам, поскольку "вы" можете быть пользователем или сборкой.
Начнем мы с того, что вкратце расскажем о защите, показывая, каким образом эти два ее вида сосуществуют в системе .NET. Затем, хоть это так сразу и не нужно, последует краткий экскурс в Internet-защиту, чтобы мы могли, когда потребуется, использовать эту информацию. Ну, а потом мы начнем подробно рассказывать, как в системе .NET применяется защита, работающая на основе ролей.

Защита на основе пользователей

С позиции традиционной защиты, работающей на основе пользователей, вопрос опознавания состоит в следующем: "Что за личность пытается выполнить определенное действие?" Под личностью обычно понимается имя или учетная запись пользователя. Мандат — это то, что вы предъявляете, чтобы показать, кто вы такой. Он является свидетельством, представленным для проверки. Мандатом пользователя может быть пароль, смарт-карта (интеллектуальная карта) или какое-нибудь биометрическое устройство. Такой мандат должен проверяться некоторым средством защиты. Примером может служить проверка пароля пользователя на соответствие регистрационному имени, которая выполняется на основе информации базы данных с пользовательскими именами и зашифрованными паролями. Если система разрешает доступ без проверки, то говорят, что такая система разрешает анонимный доступ. На жаргоне специалистов по системам защиты личность, которая может быть опознана, называется принципалом (principal).
Что касается вопроса о разрешении, то он состоит в следующем: "Может ли личность выполнять нужные ей действия?" Данные принципала затем сравниваются с некоторым списком прав, чтобы определить, разрешен принципалу доступ или нет. Например, чтобы получить доступ к файлу, имя пользователя проверяется в списке доступа (ACL), составленном для той операции, которую нужно выполнить. Эта проверка делается для того, чтобы определить, можете ли вы иметь доступ к файлу. Конечно, доступ — это не всегда все или ничего. Например, у вас может быть право читать файл, но при этом могут отсутствовать права делать в нем изменения.
В многоуровневой архитектуре личность сервера часто обладает очень большой властью, и потому необходимо ограничить подмножество полномочий сервера, которое может запросить клиент. В других случаях, например, анонимного доступа, сервер может не знать, кем в действительности является его клиент. В таких случаях сервер исполняет роль клиента. При выполнении код сервера временно использует личность клиента, а не сервера. В случае анонимного доступа сервер использует личность специально подготовленной учетной записи пользователя. Например, встроенная учетная запись для анонимного доступа на информационный сервер Internet (Internet Information Server, H.S) называется Internet Guest Account (учетная запись гостя Internet).
В основу системы защиты Windows, на которой, в свою очередь, построена защита .NET и ASP.NET, положены принципы защиты на основе пользователей.

Защита доступа к коду

Одна из трудностей мира программ, таких, как компоненты от сторонних производителей и загружаемый по сети код, состоит в следующем: вы открываете свою систему для угрозы и риска, идущих от выполняемого кода неизвестного происхождения. Возможно, вы захотите, чтобы доступ к макросам такого приложения как Word был ограничен только документом, в котором они находятся. И безусловно, следует пресечь выполнение потенциально злонамеренных Web-сценариев. Кроме того, необходимо оградить свою систему от ошибок в программах, полученных от известных поставщиков. Для этого предназначена защита доступа к коду CAS (Code Access Security) — составная часть системы защиты .NET.

Политика безопасности

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

Разрешения

Политика безопасности определяется с помощью разрешений. Разрешения — это объекты, используемые для описания прав и полномочий сборок на доступ к другим объектам или на выполнение некоторых действий. Сборки могут запрашивать определенные разрешения. Именно политикой безопасности определяется, какие именно разрешения будут предоставлены сборке.
Вот, например, некоторые из тех классов, которые предоставляют разрешения:
• SecurityPermission управляет доступом к системе безопасности. В понятие управления доступом входит право вызывать неуправляемый код, управлять потоками, принципалами, прикладными областями, подтверждениями и т п.,
• FilelOPermission управляет доступом к файловой системе;
• ReflectionPermission управляет доступом к метаданным, не являющимся общедоступными, а также к динамической генерации модулей, типов и членов.
Все классы разрешений являются производными от базового класса CodeAccess-Permission, поэтому они ведут себя одинаково.
Запрос на некоторые разрешения можно формировать с помощью значений параметров сборки. Общеязыковая среда выполнения CLR использует метаданные для определения того, какие именно разрешения запрашиваются. Затем, используя личность кода и уровень доверия, общеязыковая среда выполнения CLR на основе политики безопасности решит, можно ли предоставить эти разрешения.
Код перед выполнением определенных ветвей может с помощью программных средств требовать (то есть запрашивать), чтобы вызывающий его код имел те или иные разрешения. Если требование не выполняется, то общеязыковая среда выполнения CLR запускает исключение System: : Security: : SecurityException. Требуя любое разрешение, надо быть готовым перехватить это исключение и работать в ситуации, когда разрешение не предоставлено. Большинству программистов не нужно требовать разрешений, потому что библиотеки каркаса .NET делают это от вашего имени за вас. Впрочем, вы все равно должлы быть готовы обеспечить обработку исключений.
Код может делать за прос также на то, чтобы предоставленные ему разрешения были ограничены или отменены. Это важно для кода, который использует компоненты или Web-сценарии от сторонних производителей. Поскольку у такого кода может быть меньший уровень доверия, чем у полностью написанного вами, то, пока он выполняется, возможно, придется ограничить имеющиеся у него права. После завершения выполнения кода можно восстановить разрешения на прежнем уровне.
Определение личности кода равнозначно вопросу опознавания в традиционной системы безопасности. А. вот вопрос о разрешениях решается на основе разрешений, предоставленных сборке ил и отнятых у нее.
Многие из классов, поддерживающих разрешения, находятся в пространстве имен System: :Secunty: : ^Permissions (Система::Безопасность::Разрешения). Кроме того, разрешения поддерживаются и некоторыми классами из пространств имен System: :Net (Система::Сеть) и Sys tem:: Data (Система-Данные).

lnternet-безопасность
Для ограничения доступа на ваш компьютер с некоторых ГР-адресов можно использовать систему защиты протокола Internet DPSec (Internet Protocol Security). Это делается с помощью дополнительного модуля IP Security Policy Management (управление политикой защиты протокола Internet), присоединенного к консоли ММС. Конечно, еще должны быть известны IP-адреса клиентов. Преимущество здесь в том, что не нужно трогать клиентские приложения, код ASP.NET и код Web-служб. Впрочем, все это не подходит для общедоступных Web- уз_пов или Web-служб, где не известно, кто является вашим потенциальным клиентом.

Информационный сервер Internet: Internet Information Server (MS)


Хотя в этой главе основное внимание уделено безопасности .NET, но важно кое-что знать и о защите информационного сервера Internet (Internet Information Server, П5). Поскольку информационный сервер Internet (US) используется как Web-службами, так и ASP.NET, то значени-Я параметров информационного сервера Internet (US) могут влиять на безопасность .NET.
В предыдущих главах, где рассказывалось об ASP.NET и Web-службах, для анонимного доступа применялись значения по умолчанию. При анонимном доступе не нужно указывать имя пользователя и пароль, чтобы получить доступ к учетной записи. Такой доступ позволяет работать под учетной записью пользователя, определенной по умолчанию. Анонимный доступ полезен для общедоступных Web-узлов и Web-служб, в которых имеется своя система опознавания путем запроса имени пользователя и пароля или некоторыми другими средствами. В этом случае можно выполнять опознавание с помощью форм ASP.NET. Создайте формы, предназначенные для получения имени пользователя и пароля, а затем сверяйте полученные значения с теми, что находятся в файле конфигурации или в базе данных.
Информационный сервер Internet (US) поддерживает основные схемы опознавания, предусмотренные в протоколе передачи гипертекстовых файлов (HTTP). Впрочем, для этого он должен быть соответствующим образом настроен. Схемы опознавания перечислены в табл. 13.1. В каждом из этих сценариев информационный сервер Internet (US) проверяет, соответствуют ли имеющейся учетной записи те данные, которые представил пользователь. Каждый раз, когда нужно зашифровать канал HTTP-связи, применяется протокол защищенных сокетов (Secure Sockets Layer, SSL). Использование протокола защищенных сокетов SSL приводит к ухудшению производительности. О протоколе защищенных сокетов SSL в данной главе не рассказывается.
Кроме того, вам еще придется делать авторизацию, то есть настраивать доступ этих учетных записей пользователей к необходимым файлам (графике, файлам с хранящимися данными и т.д.) и другим ресурсам (например, базам данных). Что же касается общедоступных Web-узлов и Web-служб, то здесь такой подход бесполезен, потому что у пользователей не будет учетных записей.
Компания Microsoft предложила схему опознавания, которая называется Passport (Паспорт). И хотя в серверной части системы ASP.NET эта схема поддерживается с помощью класса System::Web::Security::Passportldentity,но в момент написания этих слов еще не было инструментов разработки, способных работать с клиентской частью схемы Passport (Паспорт). Эта схема позволяет избежать трудностей, связанных с указанием конкретных учетных записей или конкретных машин. О схеме Passport (Паспорт) в данной главе не рассказывается.

Схемы опознавания

Информационный сервер Internet: Internet Information Server (MS)
Хотя в этой главе основное внимание уделено безопасности .NET, но важно кое-что знать и о защите информационного сервера Internet (Internet Information Server, П5). Поскольку информационный сервер Internet (US) используется как Web-службами, так и ASP.NET, то значени-Я параметров информационного сервера Internet (US) могут влиять на безопасность .NET.
В предыдущих главах, где рассказывалось об ASP.NET и Web-службах, для анонимного доступа применялись значения по умолчанию. При анонимном доступе не нужно указывать имя пользователя и пароль, чтобы получить доступ к учетной записи. Такой доступ позволяет работать под учетной записью пользователя, определенной по умолчанию. Анонимный доступ полезен для общедоступных Web-узлов и Web-служб, в которых имеется своя система опознавания путем запроса имени пользователя и пароля или некоторыми другими средствами. В этом случае можно выполнять опознавание с помощью форм ASP.NET. Создайте формы, предназначенные для получения имени пользователя и пароля, а затем сверяйте полученные значения с теми, что находятся в файле конфигурации или в базе данных.
Информационный сервер Internet (US) поддерживает основные схемы опознавания, предусмотренные в протоколе передачи гипертекстовых файлов (HTTP). Впрочем, для этого он должен быть соответствующим образом настроен. Схемы опознавания перечислены в табл. 13.1. В каждом из этих сценариев информационный сервер Internet (US) проверяет, соответствуют ли имеющейся учетной записи те данные, которые представил пользователь. Каждый раз, когда нужно зашифровать канал HTTP-связи, применяется протокол защищенных сокетов (Secure Sockets Layer, SSL). Использование протокола защищенных сокетов SSL приводит к ухудшению производительности. О протоколе защищенных сокетов SSL в данной главе не рассказывается.
Кроме того, вам еще придется делать авторизацию, то есть настраивать доступ этих учетных записей пользователей к необходимым файлам (графике, файлам с хранящимися данными и т.д.) и другим ресурсам (например, базам данных). Что же касается общедоступных Web-узлов и Web-служб, то здесь такой подход бесполезен, потому что у пользователей не будет учетных записей.
Компания Microsoft предложила схему опознавания, которая называется Passport (Паспорт). И хотя в серверной части системы ASP.NET эта схема поддерживается с помощью класса System::Web::Security::Passportldentity,но в момент написания этих слов еще не было инструментов разработки, способных работать с клиентской частью схемы Passport (Паспорт). Эта схема позволяет избежать трудностей, связанных с указанием конкретных учетных записей или конкретных машин. О схеме Passport (Паспорт) в данной главе не рассказывается.
Таблица 13.1. Схемы опознавания
Схема Вид опознавания
Базовая (basic) В действительности пользовательская информация и пароль передаются как простой текст. Это стандартная схема опознавания, предусмотренная в протоколе передачи гипертекстовых файлов (HTTP); безопасной она не является
Базовая поверх протокола защищенных сокетов SSL (basic over SSL) Хотя опознавание является базовым, но канал связи закодирован, так что имя пользователя и пароль защищены
Профильная (Смешанная) (digest) Использует безопасное хэширование для передачи имени пользователя и пароля. Это не совсем безопасный метод, потому что хэш-коды хранятся на сервере и являются обратимыми3. Была введена в протоколе передачи гипертекстовых файлов HTTP 1.1 в качестве базового опознавания
Интегрированная защита Windows (Windows Integrated Security) В традиционной системе безопасности Windows используются протоколы NTLM и Kerberos (Цербер). Информационный сервер Internet (US) выполняет опознавание, если мандат совпадает с учетной записью пользователя. Нельзя использовать в модулях доступа (proxies) и брандмауэрах. NTLM — это унаследованный Windows протокол безопасности
Сертификаты поверх протокола защищенных сокетов SSL {certificates over SSL) Клиент получает сертификат, который соответствует учетной записи пользователя
Безопасная спецификация для SOAP разрабатывается W3C. С помощью сообщений SOAP вы можете создавать свою собственную систему опознавания. Так как XML-код передается в виде текста, то необходимо использовать протокол защищенных сокетов SSL для шифрования сообщений (особенно при использовании таких, например, дескрипторов, как <user> (пользователь) и <password> (пароль)). В общем, при применении SOAP данные, используемые системой защиты, надо шифровать.

Защита .NET на основе ролей

Большинство людей имеют хотя бы интуитивное представление о пользователях и паролях. В то же время сервер транзакций корпорации Microsoft (MTS) и СОМ+ поддерживают хорошо известную и легкую для понимания систему зашиты на основе ролей. Более детальное изучение защиты .NET лучше всего начинать с личностей и ролей. Сначала мы изучим этот материал, имея в виду Windows-приложения, а затем — применительно к ASP.NET.

Принципалы и личности

Каждый поток связан с принципалом общеязыковой среды выполнения CLR. Принципал содержит личность, которая представляет идентификатор пользователя, запускающего поток. Текущий принципал, связанный с вызываемым потоком, является значением статического свойства Thread: : CurrentPrincipal.
Объекты принципала реализуют интерфейс IPrincipal. Этот интерфейс обладает одним методом и одним свойством. Свойство Identity (Личность) возвращает текущий объект личности, а метод IsInRole используется, чтобы определять, находится ли данный пользователь в указанной роли. Использование принципалов, личностей и ролей показано в примере, который называется RoleBasedSecurity.
Сейчас в системе .NET имеется два класса принципалов: WindowsPrincipal и GenericPrincipal. GenericPrincipal полезен тогда, когда должен быть реализован ваш собственный принципал. Ну а класс WindowsPrincipal представляет пользователя Windows и связанные с ним роли.
В примере RoleBasedSecurity программа начинается с требования предоставить SecurityPermission для манипуляций с объектом принципала. Затем программа задает политику принципала AppDomain. SecurityPermission — класс разрешения, выдаваемого системой защиты. Этот класс устанавливает для разрешения определенный набор флажков. В текущем примере конструктор SecurityPermission принимает в качестве параметра Security PermissionFlag: :ControlPrincipal, указывающий на возможность управлять объектом принципала. Метод Demand (Требование) класса SecurityPermission вызывается для того, чтобы узнать, есть ли у нас разрешение, позволяющее манипулировать названным объектом принципала. Если есть, то тогда мы продолжаем менять политику этого принципала. Ну а если такого разрешения у нас нет, то Demand (Требование) запустит исключение SecurityException, и выполнение программы прекратится.
// определить право изменять политику принципала Console::WriteLine(
"Demanding right to change principal policy");
// "Требование права изменять политику принципала" SecurityPermission *sp = new SecurityPermission(
SecurityPermissionFlag::ControlPrincipal) ; try
{
// Проверить, всем ли вызывающим программам
// выше в стеке вызовов
// предоставлено разрешение управлять
// объектом принципала перед работой с ним.
sp->Demand(); // Запрос
}
catch(SecurityException *se)
{
//не может управлять объектом принципала
Console::WriteLine(se->Message); // Сообщение
return;
}
Затем мы меняем политику принципала. Вместо заданной по умолчанию UnauthenticatedPrincipal задаем политику WindowsPrincipal. Это значит, что объекты принципала и личности будут создаваться на основе предоставленного пользователем операционной системы опознавательного знака текущего потока. Кроме того, это означает, что роли будут задаваться на основе групп пользователей операционной системы.
AppDomain *ap = AppDomain::CurrentDomain; ap->SetPrincipalPolicy!
PrincipalPolicy::WindowsPrincipal); Console::WriteLine(
"AppDomain Principal Policy changed to
WindowsPrincipal");
// "Политика принципала AppDomain изменена на
// WindowsPrincipal");
Далее мы получаем принципал текущего потока и проверяем, является ли он WindowsPrincipal. Так как пример RoleBasedSecurity является (консольным) Windows-приложением, то свойству CurrentPrincipal должен соответствовать класс
WindowsPrincipal.
IPrincipal *ip;
ip = Thread::CurrentPrincipal; // Поток
WindowsPrincipal *wp =
dynamic_cast<WindowsPrincipal *>(ip); if (wp == 07
Console::WriteLine(
"Thread::CurrentPrincipal is NOT a WindowsPrincipal"); // "Поток:: CurrentPrincipal - HE WindowsPrincipal"); else
Console::WriteLine(
"Thread::CurrentPrincipal is a WindowsPrincipal"); // "Поток:: CurrentPrincipal - WindowsPrincipal");
Объект личности выполняет реализацию интерфейса I Identity. У этого интерфейса имеется три свойства:
• Name (имя) — строка, относящаяся к личности. Общеязыковая среда выполнения CLR получает его от операционной системы или от средства доступа, используемого для опознавания. Таким средством доступа может быть, например, ASP.NET;
• IsAuthenticated — логическое значение, указывающее, был ли пользователь опознан;
• AuthenticationType — строка, которая указывает, какое опознавание использовалось операционной системой или средством доступа. Типами опознавания являются, например, Basic (Основной), NTLM, Kerberos (Цербер), Forms (Формы) или Passport (Паспорт).
Есть несколько типов объектов личности. Поскольку это Windows-программа, то имеется объект Windowsldentity, связанный с WindowsPrincipal. Вот пример, при выполнении которого распечатывается информация о свойствах объекта личности. На печать выводятся имя пользователя текущего потока и тип используемого опознавания.
Ildentity *ii = ip->Identity; Console::WriteLine(
"Thread::CurrentPrincipal Name: {0} Type: {1}
I/ Поток:: Имя {0} Тип: {1}
IsAuthenticated: {2}", ii->Name, // Имя ii->AuthenticationType, _box(ii->IsAuthenticated));
Вот что распечатывается на моей машине:
Thread::CurrentPrincipal Name: HPDESKTOPXAdministrator
Type: NTLM IsAuthenticated: True
Перевод такой:
Имя Поток::CurrentPrincipal: HPDESKTOPXAdministrator
Тип: NTLM IsAuthenticated: Истина
Подстановка имени вашей машины в примерах
В нескольких примерах используется такое имя машины, как HPDESKTOP. Запуская примеры на своем компьютере, вы можете подставлять имя соответствующей машины или соответствующего домена.
Работающая на машине операционная система использовала для опознавания протокол NTLM. Пользователь, запустивший программу, был опознан как "Administrator" ("Администратор"). Затем программа проверяет, действительно ли он является объектом Windows Identity. В этом объекте есть и другие свойства и методы, а не только те, что находятся в интерфейсе I Identity. Одним из членов Windows Identity является идентификатор лексемы учетной записи для Win32. Это идентификатор пользователя выполняемого потока.
// получить информацию от личности из WindowsPrincipal Windowsldentity *wi =
dynamic_cast<Windows!dentity *>(wp->Identity); if (wi != OY
Console::WriteLine(
"WindowsPrincipal Identity Name: {0} Type: {1}
Authenticated: {2} Token: {3}",
// Опознан: {2} Лексема: {3} ",
wi->Name, // Имя
wi->AuthenticationType,
_box(wi->IsAuthenticated),
_box(wi->Token)); // Лексема
Зная имя пользователя, можно решать программным путем, есть ли у пользователя право производить некоторые операции, не выполняя при этом те или иные ветви кода. (Иначе говоря, зная имя пользователя, можно определить полномочия.) Это показано в примере RoleBasedSecurity с помощью оператора условного перехода if-else.
// имя пользователя используется для разрешения
// выполнения ветви кода
String * name = wp->Identity->Name; // Строка - Имя
if (name->Equals("HPDESKTOP\\Administrator"))
// если (имя-> Равняется ("HPDESKTOP \\ Администратор"))
{
Console::WriteLine(
"Name matches HPDESKTOP\\Administrator");
// "Имя равно HPDESKTOP \\ Администратор"
// разрешить выполнение ветви кода, которая идет сюда...
}
else
{
Console::WriteLine(
"Name does not match HPDESKTOP\\Administrator") ;
// "Имя не равно HPDESKTOP \\ Администратор"
// не разрешить выполнение ветви кода, которая идет сюда...
}
Далее в примере RoleBasedSecurity показано, как можно выполнять некоторые действия в зависимости от членства в роли, которая определена пользователем или является встроенной, такой, например, как Administrator (Администратор), Guest (Гость) и User (Пользователь).

Роли .NET в Windows

Чтобы не проверять каждое имя пользователя, вы можете назначить пользователям те или иные роли Затем можно проверять, принадлежит ли пользователь к определенной роли или нет Примером применения ролей является стандартная группа администраторов Вам не нужно присваивать личности пользователя все полномочия администратора по отдельности, а затем проверять, есть ли у конкретных пользователей те или иные полномочия. Вы просто зачисляете пользователя в группу администраторов И тогда ваш код может проверять, находится ли пользователь в группе администраторов, прежде чем дать ему возможность, например, создать нового пользователя. Надо сказать, что роли .NET отличаются от ролей СОМ+.
Роли в NT4 и Wmdows2000 задаются путем определения групп. Каждая группа представляет какую-либо роль. Перейдите на Control Panel (Панель управления) и выберите команду Administrative Tools (Инструменты администрирования). В списке Administrative Tools (Инструменты администрирования) выберите пункт Computer Management (Управление компьютером). Появится окно дополнительного модуля Computer Management (Управление компьютером), присоединенного к консоли ММС. В этом окне разверните узел Local Users and Groups (Локальные пользователи и группы). Как показано на рис. 13.1, при выборе узла Groups (Группы) вы увидите все группы, определенные на вашей машине. К ним была добавлена группа CustomerAdmm.
Некоторые группы, такие, например, как Administrators (Администраторы) и Guests (Гости), являются встроенными, потому что были для вас заранее определены. Что же касается группы CustomerAdmm, то она определена пользователем. В эту группу входят все администраторы, которые имеют право изменять информацию пользователя Acme
Чтобы добавить на локальную машину новую группу, щелкните правой кнопкой мыши на узле Groups (Группы), а затем выберите команду New Group (Добавление группы). Появится диалоговое окно, которое вам предстоит заполнить.
Это диалоговое окно, уже заполненное данными для создания новой группы Hote-lAdmin, показано на рис. 13.2. В группу HotelAdmin должны входить все пользователи машины, которые могут добавлять или изменять информацию о гостиницах в системе HotelBroker. Чтобы добавить группу в систему, надо щелкнуть на кнопке Create (Создать). Добавление пользователей в систему или их удаление из нее выполняется, соответственно, с помощью кнопок Add (Добавить) и Remove (Удалить).
Чтобы внести изменения в существующей группе, выделите ее, щелкните на ней правой кнопкой мыши и выберите команду Properties (Свойства). Щелкнув на кнопке Add (Добавить), вы получите диалоговое окно со всеми пользователями системы. Теперь можно выбирать нужных пользователей и добавлять их в группу. На рис. 13.3 показано добавление пользователя в группу HotelAdmin. Обратите внимание, что JaneAdmm и Ре-terT— это ранее созданные и настроенные учетные записи пользователей. Удаление пользователей из группы выполняется с помощью кнопки Remove (Удалить).
В программе имя роли надо указывать, используя имя домена или машины. На моей машине роль CustomerAdmm обозначается как "HPDESKTOP\\CustomerAdmm", а роль HotelAdnun — как "HPDESKTOP\\HotelAdmin". Для заранее установленных групп используется префикс "BUILTIN" ("ВСТРОЕННЫЙ"), например, встроенная группа Administrators (Администраторы) обозначается как "BUILTIN\\Admmistrators" ("ВСТРОЕН-НЫЕ\\Администраторы"). Чтобы избежать проблем, связанных с переводом и интернационализацией, для обращения к встроенным ролям используется перечисление System::Security::Principal::WindowsBuiltlnRole (Система .Защита::Прин-ципал-'WindowsBuiltInRole). Вместо того, чтобы использовать строку "BUILTIN\\Admi-nistrators" ("ВСТРОЕННЫЕ\\Администраторы"), группу Administrators (Администраторы) можно обозначать как WindowsBuiltlnRole: :Administrator (Wmdows - Администратор).
Пример RoleBasedSecurity, который мы рассматривали в предыдущем разделе, на этот раз проверяет, имеет ли текущий пользователь несколько ролей Вы можете передавать роль в виде строки или использовать перечисление WindowsBuiltlnRole Помните, что необходимо изменить программу, чтобы использовать фактическое имя вашей машины, когда вы выполняете пример на вашем компьютере Для выбора ветвей кода, соответствующих членству в этих ролях, в программе используется оператор условного перехода if-else
// использовать роль, чтобы выбрать ветвь кода
String *adminRole = "HPDESKTOP\\CustomerAdmin"; // Строка
if(wp->Is!nRole(adminRole))
{
Console.:WriteLine(
"In Customer Administrator role."); // "В роли Администратора." // выбрать ветвь кода для CustomerAdmin... }
else {
Console::WriteLine(
"Not in Customer Administrator role."); // "He в роли Администратора." // не выбирать ветвь кода для CustomerAdmin... }
// использование встроенных ролей для выбора ветви кода if(wp->Is!nRole(WindowsBuiltlnRole::Administrator)) // если Администратор {
Console::WriteLine(
"In Administrator role");
// "В роли Администратора"
// выбрать ветвь кода для Администратора... }
else {
Console::WriteLine(
"Not in Administrator role."); // "He в роли Администратора." // не выбирать ветвь кода для Администратора...
}
if(wp->Is!nRole(WindowsBuiltlnRole::Guest))
// если Гость
{
Console::WriteLine( "In Guest role"); // "В роли Гостя"
// выбрать ветвь кода для Гостя... }
else {
Console::WriteLine(
"Not in Guest role."); // "He в роли Гостя." //не выбирать ветвь кода для Гостя...
}
if(wp->Is!nRole(WindowsBuiltlnRole::User))
// если Пользователь
{
Console::WriteLine(
"In User role");
// "В роли Пользователя" // выбрать ветвь кода для Пользователя...
}
else
{
Console::WriteLine (
"Not in User role"); // "He в роли Пользователя"
// не выбирать ветвь кода для Пользователя... }
Если вы зарегистрировались как Administrator (Администратор), то при выполнении примера RoleBasedSecurity получится следующий вывод
Demanding right to change principal policy
AppDomain Principal Policy changed to WindowsPrincipal
Thread::CurrentPrincipal is a WindowsPrincipal
Thread::CurrentPrincipal Name: HPDESKTOPXAdministrator
Type: NTLM IsAuthenticated: True
WindowsPrincipal Identity Name: HPDESKTOPXAdministrator
Type: NTLM Authenticated: True Token: 260
Name matches HPDESKTOPXAdministrator
In Customer Administrator role
In Administrator role
Not in Guest role
In User role
Перевод такой:
Требование права изменять политику принципала
Политика принципала AppDomain заменена WindowsPrincipal
Поток:: CurrentPrincipal - WindowsPrincipal
Поток:: CurrentPrincipal Имя HPDESKTOP\Administrator
Тип: NTLM IsAuthenticated: Истина
Имя личности WindowsPrincipal: HPDESKTOP\Administrator
Тип: Заверен NTLM: Истинная Лексема: 260
Имя совпадает с HPDESKTOPXAdministrator
В роли Клиентского Администратора
В роли Администратора
Не в роли Гостя
В роли Пользователя
Ну а если вы зарегистрировались как JaneAdmin, то при выполнении RoleBasedSecurity вывод будет следующий:
Demanding right to change principal policy
AppDomain Principal Policy changed to WindowsPrincipal
Thread::CurrentPrincipal is a WindowsPrincipal
Thread::CurrentPrincipal Name: HPDESKTOP\JaneAdmin Type:
NTLM IsAuthenticated: True
WindowsPrincipal Identity Name: HPDESKTOPXJaneAdmin Type:
NTLM Authenticated: True Token: 260
Name does not match HPDESKTOPXAdministrator
Not in Customer Administrator role.
Not in Administrator role.
Not in Guest role.
In User role
Перевод такой:
Требование права изменять политику принципала
Политика принципала AppDomain заменена WindowsPrincipal
Поток:: CurrentPrincipal - WindowsPrincipal
Поток:: CurrentPrincipal Имя: HPDESKTOPXJaneAdmin Тип:
NTLM IsAuthenticated: Истина
Имя личности WindowsPrincipal: HPDESKTOPXJaneAdmin Тип:
NTLM Заверен: Истинная Лексема: 260
Имя не совпадает с HPDESKTOPXAdministrator
Не в роли Клиентского Администратора.
Не в роли Администратора.
Не в роли Гостя.
В роли Пользователя

Другие классы личностей
Теперь более подробно рассмотрим другие классы личностей. Сейчас в .NET Framework есть четыре таких класса:
• Forms Identity используется классом FormsAuthenticationModule. Мы поговорим о классе Forms Identity, когда будет обсуждаться опознавание форм ASP.NET;
• Genericldentity может представлять любого пользователя. Этот класс используется вместе с GenericPrincipal для общих или пользовательских личностей и принципалов;
• Passportldentity используется при опознавании по схеме Passport (Паспорт). Так как Passport (Паспорт) мы не обсуждаем, то речи об этом классе вести не будем;
• Windows Identity представляет пользователя Windows. У экземпляра WindowsPrincipal значением его свойства Identity будет экземпляр Windowsldentity. Для аутентифицированных пользователей доступен тип используемого опознавания (NTLM, Kerberos (Цербер) и т.д.).
Обратите внимание, что свойства интерфейса I Identity доступны только для чте-"ния и поэтому не могут быть изменены.
Даже если ваши пользователи не аутентифицированы, вы все равно можете получить Windowsldentity для любого потока. Экземпляр Windowsldentity для текущего пользователя можно получить, используя статический метод Windowsldentity:: GetCurrent. И тогда из этого экземпляра Windowsldentity можно с помощью конструктора WindowsPrincipal получить экземпляр WindowsPrincipal. Ну, а что представляет собой эта личность, мы обсудим в следующем разделе.
Среди примеров этой главы не было варианта программы HotelBroker, который нельзя запускать, если у вас нет роли HotelBrokerAdmin. Однако нужные изменения в предыдущем варианте вы можете сделать сами. В качестве руководства используйте следующий код:
AppDomain *ap = AppDomain::CurrentDomain; ap->SetPrincipalPolicy(
PrincipalPolicy::WindowsPrincipal) ; IPrincipal *ip;
ip = Thread::CurrentPrincipal;
String *hotelAdminRole = "HPDESKTOP\\HotelAdmin"; // Строка if(!ip->IsInRole(hotelAdminRole)) {
MessageBox::Show(
"Sorry. You must be a Hotel Administrator.",
// "Жаль. Вы должны быть Администратором гостиницы.",
"Acme Customer Management System",
// "Система управления Клиентами Acme ",
MessageBoxButtons::ОК,
MessageBoxIcon::Exclamation); // Восклицательный знак return 0; }

Личность в операционной системе и общеязыковой среде выполнения CLR
Как уже говорилось в начале этой главы, защита .NET строится поверх системы защиты операционной системы компьютера. Личность, связываемая с потоком при помощи общеязыковой среды выполнения CLR, и личность, связываемая с потоком при помощи операционной системы — это не одно и то же. В операционной системе личность потока представлена значением объекта Windows Identity, возвращаемого статическим методом Windowsldentity: :GetCurrent. Ну, а личность в общеязыковой среде выполнения CLR представлена значением объекта Thread: :CurrentPrincipal8. Если вернуться к примеру, о котором говорилось в начале этой главы, то личности, как управляемые, так и неуправляемые, при доступе к файлу из .NET должны иметь на него права внутри соответствующих сред.
Значения текущего Windowsldentity и текущего Thread: :CurrentPrincipal задаются в двух разных местах: это параметры информационного сервера Internet (US) и конфигурационные файлы ASP.NET.

Разрешения коду на доступ

Для доступа к ресурсу, такому, например, как файл, или для выполнения определенной операции коду требуются разрешения Некоторые разрешения даются каждой сборке политикой безопасности (о которой еще пойдет речь в этой главе). Разрешения коду на доступ могут запрашиваться самим кодом. И тогда, исходя из политики безопасности для той или иной сборки, общеязыковая среда выполнения CLR будет решать, какие разрешения следует предоставить. О том, как писать нестандартное разрешение, мы говорить не будем
Вот некоторые примеры разрешений коду на доступ:
• DNSPermission управляет доступом к имеющимся в сети серверам доменных имен;
• EnvironmentPermission управляет доступом на чтение или запись переменных среды;
• FilelOPermission управляет доступом к файлам и каталогам;
• FileDialogPermission позволяет читать файлы, выбранные в диалоговом окне Open (Открыть). Полезно тогда, когда FilelOPermission не предоставлено;
• Ref lectionPermission управляет возможностями получения доступа к метаданным, не являющимся общедоступными, и способностью порождать метаданные,
• RegistryPermission управляет возможностью получать доступ к системному реестру и изменять его,
• SecurityPermission управляет использованием подсистемы защиты,
• SocketPermission управляет возможностью устанавливать соединения или принимать их по транспортному адресу;
• UlPPermission управляет пользователями различных средств пользовательского интерфейса, в том числе и буферами обмена;
• WebPermission управляет возможностью устанавливать соединения или принимать их по Web-адресу.
Использование этих разрешений называется защитой доступа для кода (Code Access Security), потому что в их основе лежит не личность пользователя, выполняющего код, а то, имеет ли сам код право предпринять некоторое действие.

Простой запрос разрешения кодом

В примере SimplePermissionCodeRequest вначале запрашивается разрешение на доступ к файлу. Если в ответ на этот запрос общеязыковая среда выполнения CLR разрешения не дает, то внутри конструктора файлов она запускает исключение SecurityException. Впрочем, сперва код сам проверяет, есть ли у него это разрешение. Если разрешения у кода нет, то он и не пытается получить доступ к файлу, а просто возвращает управление вызвавшей его программе.
Обычно такое действие является излишним, потому что общеязыковая среда выполнения CLR будет предъявлять свое требование внутри конструктора, но часто вам бывает нужно проверить разрешения перед выполнением некоторого кода, чтобы выяснить, имеете ли вы необходимые права.
Класс FilelOPermission моделирует разрешения (на доступ к файлам) общеязыковой среды выполнения CLR. Его конструктору должен быть предоставлен полный путь, и для получения полного пути мы используем класс Path (Путь), о котором говорилось в главе 8 "Классы каркаса NET Framework". Мы просим доступ к файлу на чтение, запись и добавление в конец (append). Другими возможными правами доступа являются NoAccess или PathDiscovery. Последнее требуется для доступа к информации о самом пути к файлу. Вы, возможно, хотите разрешить доступ к файлу, но при этом скрыть определенную информацию, такую, например, как структура каталогов или имена пользователей в пути.
В результате выдачи запроса проверяется, есть ли у вас требуемое разрешение. Метод Demand (Требование) проверяет, имеют ли это разрешение все вызывающие программы в стеке. Другими словами, нам надо убедиться, что это право имеется не только у сборки с выполняющимся кодом, но также у всех сборок, вызвавших этот код. Если исключение генерируется, то затребованного права нет, и нам придется завершить выполнение программы.
static int Main() {
String *filename = ".\\read.txt"; // Строка
String *fileWithFullPath = // Строка
Path::GetFullPath(filename); // имя файла
try
{
FilelOPermission *fileIOPerm =
new FilelOPermission(
FilelOPermissionAccess::AllAccess, fileWithFullPath); fileIOPerm->Demand(); // Запрос }
catch(Exception *e) // Исключение {
Console::WriteLine(e->Message); // Сообщение return 1; } try
{
Filelnfo *file = new Filelnfo(filename); // имя файла
StreamReader *sr = file->OpenText(); // файл
String *text; // Строка
text = sr->ReadLine();
while (text != 0)
// пока (текст! = 0)
{
Console::WriteLine(text); // текст text = sr->ReadLine(); // текст
}
sr->Close(); }
catch(Exception *e) // Исключение {
Console::WriteLine(e->Message); // Сообщение }
return 0; }
Даже если у кода есть разрешение на чтение, полученное от общеязыковой среды выполнения CLR, у пользователя все равно должно быть разрешение на чтение от файловой системы. Если у пользователя такого разрешения нет, то при вызове метода OpenText будет запущено исключение UnauthorizedAccessException (Исключение Unau-IhorizedAccess, т.е. Исключение несанкционированного доступа).
Будьте внимательны, когда нужно передать коду в других сборках объекты, прошедшие проверку на безопасность в своих конструкторах. Поскольку проверка выполнена в конструкторе, то общеязыковая среда выполнения CLR уже не делает никаких проверок прав доступа. Сборка, которой вы передаете объект, может не иметь таких же прав, как ваша. И если бы объект Filelnfo был передан другой сборке, не получившей от общеязыковой среды выполнения CLR права на чтение, то ему общеязыковая среда выполнения CLR все равно не помешала бы получить доступ к файлу. Дело в том, что в таком случае не было бы никакой дополнительной проверки на безопасность. Конечно, разработчики пошли на этот компромисс ради производительности, — чтобы избежать проверки на безопасность при каждой операции. То же самое справедливо и для других разрешений коду на доступ.

Как работает запрос на разрешение

Чтобы проверить, имеет ли код разрешение на доступ к ресурсу или на выполнение операции, общеязыковая среда выполнения CLR проверяет все вызывающие программы из стекового фрейма с целью убедиться, предоставлено ли запрошенное разрешение каждои сборке, имеющей метод в стеке. И если у какой-либо вызывающей программы из стека нет требуемого разрешения, то запускается исключение SecurityException.
Код, имеющий меньшую степень доверия, не может использовать проверенный код для выполнения несанкционированного действия. Процедуры в стеке могут принадлежать разным сборкам, имеющим разные наборы разрешений. Например, у созданной вами сборки могут быть все права, но ее может вызывать загруженный по сети компонент, права которого вы бы хотели ограничить (чтобы он не смог открыть вашу книгу адресов электронной почты).
Из следующих разделов вы узнаете, что с помощью методов Deny (Запретить) или Assert (Утвердить) базового класса CodeAccessPermission можно изменить результаты, полученные при просмотре стека.

Стратегия запроса разрешений

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

Запрет разрешений

К разрешению можно применять метод Deny (Запретить). Тогда, даже если политика безопасности и разрешает доступ к файлу, любая попытка такого доступа будет безуспешной. Это демонстрируется примером Simple-PermissionCodeDenial. Вместо того, чтобы требовать разрешение, мы вызываем метод Deny (Запретить) объекта FilelOPermission.
try {
fileIOPerm->Deny(); Console::WriteLine(
"File Access Permission Removed"); // "Запретить доступ к файлу"
}
catch(SecurityException *se)
{
Console::WriteLine(se->Message); // Сообщение
}
Затем мы попытгемся прочитать файл с помощью метода ReadFile. Вскоре мы объясним, зачем это дегается внутри другого метода. Поскольку в этом разрешении было отказано, то конструктор Filelnf о запустит исключение SecurityException.
try
{
Filelnfo *f.le = new Filelnfo (filename) ; // имя файла
StreamReade: *sr = file->OpenText() ;
String *tex;; // Строка
text = sr-XeadLine();
while (text != 0)
{
Console. :WriteLine (" {0}", text); text = ;r->ReadLine ();
}
sr->Close ():
}
catch (SecurityE<ception *se)
{
Console: : WrLteLine(
"Could iot read file: {0}",se->Message); // Сообщение
// "He :мог прочитать файл: {0} "
}
Потом, чтобы шнулировать отказ в доступе, мы вызываем статический метод RevertDeny класс; FilelOPermission, и снова пытаемся прочитать файл. На этот раз файл читать можно Данный вызов Deny (Запретить) действует до тех пор, пока содержащий его код не в>звратит управление вызвавшей программе, или до следующего вызова метода Deny (Запретить). RevertDeny удаляет все текущие запросы Deny (Запретить).
FilelOPermissicn: :RevertDeny();
ReadFile();
Затем мы вызывем метод Deny (Запретить), чтобы еще раз удалить разрешение.

Утверждение разрешений

Метод Assert Утвердить) позволяет вам требовать разрешение, даже если у вас нет соответствующих грав доступа. Вам также может потребоваться утвердить разрешение еще и потому, что, хотя ваша сборка и обладает нужным правом, но другие вызовы из цепочки вызовов егоне имеют. Утверждать вы можете только те разрешения, которые предоставлены вашейсборкой. Если бы это было не так, то обойти защиту общеязыковой среды выполненияСЬК было бы очень легко.
При внполнении примера SimplePermissionCodeDenial утверждается разрешшие FilelOPermission и делается попытка чтения файла.
fileIOPerm->Deny() ;
fileIOPerm->Assert();
ReadFilet);
ReadFileWithAssert(filelOPerm);
ReadFile() ;
Но прочитать файл не удается! Утверждение действует только в вызванном методе. Метод ReadFileWithAssert может читать файл потому, что утверждает разрешение во время своего выполнения, а затем пытается выполнить чтение. Метод Assert (Утвердить) останавливает просмотр стека разрешений, и потому выше стекового фрейма разрешения не проверяются, так что выполнение действия будет продолжено, но самих разрешений метод не выдает. Поэтому если код, расположенный ниже стекового фрейма (такой, как ReadFile), пытается требовать разрешение, в котором было отказано (так делает конструктор Filelnfo), то будет запущено исключение SecurityException11. Аналогично, Deny (Запретить) не дает выполнять действие тем вызывающим программам, которые расположены выше в стековом фрейме, а не на текущем уровне
.static void ReadFileWithAssert(FilelOPermission *f)
{
try {
f->Assert(); Console::WriteLine(
"File Permission Asserted in same procedure as read.");
// "Разрешение Файла, утвержденное
// в той же самой процедуре, что и чтение.
" Filelnfo *file = new Filelnfo(filename);
// имя файла
StreamReader *sr = file->OpenText();
// файл
String *text;
// Строка
text = sr->ReadLine(); // текст
while (text != 0)
{
Console::WriteLine(" {0}", text); // текст
text = sr->ReadLine(); // текст
}
sr->Close();
}
catch(SecurityException *se)
{
Console::WriteLine(
"Could not read file: {0}", se->Message); // Сообщение
// "He смог прочитать файл: "
}
}
He забывайте, что Assert (Утвердить) применяется только к операциям ввода-вывода, выполняемым в данной программе для указанного файла, который передается конструктоpy FilelOPermission. Вызов Assert (Утвердить) действует до тех пор, пока не завершится выполнение кода, в котором он находится. Следовательно, попытка выполнить ReadFile будет неудачной, если она делается после завершения ReadFileWithAssert. RevertAssert удаляет все текущие запросы Assert (Утвердить).
Метод Assert (Утвердить) создает бреши в защите, ведь какая-нибудь вызывающая программа из стекового фрейма вполне может использовать программу, которая вызывает этот метод как раз для нарушения безопасности. Любое использование Assert (Утвердить) должно рассматриваться с позиций защиты.
Запустите программу SimplePermissionCodeDenial и понаблюдайте за ее выводом.

Другие методы разрешений

PermitOnly указывает разрешения, которые должны быть успешными. Вы указываете, к каким ресурсам вам нужен доступ. Вызов PermitOnly действует до тех пор, пока не завершится выполнение кода, в котором он находится, или до следующего вызова этого метода. RevertPermitOnly удаляет все текущие запросы PermitOnly. RevertAll отменяет результаты выполнения Deny (Запретить), PermitOnly и Assert (Утвердить).

Класс SecurityPermission

Класс SecurityPermission управляет "метаразрешениями", которые, в свою очередь, управляют подсистемой защиты общеязыковой среды выполнения CLR. Давайте снова обратимся к примеру RoleBasedSecurity, который уже рассматривали в этой главе. В нем с помощью метода AppDomain: : SetPrincipalPolicy задавалась политика принципалов для прикладных областей:
AppDomain *ap = AppDomain::CurrentDomain;
ap->SetPrincipalPolicy(
PrincipalPolicy::WindowsPrincipal);
Тип принципала, возвращаемый Thread: :CurrentPrincipal, будет зависеть от политики принципалов прикладной области. В перечислении System: : Security:: PrincipalPolicy определены следующие три политики опознавания для прикладной области:
• WindowsPrincipal применяет текущего пользователя, связанного с потоком. Thread: : CurrentPrincipal возвращает объект WindowsPrincipal;
• UnauthenticatedPrincipal применяет неаутентифицированного пользователя. Thread: :CurrentPrincipal возвращает объект GenericPrincipal. Это политика, задаваемая по умолчанию;
• NoPrincipal возвращает пустой указатель (null) для Thread: : CurrentPrincipal.
Политику можно задавать с помощью метода SetPrincipalPolicy экземпляра AppDomain, относящегося к текущей прикладной области. Статический метод AppDomain: :CurrentDomain вернет текущий экземпляр. Этот метод должен вызываться перед любым вызовом Thread: :CurrentPrincipal. Дело в том, что объект принципала не создается до первой попытки доступа к этому свойству.
Чтобы при выполнении примера RoleBasedSecurity можно было задавать политику принципалов, у него должно быть право ControlPrincipal. С целью убедиться, что выполняемый код имеет такое право, перед изменением политики можно вызвать метод Demand (Требование) объекта SecurityPermission. Если у вас нужного разрешения нет, то будет запущено исключение SecurityException.
SecurityPermission *sp = new SecurityPermission( SecurityPermissionFlag::ControlPrincipal) ;
try
{
// Проверить, имеют ли все вызывающие программы выше в стеке
// вызовов разрешение управлять
// объектом принципала перед выполнением действий над ним.
sp->Demand();
}
catch(SecurityException *se)
{
// не может управлять объектом принципала
Console::WriteLine(se->Message); // Сообщение
return;
}
Сначала мы создаем новый экземпляр SecurityPermission, передавая конструктору нужное нам разрешение защиты, чтобы видеть, имеем ли мы право использовать это разрешение. SecurityPermissionFlag— это перечисление разрешений, используемых классом SecurityPermission. Разрешение ControlPolicy предоставляет право изменять политику. Очевидно, такое право должно быть предоставлено только проверенному коду. Затем мы требуем (запрашиваем) само разрешение.
Как уже говорилось, вы можете утверждать только те разрешения, которыми ваша сборка действительно обладает. Поэтому жульнические компоненты при выполнении в вашем коде не смогут просто так утверждать разрешения. Вы можете либо задавать политику безопасности, либо с помощью класса SecurityPermission запретить компонентам вызывать метод Assert (Утвердить). Создайте экземпляр этого класса со значением SecurityPermissionFlag: :Assertion, а затем примените к разрешению метод Deny (Запретить). Есть и другие действия, которые можно контролировать с помощью класса SecurityPermission. Среди них укажем следующие: создание прикладных областей и управление этими областями, задание политики, разрешение или запрещение выполнения, слежение за выполнением проверки правильности, а также получение доступа к неуправляемому коду.

Неуправляемый код


Утверждения необходимы для того, чтобы управлять доступом к неуправляемому коду. Дело в том, что этот код не должен прямо вызываться управляемым кодом. Чтобы вызывать неуправляемый код, требуется соответствующее разрешение. Поскольку общеязыковая среда выполнения CLR просматривает стек, чтобы проверить, имеют ли все вызывающие программы разрешение на вызов неуправляемого кода, то разрешение на вызов неуправляемого кода надо предоставить всему коду. Следовательно, сборки, не относящиеся к тем, которым вы доверяете, могли бы выполнять операции с помощью вызовов интерфейса 32-разрядных Windows-приложений (API Win32) и подрывать систему безопасности .NET.
Лучше делать вызовы через интерфейсные классы сборки, которая имеет право на управляемый код. Код интерфейсного класса должен сначала выяснить, есть ли у вызывающей программы соответствующие права от общеязыковой среды выполнения CLR, и поэтому требует, чтобы вызывающая программа имела минимальный набор разрешений, необходимых для выполнения задачи (такой, например, как запись в файл). Если требование прав удовлетворено, то интерфейсный код может утвердить право на управляемый код. Тогда никакой другой сборке из цепочки вызовов право на управляемый код не требуется.
Например, когда вы просите файловые .NET-классы удалить какой-либо файл, то вначале они требуют разрешение на удаление этого файла. Если разрешение получено, то код утверждает право на вызов управляемого кода и вызывает для выполнения удаления интерфейс 32-разрядных Windows-приложений (API Win32).

Разрешения на основе атрибутов

Пример SimplePermissionAttributeRequest показывает, как использовать атрибуты для выдачи запросов на те или иные разрешения. В этом примере атрибут нужен для того, чтобы передать метаданные в сборку, для запуска которой требуется разрешение Centres JLPrincipal. Таким образом можно заранее узнать, какие компоненты конфликтуют с политикой безопасности.
[assembly: Secur: i_t у Permission (
SecurityAc t ion: : RequestMinimum,
ControlPrinc:ipal=true) ] ;
public _gc class PermAttrib
// класс сборицхнгка мусора PermAttrib
{
public:
static int Imain()
{
В перечислении SecurityAction имеется несколько значений, часть из которых можно применягь к: классу или методу, а часть, как в этом примере, — к сборке. К сборкам применяются RequestMinimum, RequestOptional И RequestRefuse. RequestMinimu-m указывает на те метаданные, чьи разрешения необходимы для запуска сборки. Reques-tOjptional указывает на те разрешения метаданных, которые сборке неплохо бы им:еть., но без которых, впрочем, она все-таки может выполняться. RequestRefuse указывает на те разрешения, от которых сборка хотела бы отказаться15.
Если в примере SimplePermissionAttributeRequest значение атрибута поменять на RequestRefuse, а затем этот пример запустить, то хотя сборка и будет загружена, попытка изменить политику приведет к запуску исключения SecurityException.
Остальные значения применяются к классам и методам. Значение LinkDemand нужно тогда, когда делается ссылка на некоторый тип. При этом требуется, чтобы непосредственно вызвавшая программа имела разрешение. Другие значения применяются во время выполнения. InheritanceDemand требует, чтобы разрешение имелось у производного класса. Assert (Утвердить), Deny (Запретить), PermitOnly и Demand (Требование) требуют именно то, что от них можно ожидать.
Вот пример требования FilelOPermission, применяемого к классу с помощью атрибута. Для файла требуется Al lAcce s s. Надо обязательно указывать полный путь к файлу.
[FilelOPermission(
SecurityAction::Demand,
All = "c:\\foo\\read.txt")]
public class Simple
// общедоступный Простой класс
{
};

Разрешение принципала

Безопасность на основе ролей управляется классом PrincipalPermission. Пример под тем же названием проверяет с помощью этого класса, что личность пользователя, под которой запускается программа, — это Administrator (Администратор). Мы это делаем, передавая конструктору имя личности и строку, представляющую роль. И снова для разрешения мы применяем метод Demand (Требование), чтобы проверить законность запроса на разрешение.
// Требовать, чтобы код выполнял Администратор
PrincipalPermission *pperm = new PrincipalPermission(
wi->Name, adminRole); // Имя try
{
pperm->Demand(); Console::WriteLine(
"Code demand for an administrator succeeded."); // "Запрос кода для Администратора успешный."
}
catch(SecurityException *)
{
Console::WriteLine(
"Demand for Administrator failed."); // "Запрос для Администратора потерпел неудачу."
}
Если программу запустил администратор, то требование успешно удовлетворяется. В противном случае оно не удовлетворяется и запускается исключение. Затем код проверяет, является ли именем пользователя JaneAdmin (это имя не системного администратора, и оно всего лишь входит в группу Customer Admin), и что указанная роль выполняет программу.
String *customerAdminRole = // Строка "HPDESKTOP\\CustomerAdmin";
PrincipalPermission *pp;
pp = new PrincipalPermission(
"HPDESKTOP\\JaneAdmin", customerAdminRole);
try
{
pp->Demand(); Console::WriteLine(
"Demand for Customer Administrator succeeded.");
// "Запрос для Администратора клиентов успешный."
}
catch(SecurityException *)
{
Console::WriteLine(
"Demand for Customer Administrator failed.");
// "Запрос для Администратора клиентов потерпел неудачу."
}
В базовом классе CodeAccessPermission имеются методы создания разрешений, представляющих собой объединение или пересечение нескольких разрешений. Класс PrincipalPermission не является производным от CodeAccessPermission, потому что он работает на основе личности, связанной с кодом, а не с правами на код. Но как бы там ни было, он использует те же идиомы, что и классы, производные от CodeAccessPermission.
Ниже приведен пример кода, проверяющего, запустила ли его одна из двух указанных личностей администраторов.
String *idl = "HPDESKTOP\\Administrator"; // Администратор String *id2 = "HPDESKTOP\\PeterT"; // Строка
PrincipalPermission *ppl =
new PrincipalPermission(idl, adminRole); PrincipalPermission *pp2 =
new PrincipalPermission(id2, adminRole);
IPermission *ipermission = pp2->Union(ppl); // Объединение - try {
ipermission->Demand() ; Console: :WriteLine(
"Demand for either administrator succeeded."); // "Запрос для любого из администраторов успешный." }
catch(SecurityException *) {
Console::WriteLine (
"Demand for either administrator failed.");
// "Запрос для любого из администраторов потерпел неудачу."
}
Затем код смотрит, запущен ли он личностью любого из администраторов.
Principal-Permission *pp3 =
new PrincipalPermission(0, adminRole);
try
{
pp3->Demand(); Console::WriteLine(
"Demand for any administrator succeeded.");
// "Запрос для любого администратора успешный."
}
catch(SecurityException *)
{
Console::WriteLine(
"Demand for any administrator failed.");
// "Запрос для любого администратора потерпел неудачу."
}
Когда пользователи не аутентифицированы, то даже если они действительно принадлежат соответствующим ролям, выполнение Demand (Требование) все равно будет неудачным.


Резюме

В этой главе мы попытались объяснить, в чем состоят основы защиты NET Такая система защиты бывает двух видов защита личности пользователя и защита доступа к коду (Code Access Security) В первом случае определяется, какая личность выполняет код Ну, а во втором, — какие права есть у выполняющегося кода Имея то и другое, вы обеспечены простыми инструментами для создания устойчивых приложений
Что сейчас отсутствует в защите NET, так это распределенная личность и защита доступа к распределенному коду Удаленный код не может использовать информацию политики для принятия решений, да и личность не передается автоматически с помощью удаленных вызовов


 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г. Яндекс.Метрика