NET программирование
Главная Новости
Framework .NET
Visual Studio .NET Самоучитель C# Учебники О сайте
Что такое Microsoft .NET?
Основы технологии .NET
Программирование на управляемом C++
Объектно-ориентированное программирование на управляемом C++
Управляемый C++ в .NET Framework
Создание графических пользовательских интерфейсов
Сборки и развертывание
Классы каркаса .NET Framework
Программирование в ADO.NET
ASP.NET и Web-формы
Web-службы
Web-узлы и Web-службы,работающие на основе ATL Server
Защита
Трассировка и отладка в .NET
Смешивание управляемого и неуправляемого кода
Приложение. Visual Studio.NET
Защита

Защита, или безопасность — это одно из основных требований к приложениям и при разработке ее следует учитывать не в последнюю очередь Однако из педагогических соображений говорить о защите легче тогда, когда уже состоялось знакомство с прикладной моделью 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 (Система-Данные).

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

Хотя в этой главе основное внимание уделено безопасности .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 данные, используемые системой защиты, надо шифровать.

Большинство людей имеют хотя бы интуитивное представление о пользователях и паролях. В то же время сервер транзакций корпорации 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 отличаются от ролей СОМ+.

Роли в 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 (Добавление группы). Появится диалоговое окно, которое вам предстоит заполнить.

Рис 13.1. Группы, определенные в машине

Это диалоговое окно, уже заполненное данными для создания новой группы Hote-lAdmin, показано на рис. 13.2. В группу HotelAdmin должны входить все пользователи машины, которые могут добавлять или изменять информацию о гостиницах в системе HotelBroker. Чтобы добавить группу в систему, надо щелкнуть на кнопке Create (Создать). Добавление пользователей в систему или их удаление из нее выполняется, соответственно, с помощью кнопок Add (Добавить) и Remove (Удалить).

Чтобы внести изменения в существующей группе, выделите ее, щелкните на ней правой кнопкой мыши и выберите команду Properties (Свойства). Щелкнув на кнопке Add (Добавить), вы получите диалоговое окно со всеми пользователями системы. Теперь можно выбирать нужных пользователей и добавлять их в группу. На рис. 13.3 показано добавление пользователя в группу HotelAdmin. Обратите внимание, что JaneAdmm и Ре-terT— это ранее созданные и настроенные учетные записи пользователей. Удаление пользователей из группы выполняется с помощью кнопки Remove (Удалить).

Рис 13.2 Диалоговое окно, в котором создается группа HotelAdmin

В программе имя роли надо указывать, используя имя домена или машины. На моей машине роль CustomerAdmm обозначается как "HPDESKTOP\\CustomerAdmm", а роль HotelAdnun — как "HPDESKTOP\\HotelAdmin". Для заранее установленных групп используется префикс "BUILTIN" ("ВСТРОЕННЫЙ"), например, встроенная группа Administrators (Администраторы) обозначается как "BUILTIN\\Admmistrators" ("ВСТРОЕН-НЫЕ\\Администраторы"). Чтобы избежать проблем, связанных с переводом и интернационализацией, для обращения к встроенным ролям используется перечисление System::Security::Principal::WindowsBuiltlnRole (Система .Защита::Прин-ципал-'WindowsBuiltInRole). Вместо того, чтобы использовать строку "BUILTIN\\Admi-nistrators" ("ВСТРОЕННЫЕ\\Администраторы"), группу Administrators (Администраторы) можно обозначать как WindowsBuiltlnRole: :Administrator (Wmdows - Администратор).

Рис 13.3. Добавление в группу пользователя JaneAdmin Уже добавлен пользователь PeterT

Пример 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; }


Как уже говорилось в начале этой главы, защита .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 управляет "метаразрешениями", которые, в свою очередь, управляют подсистемой защиты общеязыковой среды выполнения 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 (Требование) все равно будет неудачным.

С набором разрешений можно работать, используя класс PermissionSet. Методы AddPermission и RemovePermission дают возможность добавлять в набор экземпляры класса, производного от CodeAccessPermission. Тогда методы Deny (Запретить), PermitOnly или Assert (Утвердить) можно применять не к отдельным разрешениям, а к целым их наборам. Таким образом легче указать, что разрешается компонентам и сценариям сторонних производителей. Пример PermissionSet показывает, как это делается.

Вначале мы определяем интерфейс lUserCode, который будет использоваться нашим "проверенным" кодом для доступа к некоторому коду сторонних производителей. Хотя на самом деле этот "сторонний" код обычно располагается в отдельной сборке, но, чтобы не усложнять пример, мы все поместили в одну и ту же сборку.

public _gc _interface lUserCode 
// сборщик мусора - интерфейс lUserCode 
{ 
int PotentialRogueCode(); 
}; 
public _gc class ThirdParty : 
public lUserCode // класс сборщика мусора 
ThirdParty: lUserCode 
{ 
public: 
int PotentialRogueCode() 
{ 
try { 
String *filename = ".\\read.txt"; // Строка 
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; 
} 
};

Наш код создаст новый экземпляр "стороннего" класса, который должен загрузить код в нашу сборку. Затем мы вызываем метод OurCode, передавая ему "сторонний" код.

static int Main() 
{ 
ThirdParty *thirdParty = new ThirdParty; 
OurClass *ourClass = new OurClass; 
ourClass->OurCode(thirdParty); 
return 0; 
}

Теперь посмотрим на метод OurCode. Он создает набор разрешений, состоящий из неограниченных разрешений на пользовательский интерфейс и на доступ к файлам. Затем он отменяет разрешения, находящиеся в этом наборе.

void OurCode(lUserCode *code) 
{ 
UlPermission *uiPerm = new UlPermission( 
PermissionState::Unrestricted); // Неограниченный 
FilelOPermission *fileIOPerm = new FilelOPermission( 
PermissionState::Unrestricted); // Неограниченный PermissionSet 
*ps = 
new PermissionSet(PermissionState::None); 
ps->AddPermission(uiPerm) ; 
ps->AddPermission(filelOPerm); 
ps->Deny(); 
Console::WriteLine("Permissions denied."); // "Разрешения запрещены." 
return; 
}

Потом вызывается "сторонний" код. После возврата из него запрет разрешения отменяется и снова вызывается "сторонний" код.

int v = code->PotentialRogueCode(); 
CodeAccessPermission::RevertDeny(); 
Console::WriteLine("Permissions allowed.") ; 
// "Разрешения позволены." 
v = code->PotentialRogueCode() ;

При первом вызове PotentialRogueCode выполнение кода завершается аварийно, а при. втором — успешно. Каждый фрейм в стеке может иметь только один набор разрешений для отказа. Вызывая Deny (Запретить) для набора разрешений, вы перекрываете все остальные вызовы Deny (Запретить) для этого набора в стековом фрейме.

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

Идентифицировать работающий код можно с помощью нескольких классов разрешений для личности:

  • PublisherldentityPermission моделирует цифровую подпись издателя программного обеспечения;
  • SiteldentityPermission моделирует Web-узел, с которого поступил код;
  • StrongNameldentityPermission моделирует строгое имя сборки;
  • ZoneldentityPermission моделирует зону, из которой поступил код;
  • URLIdentityPermission моделирует унифицированный указатель информационного ресурса (URL) и протокол, по которому принят код.

Эти разрешения представляют подтверждение, которое можно использовать для определения политики безопасности. Разрешения для личности — это не разрешения на доступ к коду.

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

В классе AppDomain имеется функция ExecuteAssembly, которая заставляет сборку выполняться. Ее единственным аргументом является экземпляр класса Evidence (Подтверждение). Этот класс является коллекцией объектов, которая представляет личность сборки, и в то же время является коллекцией объектов, которые представляют подтверждение.

Сказанное иллюстрируется примером Evidence (Подтверждение). При его выполнении получится коллекция подтверждений, ассоциированная со строгим именем сборки, а также распечатаются соответствующие значения.

Evidence *ev = AppDomain::CurrentDomain->Evidence; 
lEnumerator *iEnum = ev->GetEnumerator(); 
bool bNext; // логическая (булева) переменная bNext 
Console::WriteLine( 
"Evidence Enumerator has {0} members", 
// "Перечисление Evidence имеет {0} членов " 
_box(ev->Count)}; bNext = iEnum->MoveNext(); while (bNext == true) 
// пока (bNext == истина) { 
Object *x = iEnum->Current; 
Type *t = x->GetType(); 
Console::WriteLine(t->ToString()); 
if (t == _typeof(Zone)) // если Зона 
{ 
Zone *zone = dynamic_cast<Zone *>(x); 
// Зона Console::WriteLine( 
" {0}", _box(zone->SecurityZone)); // зона 
} 
else if (t == _typeof(Url)) // если Url 
{ 
Url *url = dynamic_cast<Url *>(x); Console::WriteLine( 
" {0}", url->Value); // Значение } 
else if (t == _typeof(Hash)) { 
Hash *hash = dynamic_cast<Hash *>(x); 
unsigned char mdSHash _gc [] = hash->MD5; 
unsigned char shalHash _gc [] = hash->
SHAl; Console::WriteLine( 
MD5 Hash of Assembly:"); Console::Write(" "); 
for(int i = 0; i < md5Hash->Length; i++) 
Console::Write(mdSHash[i]); 
// Запись Console::WriteLine(); Console::WriteLine( 
SHA1 Hash of Assembly:"); Console::Write(" "); 
// Запись forfint i = 0; i < shalHash->Length; i++) 
Console::Write(shalHash[i]); // Запись Console::WriteLine (); } 
else if (t == _typeof(StrongName)) { 
StrongName *sn = dynamic_cast<
StrongName *>(x); Console::WriteLine( 
" StrongName of Assembly is: {0} version: {!}", 
// версия sn->Name, // Имя sn->Version); // Версия Console::WriteLine( 
" Assembly public key:"); Console::Write( // Запись "); 
Console::WriteLine( sn->PublicKey); } 
bNext = iEnum->MoveNext(); }

При выполнении этого примера должна получиться примерно следующая выдача:

Evidence Enumerator has 4 members System. Security.Policy.Zone 
MyComputer System.Security.Policy.Url 
file:///C:/01/NetCpp/Chapl3/Evidence/Debug/Evidence.exe System.Security.Policy.StrongName 
StrongName of Assembly is: Evidence version: 1.0.685.28667 
Assembly public key: 0024000004800... 
...EA897BA System.Security.Policy.Hash 
MD5 Hash of Assembly: 
41601023422625513614244127248522225222668 
SHA1 Hash of Assembly: 
216132245725424821. . .

Перевод такой:

Перечисление Evidence имеет 4 члена Система. Защита. Политика. Зона 
MyComputer Система. Защита. Политика. Url 
file:///С:/OI/NetCpp/Chapl3/Evidence/Debug/Evidence.exe 
Система. Защита. Политика. 
StrongName 
StrongName сборки: 
версия Evidence: 1.0.685.28667 
Открытый ключ сборки: 0024000004800... 
... ЕА897ВА Система. 
Защита. 
Политика. 
Хэш-код 
MD5 Хэш-код сборки: 
41601023422625513614244127248522225222668 
SHA1 Хэш-код сборки: 
216132245725424821...

Для этой сборки подтверждением, относящимся к Zone (Зона), является MyComputer. Подтверждение Url — это местоположение сборки на диске. Что касается подтверждения Hash, то оно может дать нам хэш-коды сборки, а именно MD5 и SHA1. Подтверждение StrongName дает нам информацию об уникальном имени сборки, полученном из атрибута AssemblyKeyFileAttribute("KeyPair.snk") в исходном файле Assemblylnfo.cpp.

Кое-что из этого подтверждения можно преобразовать в соответствующие разрешения для личности. Например, у класса Zone (Зона) имеется метод Createldenti-tyPermission. Он возвращает интерфейс iPermission, который представляет, в свою очередь, экземпляр ZoneldentityPermission, связанный с этой частью подтверждения. Похожие методы имеются у классов Url (унифицированный указатель информационного ресурса) и StrongName.

Другой способ просмотра разрешений для личности — это получить ответы на рад вопросов.

  • Кто опубликовал (подписал) этот код?
  • Какое имя сборки?
  • Из какого Web-узла или унифицированного указателя информационного ресурса (URL) она была загружена?
  • Из какой зоны получен код?

Создатель прикладной области (хоста) может также предоставить подтверждение, передавая его коллекции Evidence (Подтверждение) при вызове метода ExecuteAssembly. Конечно, у такого кода должно быть разрешение ControlEvidence. Общеязыковой среде выполнения CLR также доверено добавлять подтверждение, ведь, кроме всего прочего, именно она проводит политику безопасности. Подтверждение можно расширять: вы можете определять свои типы подтверждений и использовать их в политике безопасности.

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

Политика безопасности задается на нескольких уровнях. Предоставленные разрешения определяются пересечением уровней политики. Этими уровнями являются предприятие, машина, прикладная область и пользователь. Если между разрешениями, предоставленными на определенном уровне, возникает конфликт, то выбирается более ограничивающий вариант. Итак, политика предприятия имеет приоритет над политикой каждой машины предприятия, а политика машины — над любой политикой прикладной области или отдельного пользователя.

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

Корневой узел указанной структуры называется "All_Code". Ниже этого уровня находится набор дочерних узлов, и у каждого из них могут быть свои дочерние узлы. Каждый узел представляет некоторую кодовую группу. Если код принадлежит к какой-либо кодовой группе, то он может быть членом одной из ее дочерних групп. Если же код не принадлежит к определенной кодовой группе, то ни к одной из ее дочерних групп он принадлежать не может.

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

У кодовых групп может быть два атрибута. Атрибут exclusive (исключающий) требует, чтобы код никогда не получал разрешений, кроме тех, что приписаны исключительной группе. Очевидно, код может принадлежать только к одной группе, отмеченной как исключительная. Ну а второй атрибут, level final (конечный уровень), указывает, что при вычислении членства кодовых групп не следует учитывать никаких уровней, находящихся ниже текущего уровня политики. Уровни рассматриваются в следующем порядке: предприятие, машина, пользователь и прикладная область.

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

  • Nothing (Ничего). Нет разрешений, то есть работать нельзя;
  • Execution (Выполнение). Разрешен только запуск, но нельзя использовать защищенные ресурсы;
  • Internet. Набор разрешений, определяемый политикой по умолчанию, подходит для содержимого неизвестного происхождения;
  • Locallntranet. Набор разрешений, определяемый политикой по умолчанию, применяемой в пределах предприятия;
  • Everything (Все). Все стандартные (то есть встроенные) разрешения, за исключением разрешения опускать проверку;
  • FullTrust. Полный доступ ко всем ресурсам, защищенным разрешениями, которые могут быть неограниченными.

Из всех встроенных именованных наборов разрешений изменять можно только Everything (Все). В любом случае вы можете определять свои собственные наборы разрешений.

Политика безопасности хранится в нескольких XML-файлах конфигурации. Конфигурация защиты машины находится в файле security.config, который хранится в папке

\WINNT\Microsoft.NETXFramework\vx.x.xxxx\CONFIG. Конфигурация защиты пользователя также хранится в файле security. config, но этот файл хранится в другой папке, \Documents and Settings\UserName\Application DataXMicrosoft\CLR Security Config\vx.x.xxxx.

Редактировать эти XML-файлы напрямую не рекомендуется. Делать изменения в политике на уровнях предприятия, машины и пользователя можно с помощью сервисной программы caspol.exe (Code Access Security Policy, Политика защиты доступа к коду), запускаемой с командной строки.

Рис 13.4. Наборы разрешений и группы для политики на уровне машины

Сервисная программа администрирования .NET Admin Tool, которая также позволяет изменять политику, имеет более дружественный интерфейс. Она была описана в главе 7 "Сборки и развертывание". Эта сервисная программа является дополнительным модулем, присоединенным к консоли ММС, ее можно запустить двойным щелчком на файле \WINNT\Microsoft.NET\Framework\vl. 0.2914 \mscorcf g.mse. (Если версия сервисной программы больше, то папка будет другой.) На рис. 13.4 показаны кодовые группы и наборы разрешений, определенные для машины, а также уровни политики безопасности текущего пользователя — именно так, как они отображаются в левой области окна сервисной программы администрирования .NET Admin Tool.

Давайте с помощью этого инструмента изучим текущие политики на машинном уровне. Вначале посмотрим на именованные наборы разрешений. Как видно из рис. 13.4, кроме именованных наборов разрешений, заданных по умолчанию, на машинном уровне пока что не создано ни одного такого набора. Если выбрать именованный набор разрешений Internet, а затем в правой области окна — View Permissions (Просмотр разрешений), то можно будет выбирать любое разрешение и просматривать его параметры. На рис. 13.5 как раз показаны параметры для разрешения Userlnterface, находящегося в именованном наборе Internet.

Рис 13.5. Разрешения Userlnterface из набора Internet для машинного уровня

На рис. 13.6 показаны свойства кодовой группы в зоне Internet для политики на машинном уровне. Видно, что для этой группы выбран тип условия Zone (Зона), а значением, связанным с этим типом, является зона Internet. Просмотреть или выбрать именованный набор разрешений, связанный с зоной Internet, можно на вкладке Permission Set (Набор разрешений).

Чтобы показать, как политика безопасности влияет на выполнение кода, рассмотрим пример Policy (Политика). Этот пример, если не учитывать показанный ниже код, аналогичен уже рассмотренному примеру Evidence (Подтверждение) Кроме вывода информации, относящейся к подтверждению, при выполнении примера Policy (Политика) выводится также содержимое файла read txt.

String *filename = ".\\read.txt"; // Строка 
try 
{ 
String *fileWithFullPath = // Строка 
Path::GetFullPath(filename); // имя файла 
Filelnfo *file = new Filelnfo(filename); // имя файла 
StreamReader *sr = file->OpenText(); // файл 
String *text; // Строка 
text = sr->ReadLine(); // текст 
while (text '=0) 
// пока (текст1 = 0) 
{ 
Console::WriteLine(text); // текст 
text = sr->ReadLine(); // текст 
} 
sr->Close(); 
} 
catch(Exception *e) // Исключение 
{ 
Console::WriteLine(e->Message); // Сообщение 
}

Если, ничего не изменяя в пользовательской политике безопасности, заданной по умолчанию, выполнить программу Policy (Политика), то можно увидеть, что эта программа успешно читает файл Ну, а если сделать в политике описанные ниже изменения, то при попытке прочитать файл выполнение этого примера аварийно завершится с запуском исключения Security Exception (Исключение защиты)

Рис 13.6. Диалоговое окно свойств для зоны Internet, политика на машинном уровне

На рис 13 7 показана проводимая по умолчанию пользовательская политика (Сейчас мы работаем не на машинном уровне, а на уровне пользователя )

Теперь, чтобы можно было управлять политикой безопасности для сборки примера Policy (Политика), мы добавим новый именованный набор разрешений, TestStrongName, а также новую именованную кодовую группу, TestStrongNameGroup

Рис 13.7. Проводимая по умолчанию пользовательская политика

Рис 13.8. Начальный экран диалога Create Permission Set (Создать набор разрешений)

Чтобы добавить новый набор разрешений, щелкните правой клавишей мыши на узле PermissionSets, расположенном под нужным уровнем (в данном случае под уровнем пользователя (User)) Затем выберите команду New (Создать) Появится начальный экран диалога Create Permission Set (Создать набор разрешений), который можно заполнять данными так, как показано на рис. 13.8.

Затем щелкните на кнопке Next (Далее) Тогда вы увидите следующий экран диалога Create Permission Set (Создать набор разрешений), рис. 13.9.

Редактировать те или иные элементы набора разрешений можно, как показано на рис. 13.10, с помощью кнопок Add (Добавить) и Remove (Удалить) Установить значения параметров разрешения можно в диалоге Permission Settings (Параметры разрешения) (здесь не показан) при добавлении разрешения В диалоговом окне Permission Settings (Параметры разрешения) для конкретного разрешения можно выбрать все необходимые значения параметров, а также установить переключатель для того, чтобы сборки получили неограниченный доступ к конкретному разрешению Результат показан на рис 13 10

Чтобы завершить создание набора разрешений, щелкните на кнопке Finish (Готово) В конце названия узла политики User (Пользователь) должна появиться звездочка, которая означает, что узел был изменен, но изменения еще не сохранены Чтобы их сохранить, щелкните правой кнопкой мыши на узле политики User (Пользователь) и выберите команду Save (Сохранить)

Захотев изменить уже добавленное разрешение, в сервисной программе администрирования NET Admin Tool выполните двойной щелчок на названии разрешения, и сделайте нужные изменения На рис 1311 показано диалоговое окно, в котором можно изменять разрешение Userlnterface

Рис 13.9. Второй экран диалогового окна Create Permission Set (Создать набор разрешений)

Рис 13.10. Определение набора разрешении TestStrongName

Рис 13.11. Изменения в разрешении

Если вы изменили какую-либо политику, то в конце ее названия также появится звездочка, указывая, что эти изменения еще не сохранены. Чтобы их сохранить, щелкните правой кнопкой на узле политики User (Пользователь) и выберите команду Save (Сохранить).

Теперь это разрешение следует связать с кодовой группой, причем этой группе должна быть назначена сборка Policy.exe. Именем новой кодовой группы будет TestStrong-NameGroup. Данная группа создается путем выбора родительской группы (в нашем случае это All_Code) и затем в контекстном меню — команды New (Создать). Далее надо ввести нужную для мастера информацию. Появится диалоговое окно Create Code Group (Создать кодовую группу), которое заполняется так, как показано на рис. 13.12.

Введя в это окно нужную информацию, щелкните на кнопке Next (Далее). Тогда будет выведен второй экран диалога Create Code Group (Создать кодовую группу), который дает возможность указать условие членства кода в группе. Сборкам, удовлетворяющим указанному условию, будут предоставлены разрешения, относящиеся к этой кодовой группе. В поле со списком выберите, как на рис. 13.13, набор разрешений StrongName. Кроме того, укажемте открытый ключ для строгого имени сборки Policy.exe. Этот открытый ключ выводится при запуске программы Policy.exe; его также можно получить из сборки с помощью Ildasm.exe.

Щелкните на кнопке Next <Далее) — и вы увидите третий экран диалога Create Code Group (Создать кодовую груп пу), который дает возможность назначить кодовой группе тот или иной набор разрешений. Б$ ыберите созданный ранее набор TestStrongName (рис. 13.14).

После этого щелкните на кнопке Next (Далее), а затем — на кнопке Finish (Готово). Теперь надо будет сохранить политику в узле User (Пользователь); с этой целью нужно щелкнуть на узле правой кнопкой мыши и выбрать команду Save (Сохранить).

Рис 13.12. Создаг-tue кодовой группы TestStrongNameGroup

Рис 13.13. Задание условия для кодовой группы TestStrongNameGroup

Чтобы просмотреть сделанные вами изменения, в сервисной программе администрирования .NET Admin Tool щелкните на TestStrongNameGroup и выберите Properties (Свойства). На рис. 13.15 показано, что группа TestStrongNameGroup связана со строгим именем в сборке policy.exe. А на рис. 13.16 видно, что эта группа связана с набором разрешений TestStrongName.

На вкладке General (Общие) установите, как показано на рис. 13.17, флажок "This policy level will only have the permissions from the permission set associated with this code group" ("Этот уровень политики будет иметь разрешения только из набора разрешений, связанного с данной кодовой группой").

Установка этого флажка означает, что для сборки будут использоваться только те разрешения, которые явно указаны в наборе разрешений. Если флажок не будет установлен, то вы не сможете увидеть запуск исключения безопасности, когда программа Policy (Политика) попытается что-либо записать в файл. Щелкните на кнопке ОК. Затем щелкните правой кнопкой на узле User (Пользователь) и выберите команду Save (Сохранить).

Вспомните, что в созданный ранее набор разрешений TestStrongName не было добавлено FilelOPermission. Еще вспомните, что программа Policy.exe пытается читать файл. Чтобы опять увидеть результат такого чтения, снова запустите Policy.exe. Теперь вы заметите, что эта программа запустила исключение безопасности (Security exception), потому что в связанном с ней наборе разрешений нет FilelOPermission.

На рис. 13.18 показаны новая кодовая группа, которая называется TestStrongNameGroup, а также новый набор разрешений. Этот набор разрешений называется TestStrongName. Он был добавлен на уровне политики пользователя, чтобы управлять политикой безопасности для сборки Policy (Политика).

Рис 13.14. Присвоение набора разрешений кодовой группе Test-StrongNameGroup

Рис 13.15. Условие членства в группе TestStrongNameGroup

Рис 13.16. Разрешения из набора разрешении Test-StrongName, связанные с группой TestStrongNameGroup

Рис 13.17. Указание использовать только разрешения из набора разрешении

Рис 13.18 Измененный уровень политики пользователя для примера Policy (Политика)

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

Что сейчас отсутствует в защите NET, так это распределенная личность и защита доступа к распределенному коду Удаленный код не может использовать информацию политики для принятия решений, да и личность не передается автоматически с помощью удаленных вызовов

назад главная вперед

Главная Новости Visual Studio .NET Самоучитель C# Учебники О сайте
Framework 3.0
Microsoft официально заявила о переименовании своего API WinFX в более привычный для слуха .NET Framework 3. Это, вероятно, последнее переименование. Нет уже Longhorn (Vista), канули в прошлое Avalon (WPF), Indigo (WCF). Время пришло переименовать WinFX…
DirectX 10
Новый набор интерфейсов программирования приложений (API) DirectX 10 увеличит скорость работы компьютеров при запуске ряда приложений в 4 раза. Об этом заявили разработчики DirectX компания Microsoft, сообщается на сайте The Inquirer...
Microsoft в России
Компания Microsoft открыла производство в России, пишет газета "Ведомости". Диски с Windows и Microsoft Office с марта печатаются на ярославском полиграфическом комбинате ООО "Сонопресс". Sonopress входит в концерн Bertelsmann и производит диски также для Sony BMG...
счетчик посещений
Используются технологии uCoz