WWW.DISSERS.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА

   Добро пожаловать!

Pages:     | 1 |   ...   | 2 | 3 || 5 | 6 |   ...   | 12 |

«Michael Howard David LeBlank WRITING SECURE CODE Second Edition Microsoft Press ...»

-- [ Страница 4 ] --

Windows NT и последующие ОС семейства поддерживают ACL* двух типов: из бирательную (Discretionary Access Control List, DACL) и системную (System Access Control List, SACL) таблицы управления доступом. Первая управляет доступом к защищенным ресурсам, а вторая — аудитом защищенных ресурсов.

Вот примеры ресурсов, доступ к которым управляется таблицами DACL a аудит — таблицами SACL:

• файлы и каталоги;

• общие файлы (например \\BlakesLaptop\BabyPictures);

• разделы реестра;

• общая память;

• объекты-задания;

• мьютексы (mutex);

• именованные каналы (named pipes);

• принтеры;

• семафоры;

• объекты каталога Active Directory.

Каждая DACL обычно содержит несколько записей управления доступом (Access Control Entry, ACEJ, хотя она может быть и пустой. DACL, равная NULL, означает, что к ресурсу не применяются никакие механизмы управления доступом. «Нуле вая* DACL — это очень плохо, и ее не следует применять, потому что злоумыш леннику ничего не стоит установить свою политику доступа к объекту. О пустых DACL мы поговорим попозже.

АСЕ состоит из двух основных компонентов: учетной записи, представленн.1 >й идентификатором безопасности (security ID, SID) и перечнем разрешенных :_-й действий над ресурсом. SID представляет учетную запись пользователя, группы и. ш компьютера. Наиболее известна — очень хочется сказать «печально известна* — АСЕ-запись с SID группы Everyone (Все) с разрешением Full Control (Полный до ступ). Everyone — это название группы, иногда ее называют World (все пользова тели), се идентификатор — S-1-1-0. Full Control разрешает делать с ресурсом в< е, что заблагорассудится. Поверьте мне, Full Control действительно означает «все, что угодно*! Следует заметить, что АСЕ может быть и запрещающей, то есть не разрешающей доступ конкретной учетной записи. Например, Everyone с запре щающим разрешением Full Control означает, что любой учетной записи — в тем числе и вашей! — доступ к ресурсу закрыт. Если взломщику удастся установить :*а ресурсе такую АСЕ, создаются прекрасные условия для успешной атаки отказе в обслуживании (DoS). так как ресурс становится абсолютно недоступным.

Не совсем верно. Списки управления доступом (ACL) к объектам Windows NT/2000/ ХР состоят из избирательной (DACL) и системной (SACL) таблиц управления досту пом. Первая используется для регулирования доступа к объекту, а вторая — для управления аудитом. — Прим, перев.

Методы безопасного кодирования 152 Часть II Как определить, поддерживает ли файловая система списки ACL Для этого надо асего лишь изменить переменную szV&l, чтобы она указыва ла на ТОМ:

f include Sinclude void nainO { ctiar *szVol = "c;

\V;

dwFlags * &;

if (SetVoluiselrifoniiatlonCszVol, SUiL, 0, HULL, NULL, &dw Flags, NULL, «)) { printf("TGn Xs Xs поддерживает списки ACl. ",.

szVol, (dwFlafifs & FS_PEfl3ISTNT_ACLS) ? " ;

"не");

} else { printf ("Ошибка Xd",GetLastError(}>;

Заметьте: вы вправе использовать сетевые адреса, например \\Btakes~ Lctptop\fiabyPictifres. Подробнее правила вызова функции GetVokemelnformatKm Описаны в комплекте ресурсов Platform SDK и в библиотеке MSDN.

Эту же задачу можно решить средствами VBScript (Microsoft Visual Basic Scripting Edition) или Microsoft jScript. В следующем отрывке на VBScript для выяснения, есть ли на томе файловая система NTFS, которая поддерживает списки АС1, используется объект FileSystemObject. Однако этот код не рабо тает, если файловая система отличается от NTFS — даже при условии, что она поддерживает ACL Впрочем, в сехчействе Windows только одна файло вая система «понимает» ACL — NTFS.

Dim fso, drv Di* vol: vol = "c:\" Set fso = CreateObjeetC'Scriptirvg.FileSystffleObjeGt") Set drv = fso.GetDrive(vol) Dim fsirtfo: fsiftfo ~ drv. FileSystem Di» aels ;

aels * False If StrComp(fsinfo, "NTFS", vbTextCompare) « 0 Then aels * Trye WScript.Echo{"9TO то* " & vol & " с файловой системой " & fsiRfo) Мзсг1р±.сИо("ПоддврЖйваются пи ACL? " & aels) Как работать с объектом FileSystemQbjgct, вы узнаете из документации к Windows Script Host.

ГЛАВА 6 Выбор механизма управления доступом Примечание Владелец объекта может в любой момент вернуть себе доступ к ресурсу, даже при наличии запрещающей ACL. У всех защищаемых объек тов Windows есть свой владелец. Например, создав файл, вы автомати чески становитесь его владельцем. Единственное исключение — объек ты, создаваемые администратором;

они попадают во владение группы Administrators (Администраторы).

Как выбрать оптимальный ACL Последние несколько месяцев, анализируя приложения на предмет безопаснос ти, я следую жесткому правилу: «Присутствие каждой АСЕ в ACL, должно быть оправданным*. По сути это означает, что если нельзя определить, почему АСЕ при сутствует в ACL, ее удаляют. Проект системы создается с применение методики высокоуровневого анализа и на основе собранных бизнес-требований, Такой же подход применяют к созданию списков ACL Я видел массу приложений с неряш ливо и в спешке созданными списками ACL;

результат — уязвимости защиты или неудовольствие пользователей.

Выяснить, соответствует ли ACL целям приложения, просто:

1. определите ресурсы, которые нужны приложению;

2. выясните, какие требования по управлению доступом к ресурсам предписы вают особенности бизнеса;

3. выберите подходящую технологию управления доступом;

4- реализуйте требования по управлению ресурсами в виде технологии управ. ie ния доступом.

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

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

Full Control» устанавливалось на некоторых критически важных файлах. Созда тели объясняли этот факт просто: «Локальным пользователям нужен доступ к этим файлам». Благодаря моей настойчивости мне удалось получить от одного из про граммистов следующий ответ: «Всем пользователям надо читать файлы данных, Администраторам нужно разрешить выполнять с файлами любые задачи. 1о пользователям из бухгалтерии доступ к файлам следует закрыть», Обратите внимание на выделенные курсивом слова. Те, кто знаком со сцена риями использования системы (use case) на языке объектного моделирования UML (Unified Modeling Language), заметят, что я выделяю ключевые элементы сценария, пытаясь определить бизнес-требования. На основе последних создаются техни ческие решения, в данном случае это требования по управлению доступом, на б;

зе которых и определяют списки управления доступом.

Часть И Методы безопасного кодирования Примечание Замечательное введение в UML вы найдете в книге Мартина Фа улера (Martin Fowler) и Кендалла Скотта (Kendall Scott) «UML Distilled: A Brief Guide to the Standard Object Modeling Language» (Сущность UML:

краткое руководство по стандартному языку объектного моделирования) (2nd Edition, Addison-Wesley Publishing Co, 1999).

Как вы помните, списки ACL состоят из записей АСЕ, а последние представля ют собой правила, составленные в следующей форме: «Субъекту разрешается вы полнять действие над объектом» или «Такой-то может выполнять такие-то опера ции с данным ресурсом». В нашем примере три АСЕ. «Все пользователи в компь ютере могут читать файлы с данными», — это правило, которое прекрасно транс формируется в первую АСЕ: Interactive: Read (Интерактивные: Чтение). Это клас сическое предложение «субъект — глагол — объект». Маска доступа — 32-разряд ное значение, в котором определены права, предоставляемые или запрещаемые записью АСЕ.

Примечание Группа Interactive представляет пользователей, которые локаль но вошли в систему компьютера и работают с ресурсом, расположен ным на этом компьютере (в отличие от пользователей, которые полу чают доступ к ресурсу через сеть). С технической точки зрения, эта группа состоит из S1D пользователей, вошедших в систему по вызову Logon-User с параметром dwLogonType, установленным в LOGON32_LOGON_L\'TER ACTIVE.

Интерактивные пользователи — то же, что и «Все локальные пользователи компьютера*. В эту группу также входят пользователи, обращающиеся к компью теру по протоколу FTP или HTTP и по умолчанию прошедшие аутентификацию по базовому методу на сервере IIS 5, Аналогичную процедуру следует выполнить со всеми субъектами (пользовате лями, группами и компьютерами), пока не «нарисуется» готовый ACL. В нашем примере получается ACL, показанный в табл. 6-1.

Таблица 6-1. Список управления доступом (ACL), полученный на основании бизнес-требований Субъект Разрешения Deny: Full Control (Запретить: Полный доступ) Accounting (бухгалтерия) Read (Чтение) Interactive Administrators (Администраторы) Full Control (Полный доступ) Full Control (Полный доступ) SYSTEM Внимание! При создании ACL программными методами запрещающие АСЕ надо обязательно размещать в начале списка. В ACL, созданных средствами пользовательского интерфейса Windows, такой порядок записей АСЕ обеспечивается автоматически, Если пренебречь этим правилом и раз местить запрещающие АСЕ после разрешающих, субъекты получат до ступ, который предполагалось для них закрыть.

Выбор механизма управления доступом Ь ГЛАВА Однажды я внес в систему управления разработкой ПО сообщение об оши(же защиты — наличие разрешения Everyone: Full Control в ACL создаваемого програм мой именованного канала. Программист закрыл ошибку как соответствующую проекту, заметив, что всем пользователям нужен доступ для чтения, записи и син хронизации именованного канала. Было забавным восстановить ошибку в состо янии <-к исправлению», заметив программисту, что в своем ответе он четко опре делил требуемое содержимое ACL и почему бы ему не реализовать сказанное в программе!

Примечание Качественные списки ACL жизненно важны, если программа ра ботает в среде с серверами терминалов (Terminal Server). Многим пользо вателям предоставляется доступ к большому количеству программных ресурсов, таких как именованные каналы и общая память, а неудачные списки ACL повышают риск компрометации системы в случае блокиро вания доступа к ресурсам злоумышленником.

Примечание Подробнее о неудачных ACL и серверах терминалов почитайте в опубликованном в январе 2001 г. компанией Microsoft бюллетене по безопасности «Weak Permissions on Winsock Mutcx Can Allow Service Failure* (Неудачные разрешения Winsock-мыотекса способны вызвать сбой службы) (MS01-003) (www.microsoft.com/technet/security).

Эффективные запрещающие АСЕ-записи Иногда в процессе определения политик безопасности оказывается, что неко': о рым пользователям доступ к ресурсу не нужен. В таком случае не стоит стеснять ся или бояться применить запрещающие АСЕ.

Определить правила управления доступом очень просто — они описаны в бизнес-требованиях. Следующий этап — выбор технологий для реализации управ ления доступом и настройки механизмов в соответствии с политиками управле ния доступом.

Создание ACL Оказывается, что многие разработчики просто не знают, какие функции надо использовать для создания ACI, в приложениях, поэтому я подробно расскажу, вак это делается. Я познакомлю вас с созданием ACL в Windows NT 4 и Windows 20i Ю.

а также с некоторыми новыми возможностями Visual Studio.NET и библиотеки ATL (Active Template Library).

Создание ACL в Windows NT Я прекрасно помню, как первый раз решил создать список ACL в программе на C++, это оказалось не так-то просто. Именно тогда я понял, почему разработчики так не любят программировать качественные ACL — это сложная задача, выпол нение которой связано с созданием большого по объему и чреватого многими ошибкам кода. В помощь вам я приведу примеры кода для Windows NT 4 и более Методы безопасного кодирования 156 Часть II поздних версий. (Код для предыдущих версий Windows NT намного сложнее и связан с вызовами malloc и AddAce\) Здесь показано, как создавать ACL и дескрип тор безопасности (security descriptor), а затем прикреплять его к новому каталогу, Имейте в виду: в каталоге обычно уже есть ACL. унаследованная от родительских каталогов. В этой программе новая ACL переопределяет все существовавшие до этого. Честно говоря, я всегда выбрасываю списки ACL, по умолчанию унаследо ванные от родительского контейнера, — «слабость* ACL -родителя может передаться потомку и свести на нет все мои усилия.

Л NT4ACL.CPP «include tfinclude tfinclude PSID pEveryoneSID = NULL, pAdminSID = NULL, pNetworkSID = NULL;

PACL pACL = NULL:

PSECURITY_DESCRIPTOR pSD = NULL;

// В ACL будут следующие АСЕ-эаписи:

// Network (Deny Access) // Everyone (Read) // Administrator (Full Control) try { const int NUH.ACES = 3;

EXPLICIT_ACCESS ea[NUH_ACES];

ZeroMemoryUea, NUM_ACES * sizeof (EXPLICIT.ACCESS)) ;

// Создаем общеизвестный SID для группы сетевых пользователей Network.

SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;

if (!AllocateAndInitializeSid(&SIDAuthNT, 1, SECURITY_NETWORK_RID, 0, 0, 0, 0, 0, 0, 0, SpNetworkSID) ) throw GetLastError();

ea[0].grfAccessPermissions = GENERIC_ALL;

ea[0].grfAccessMode = DENY.ACCESS;

ea[0]. grf Inheritances NO.INHERITANCE;

ea[0]. Trustee. TrusteeForra = TRUSTEE_IS_SID;

ea[0]. Trustee. TrusteeType = TRUSTEE J[S_WELL_KNQWN_GROUP;

ea[0]. Trustee. ptstrNante = (LPTSTR) pNetworkSID;

// Создаем общеизвестный SID для группы Everyone.

SID_IuENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;

if (!AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, Выбор механизма управления доступом ГЛАВА О, О, О, О, О, О, О, fcpEveryoneSID) ) throw GetLastErrorO;

ea[1].grfAccessPermissions = GENERIC_READ;

ea[1].grfAccessHode = SET_ACCESS;

ea[1].grfInheritances NO_INHERITANCE;

ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;

ea[1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;

ea[1].Trustee.ptstrName = (LPTSTR) pEveryoneSID;

// Создаем общеизвестный SID // для встроенной группы BUILTIN\Administrators.

if (!AllocateAndInitializeSid(iSIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, SpAdminSID) ) throw GetLastErrorO;

ea[2].grfAccessPermissions = GENERIC_ALL;

ea[2].grfAccfissHode = SET.ACCESS;

ea[2].grflnheritance= NO.INHERITANCE;

ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;

ea[2].Trustee.TrusteeType = TRUSTEE_IS_GROUP;

ea[2].Trustee.ptstrName = (LPTSTR) pAdminSID;

// Создаем новый ACL с готовыми АСЕ-записями, if (ERROR.SUCCESS != SetEntriesInAcl(NUM_ACES, ea, NULL, &pACL}) throw GetlastErrorf);

// Инициализируем дескриптор безопасности.

pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);

if (pSD == NULL) throw GetLastError();

if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) throw GetLastErrorO;

// Добавляем ACL в дескриптор безопасности, if (!SetSecurityOescriptorDacl(pSD, TRUE, // fDaclPresent flag pACL, FALSE)) { throw GetLastErrorO;

Часть II Методы безопасного кодирования } else { SECURITY_ATTRIBIJTES sa;

sa.nLength = sizeof{SECURITY_ATTRIBUTES);

sa.blnheritHandle = FALSE;

sa. IpSecurityOescriptor = pSD;

if (!CreateDirectory("C:\\Program Files\\MyStuff", &sa)) throw GetLastError();

} // Конец блока try.

catch(...) { // Условие ошибки.

if (pSD) LocalFree(pSD);

if (pACL) LocalFree(pACL);

// Вызываем FreeSID для каждого SID, созданного функцией AllocateAndlnitlalizeSID.

if (pEveryoneSID) FreeSid(pEveryoneSID);

if (pNetworkSID) FreeSid(pNetworkSID);

if (pAdminSID) FreeSid(pAdminSID);

Эта программа есть в папке с примерами Secureco2\Chapter06. Как видите, все очень непросто, а детали я сейчас объясню. Прежде всего надо понять, что к объекту никогда не прикрепляется «голый» ACL — его предварительно надо «одеть» в де скриптор безопасности (security descriptor). Последний инкапсулируется в струк туре SECURITY _ATTRIBUTES, где есть поле, информирующее, унаследован ли дескрип тор процессом. В дескрипторе безопасности указаны:

• владелец (представленный SID), определенный вызовом функции SetSecurity DescriptorOwner.

• основная группа (представлена SID), определенная вызовом SetSecurityDescriptor Group;

• DACL, определенная вызовом SetSecurityDescriptorDacl, • SACL. определенная вызовом SetSecurityDescriptorSacl.

Отсутствующие компоненты дескриптора безопасности заменяются значени ями по умолчанию. Например, владелец по умолчанию — учетная запись, в кон тексте которой исполняется процесс, создавший объект, или встроенная группа Administrators (Администраторы), если вызывающая программа работает от име ни члена этой группы. В нашем примере определяется только DACL, которая со стоит из одной или нескольких структур EXPLICIT _ACCESS. Каждая EXPUCIT_ACCESS представляет одну АСЕ и содержит идентификатор учетной записи (SID) и разре шения, предоставленные ей на объекте. В EXPLICIT _ACCESS также хранятся дру ГЛАВА 6 Выбор механизма управления доступом гие сведения, например, должна ли АСЕ наследоваться потомками. Процесс создания ACL иллюстрирует рис. 6-1.

Есть еще две API-функции, но они применяются для настройки ACL-файлов:

SetFileSecunty и SetNamedSecuritylnfo. Первая доступна во всех версиях Windows ЧТ, а вторая — только в Windows NT 4 и более поздних, Если приложение будет работать в Windows 2000 или более поздней ОС, зада ча значительно облегчается наличием языка определения дескрипторов безоп, ic ности (Security Descriptor Definition Language), о котором мы сейчас поговорим, _Применение к ресурсу SECURtTY^DESGRtPTQR ACL EXPUGITJICCESS EXPLICITJ\CCESS Рис. 6-1. Процесс создания ACL Создание ACL в Windows Поняв, что многие разработчики не понимают, как действуют функции обработ ки ACL и дескрипторов безопасности в Windows NT 4, в команде создателей W i n dows 2000 решили создать средство текстового представления ACL и дескрипто ров, получившее название Security Descriptor Definition Language (SDDL — яз.лк описания дескрипторов безопасности). В SDDL идентификаторы безопасности (SID) и записи управления доступом (АСЕ) представляются понятным человс ку текстом.

Примечание Полное описание SDDL вы найдете в файле Sddl.h из набора ре сурсов Microsoft Platform SDK.

В следующем примере создается каталог C:\MyDir и ему назначаются следую щие АСЕ-записи:

• Guests (Deny: Full Control) [Гости (Запретить: Полный доступ)];

• SYSTEM (Full Control);

• Administrators (Full Control);

• Interactive (Read, Write, Execute) [Интерактивные (Чтение, Запись, Исполнение)].

Часть II Методы безопасного кодирования Л SDDLACL.cpp */ «define _WIN32_WINNT 0x ((include uinclude void main() { SECURITY.ATTRIBUTES sa;

sa.nLength = sizeof(SECURITY_ATTRIBUTES};

sa.blnheritHandle = FALSE;

char *szSD = "D:P" // DACL "(D;

OICI;

SA;

;

BG)" // Запретить доступ группе Guests '4A;

OICI;

GA;

;

S Y ) " // Разрешить полный доступ учетной записи SYSTEM "(A;

OICI;

GA;

;

ВА)" // Разрешить полный доступ группе Admins "(A;

OICI;

GR6WGX;

;

;

IU)";

// Разрешить доступ на чтение, // запись и исполнение группе Interactive if (ConvertStringSecurityDescriptorToSecurityDescriptor( szSD, SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL)) { if

I LocalFree(sa.lpSecurityDescriptor);

Эта программа (ее текст хранится в папке с примерами Secureco2\Chapter06.) значительно короче и понятнее, чем пример для Windows NT 4. Тем не менее SDDL строка в переменной szSD нуждается в отдельном комментарии. Эта переменная содержит SDDL-представление ACL (табл. 6-2).

Таблица 6-2. Структура SDDL-строки SDDL-элемент Примечание _ D означает, что это DACL Другой вариант — 5: так отмечаются D:P АСЕ аудита (в таблице SACL). За этим компонентом следуют АСЕ записи. Параметр Р устанавливает флаг SE_DACL_PROTECTED, что дает максимум контроля над АСЕ за счет запрещения наследова ния от родительских контейнеров. Если предотвращать наследо вание не надо, этот параметр опускается Строка АСЕ. Определение каждой АСЕ отделяется круглыми скоб (D;

OICI;

GA;

:;

BG) ками.

D — запрещающая АСЕ.

ГЛАВА 6 Выбор механизма управления доступом Таблица 6-2. (окончание} SDDL-элемент Примечание OICI — включить наследование потомками. Иначе говоря, эта АСЕ автоматически будет присоединяться к объектам (например файлам) и контейнерам (например каталогам), расположенные ниже в иерархии.

GA — Generic All Access, то есть полный доступ.

BG — встроенная группа Guests (Builtin Guests), Эта АСЕ закрывает всем гостям доступ к данному объекту и рас • положенным ниже в иерархии.

Два отсутствующих значения представляют соответственно ObjectTypeGuid и InherttedObjectTypeGuid. Они не используются в этом примере, так как применяются только к АСЕ объектов (в противовес контейнерам). «Объектные» АСЕ позволяют более тонко управлять доступом к объектам-потомкам A — разрешающая АСЕ.

(A;

OICI;

GA;

:;

SY) SY— учетная запись SYSTEM (локальная система) BA — встроенная группа Administrators (Builtin Administrators) (A;

O1C1;

GA;

;

;

BA) GR — чтение, GW — запись, GX — исполнение.

(A;

OICI;

GRGWGX;

;

;

IU) IU — группа Interactive (пользователи, вошедшие в систему ком пьютера) На рис. 6-2 показана структура SDDL-строки.

DACL Запрет или разрешение Участник безопасности Л Л Л 0:(D;

OICI;

GA;

;

;

BG)(A;

OICI;

GA;

;

;

SY) (A;

OICI;

GA;

;

;

ВА}(A;

OICI;

GRGWGX;

;

;

IU) 1 ' V V АСЕ Флаги наследования Маска АСЕ Рис. 6-2. Структура SDDL-строки Вам придется использовать и другие обычные и встроенные учетные записи, поэтому в табл. 6-3 мы перечислили наиболее известные SID в Windows 2000 и в более поздних ОС.

Таблица 6-3. Типы SID в SDDL SDDL-элемент Учетная запись Account Operators (Операторы учета) АО Authenticated Users (Прошедшие проверку) AU Builtin Administrators — встроенная группа Administrators (Администра BA торы) Builtin Guests — встроенная группа Guests (Гости) BG Backup Operators (Операторы архива) ВО Builtin Users — встроенная группа Users (Пользователи) BU Certificate Server Administrators — группа Administrators на сервере сер • СА тификации Creator Owner (Создатель-владелец) СО Domain. Administrators (Администраторы домена) DA см. след.

Часть II Методы безопасного кодирования (окончание} Таблица 6-3.

SDDL-элемент Учетная запись Domain Guests (Гости домена) DG Domain Users (Пользователи домена) DU Interactive (Интерактивные) IU Local Administrator (Локальный администратор) LA Local Guest (Локальные гости) LG Network (Сеть) NU Print Operators (Операторы печати) PO Power Users (Опытные пользователи) PU Restricted Code — ограниченный маркер, созданный вызовом функции RC CreateRestriciedTokeri в Windows 2000 и более поздних ОС Server Operators (Операторы сервера) SO Service Logon User — любая учетная запись, в контексте которой рабо SU тает служба Local System (Локальная система) SY World (то же, что и Even-one) WD Network Service (в Windows XP и более поздних версиях) NS Local Service (в Windows XP и более поздних версиях) IS Anonymous Logon (Анонимный вход) (в Windows XP и более поздних AN версиях) Remote Desktop Users (Пользователи удаленного рабочего стола) RD и Terminal Server Users (Пользователи сервера терминалов) (Windows XP и более поздние версии) Network Configuration Operators (Операторы настройки сети) NO (в Windows XP и более поздних версиях) Logging Users (в Windows.NET Server и более поздних версиях) Ш Monitoring Users (в Windows.NET Server и более поздних версиях) MU Преимущество языка SDDL в том, что SDDL-текст можно сохранять в конфи гурационных или XML-файлах. В частности, SDDL применяется в INF-файлах ре дактора конфигурации безопасности (Security Configuration Editor) для представ ления списков ACL системного реестра и NTFS.

Примечание В процессе кампании по безопасности Windows (Windows Security Push) было решено ограничить доступ к счетчикам производительнос ти, для чего в Windows XP создали группы Logging Users и Monitoring Users.

Создание ACL средствами Active Template Library ATL (Active Template Library) — это набор шаблонных классов C++, поставляемых в составе Visual Studio 6 и Visual Studio.NET. В последнюю добавили много свя занных с защитой ATL-классов, которые значительно облегчают выполнение стан дартных задач по управлению защитой Windows, в том числе ACL и дескриптора ми безопасности. В следующем примере (он разработан средствами Visual Studio.NET) создается каталог с ACL такого состава:

ГЛАВА 6 Выбор механизма управления доступом • Blake (Read);

• Administrators (Full Control);

• Guests (Deny: Access).

/* ATLACL.cpp */ ftinclude ttinclude using namespace std;

void main(){ try { // Пользовательские учетные записи.

CSid sidBlake("Northwindtraders\\blake");

CSid sidAdmin = Sids: :Admins();

CSid sidGuests = Sids: :Guests();

// Создаем ACL и заполняем его АСЕ-записями, // Заметьте: запрещающие АСЕ размещаются перед разрешающими.

CDacl dad;

dacl.AddDeniedAceC sidGuests, GENERIC_ALL);

dacl.AddAllowedAce(sidBlake, GENERIC_READ);

dacl.AddAllowedAce(sidAdmin, GENERICJM-L);

// Создаем дескриптор безопасности и атрибуты, CSecurityDesc sd;

sd.SetDacl(dacl);

CSecurityAttributes sa(sd);

// Создаем каталог с заданными атрибутами безопасности.

if (CreateDirectory("c:\\HyTestDir", &sa)) cout « "Каталог создан!" « endl;

} catcn(CAtlException e) { cerr « "Ошибка, приложение завершилось с ошибкой ".

« hex « (HRESULT)e « endl;

Примечание Обратите внимание на строки Sids::Admins() и Sids::Guests(), При работе с общеизвестным SID вместо «обычных» названий, таких как Administrators и Guests, подобные строки обязательны, так как в других версиях Windows (отличных, как в нашем случае, от англоязычной) груп пы называются по-другому. Полный список всех общеизвестных SID есть в пространстве имен C++, в файле atlsecurity.il.

Часть II Методы безопасного кодирования По моему мнению, этот код значительно понятнее, чем показанные ранее программы для Windows NT 4 и Windows 2000. Он проще, чем в Windows NT 4, так как не перегружен деталями, и понятнее, чем код для Windows 2000, так как SDDL текст слишком уж специфичен. Этот пример также есть в папке Secureco2\Cbapter06.

А теперь, после рассказа о качественных списках ACL и методах их создания, пора познакомить вас со стандартными ошибками программистов при создании ACL.

Как правильно упорядочить АСЕ-записи Я уже говорил о правильном порядке следования АСЕ в ACL Стандартные сред ства интерфейса Windows автоматически корректно упорядочивают АСЕ-записи.

Однако при создании программы эти средства недоступны, поэтому о правиль ном порядке придется позаботиться самостоятельно. Это особенно важно, когда программа считывает ACL ресурса, например раздела реестра, добавляет АСЕ, а затем модифицирует информацию в реестре. Вот правильная последовательность АСЕ в ACL:

• записи, явно запрещающие доступ (Explicit Deny);

• записи, явно разрешающие доступ (Explicit Allow);

• запрещения, унаследованные от непосредственного родителя;

• разрешения, унаследованные от непосредственного родителя:

• запрещения, унаследованные от «дедушки»;

• разрешения, унаследованные от «дедушки»;

• запрещения, унаследованные от «прадедушки»;

• разрешения, унаследованные от «прадедушки» и т. д.

Чтобы правильно добавить в ACL новую АСЕ, соблюдайте такую последователь ность, 1. Вызовите функцию GetSecuritylnfo или GetNamedSecuritylnfo, чтобы извлечь информацию ACL из дескриптора безопасности объекта.

2. Для каждой новой АСЕ создайте и заполните отдельную структуру ЕХРЫСП"_АССЕ55.

3. Вызовите SetEntriesInAcl, передав существующий ACL и массив структур EXPLI CIT_ACCESS, соответствующих новым АСЕ-записям.

4. Вызовите функцию SetSecuntylnfo или SetNamedSecuritylnfo, чтобы присоединить новый ACL в дескриптор безопасности.

Вот программа на C++, иллюстрирующая описанный процесс. Обратите вни мание, что здесь применяется новая функция, CreateWellKnownSid (она есть в Windows 2000 SP3, Windows XP и Windows.NET Sewer), которая делает практически то же, что и ATL-класс CSid.

I* SetUpdatedACL.cpp */ «define _WIN32_WINNT 0x ((include "windows, h" «include "aclapi.h" «include ГЛАВА 6 Выбор механизма управления доступом int main(int argc, char- argv[]) { char *szName = "c:\\junk\\data.txt";

PACL pDacl = NULL;

PACL pNewDacl = NULL;

PSECURITY.DESCRIPTOR sd = NULL;

PSID sidAuthUsers = NULL;

DWORD dwErr = 0;

try { dwErr = GetNamedSecurityInfo(szName, SE_FILE_OBJECT, DACL_SECURITYJNFORMATION, NULL, NULL, ipDacl, MULL, &sd);

if (dwErr != ERROR_SUCCESS) throw dwErr;

EXPLICIT^ACCESS ea;

ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));

DWORD cbSid = SECURITY_MAX_SID_SIZE;

sidAuthUsers = LocalAlloc{LMEM_FIXED,cbSid>;

if (SidAuthUsers == NULL) throw ERROR_NOT_ENOUGH_HEMORY;

if (!CreateWellKnownSid{WinAuthenticatedUserSid, NULL, sidAuthUsers, &cbSid)) throw GetLastErrorO;

BuildTrusteeWithSid(&ea.Trustee, sidAuthUsers);

ea.grfAccessPermissions = GENERIC_READ;

ea.grfAccessMode = SET_ACCESS;

ea.grflnheritance = NO_INHERITANCE;

ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;

ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;

dwErr = SetEntriesInAcl(1,iea,pDacl,ipNewDacl);

if (dwErr != ERROR.SUCCESS) throw dwErr;

dwErr = SetNamedSecurityInfo(szName, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, Часть II Методы безопасного кодирования NULL, pNewDacl, NULL);

} catch

if (sd) LocalFree(sd);

if (pNewOacl) LocalFree(pNewDacl);

return dwErr;

' Функции AddAccessAllowedAceEx и AddAccessAllowedObjectAce добавляют АСЕ в конец ACL. А о правильности следования записей в ACL придется позаботиться про граммисту.

Наконец, будьте осторожны cAddAccessAllowedACE, так как эта функция не под держивает управление наследованием ACL. В этом случает надо задействовать AddAccessAllowedACEEx.

Безопасность при использовании SID сервера терминалов и удаленного рабочего стола Б Windows есть стандартные SID пользователей сервера терминалов (Terminal Server) и удаленного рабочего стола (Remote Desktop), которые присутствуют в маркере пользователя, если тот вошел в систему через сервер терминалов (Win dows 2000 Server) или удаленный рабочий стол (Windows XP и более поздние вер сии). Поскольку SID размещается в маркере, вы вправе применить его для управ ления доступом к ресурсам, создав специальный ACL, например:

• Administrators (Full Control);

• Remote Desktop Users (Read) [Пользователи удаленного рабочего стола (Чте ние)];

• Interactive Users (Read, Write), Знайте: маркер не всегда содержит SID группы Remote Desktop Users, если пользователь ранее вошел в систему в интерактивном режиме. Объясню эту мысль на примере:

• находясь на работе, пользователь Madison входит в систему своего офисного компьютера и выполняет обычные задачи. Маркер Madison содержит SID группы Interactive, так как вход в систему выполнялся с локальной консоли;

• вечером Madison блокирует компьютер и оправляется домой:

ч Выбор механизма управления доступом ГЛАВА • дома он решает подключиться к офисному компьютеру средствами удален! го го рабочего стола Windows XP по VPN-каналу;

• в процессе подключения система рабочего компьютера создает для Madison новый маркер с SID группы Remote Desktop Users. Но затем служба обнаружи вает, что этот пользователь уже вошел в систему и его сеанс не закрыт, поэто му, чтобы сохранить состояние рабочего стола в неизменном виде, серьер терминалов отбрасывает новый маркер и подключается к открытому интер активному сеансу.

Теперь, с точки зрения ОС, Madison — интерактивный пользователь, и в этом качестве он получает доступ не только на чтение, но и на запись. В этом нет ни чего плохого: ему все равно предоставляется доступ для чтения и записи при ра боте с консоли, Кроме того, при «чистом» удаленном доступе Madison никак не сможет инициировать интерактивный сеанс, Конечно, циники наверняка заметят, что Madison скорее всего администратор на собственном компьютере, и все эти камлания с бубнами вокруг других SID в маркере совершенно излишни!

Вы спросите, к чему я это все рассказал? Просто не забывайте о такой возмож ности, создавая ACL.

Нулевая DACL и другие опасные типы АСЕ Нулевая DACL (NULL DACL) — один из способов предоставить полный доступ к объекту всем пользователям без разбора, в том числе и взломщикам. Я часто го ворю, что «NULL DACL - полное отсутствие защиты*. И это абсолютно верно. Ее 'ш вам совершенно все равно, что пользователи будут делать с вашим объектом — читать, писать, удалять, изменять или же закрывать доступ к объекту другим, тог да — в путь, пользуйтесь нулевой DACL, но только на свой страх и риск. Однакс я не могу себе представить продукт, в котором удастся извлечь выгоду из подобно го поведения, а это означает, что нулевая DACL совершенно бесполезна!

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

if (SetSecurityDescriptorDacH&sd, // В дескрипторе есть D A C L...

TRUE, NULL, //... н о она пуста!

FALSE)) { // Дескриптор безопасности с нулевой DACL.

} Еще одна разновидность этой же ошибки — явное заполнение «нулями* струк туры SECURITY_DESCR1PTOR. Этот код также создаст нулевую DACL:

SECURITY_DESCRIPTOR sd = { S ECURITY_0 ESCRIPTOR_REVISION, 0x0, SE_DACL_PRESENT, 0x0, 0x0, 0x0, 0x0};

// DACL пуста, т.е. нулевая.

7- Методы безопасного кодирования 168 Часть II Примечание Отладочная версия приложения будет информировать о наличии нулевых DACL, если создать ее средствами библиотеки ATL из состава Visual Studio.NET.

Работая над Windows XP, мы с членами команд Secure Windows Initiative Team и Windows Security Penetration Team потратили немало времени на поиск пустых DACL, уведомление авторов программ и борьбу за устранение подобных брешей.

Впоследствии мы проанализировали, почему же программисты создают объекты с нулевыми DACL, и обнаружили две причины:

• программисты «тонут» в огромном объеме кода, необходимого для создания списков ACL. Хочется надеяться, что вы освоили по крайней мере один из при веденных ранее трех примеров и сможете запрограммировать нужные ACL;

• программисты считают нулевые DACL вполне удовлетворительными, потому что код прекрасно работает с оснащенными такими DACL объектами. Теперь то вы понимаете, что так делать нельзя — если программа служит прекрасную службу пользователям, то очень вероятно, что таким же чудесным образом она открывает зеленый свет хакеру!

Честно говоря, в основе обеих причин — лень или недостаток опыта и про фессионализма. Верно, над качественной ACL придется потрудиться, но овчинка стоит выделки. Если продукт падет жертвой хакера из-за плохо реализованного ACL, вам все равно придется выпускать «заплату». Так почему бы не уничтожить брешь в зародыше.

Примечание Нулевая DACL и нулевой дескриптор безопасности — это «две большие разницы». Если при создании объекта установить дескриптор в NULL, ОС создаст дескриптор безопасности по умолчанию с DACL, обыч но это DACL, •унаследованная от родительского объекта.

Как-то я написал простую Perl-утилиту для поиска нулевых DACL в исходном коде на C++ и С и «пропахал» ею исходные тексты, полученные от одного из парт неров Microsoft. Я нашел около дюжины нулевых DACL, зарегистрировал эти ошиб ки в системе разработки, а после исправления снова пробежался по коду утили той — нулевые DACL пропали. Почти тремя месяцами позже, анализируя исход ный текст программы на предмет безопасности, я обнаружил странные исправ ления там, где раньше были нулевые DACL. До исправления код выглядел так:

SetSecurityDescriptorDaclC&sd, TRUE, NULL, // DACL FALSE);

а после (утилита не увидела здесь подвоха) — вот так:

SetSecurityDe$criptorDacl(&sd, TRUE, ::malloc(OxFFFFFFFF), // DACL FALSE);

ГЛАВА 6 Выбор механизма управления доступом Этот трюк глуп, но и остроумен одновременно. Если функция mattoc не в со стоянии выделить требуемый блок памяти, она возвращает NULL. Программист пытается выделить OxFFFFFFFF, или 4 294 967 295 байт, данных, что на больше н стве машин приведет к ошибке, и DACL установится в NULD. Пришлось серьезно поговорить с программистом, «исправившим» ошибку, и, конечно же, повторно «открыть» все ошибки в системе управления разработкой. Я не успокоился, пека все дыры не залатали самым тщательным образом.

Нулевая DACL и аудит У пулевых DACL есть еще одна коварная особенность: если пользователь (даже «легальный») изменит ее на Everyone (Deny: Access), то очень высока вероятность того, что в журнал событий Windows эта операция не попадет и это злонамерен ное действие останется незамеченным. Причина в том, что почти наверняка аудит объекта с такой DACL отключен — ведь SACL также пуста!

Внимание! Нулевая DACL представляет прямую опасность. Обнаружив ее, не медленно проинформируйте об ошибке и добейтесь, чтобы ее устранили.

Опасные типы АСЕ Следует опасаться АСЕ-записей трех типов: Everyone (WRITE_DAC), Everyone (Wltf TE_OWNER) и ACL, разрешающих добавлять в каталог исполняемые программь.

Everyone (WRITE_DAC) WRITE_DAC — право изменять DACL в дескрипторе безопасности объекта. Полу чив возможность изменять ACL, злонамеренный пользователь может назначить ce'ie ничем не ограниченный доступ к объекту и закрыть его для остальных, Everyone (WRITE_OWNER) WRITE_OWNER — право изменять владельца в дескрипторе безопасности объек та. По определению, владелец объекта может делать с ним все, что утодно. Недо бросовестный пользователь, присвоивший владение объектом, получает безгра ничный доступ и возможность закрыть объект для доступа других пользователе и.

Everyone (HLE_ADD_FILE) АСЕ с Everyone (FILE_ADDJ?ILE} особенно опасна, потому что позволяет ненадеж ным пользователям добавлять в файловую систему свои исполняемые програм мы. Опасность в том, что атакующий может разместить опасный файл в нужном каталоге и дождаться, когда администратор запустит программу на исполненре.

Короче говоря, никогда не разрешайте пользователям, которым не особо доверн ете, писать файлы в общие каталоги приложения.

Everyone (DELETE) АСЕ с таким разрешением позволяет удалить объект вашего приложения любому, а этого разрешать нельзя, особенно пользователям, которым вы не особенно до веряете, Методы безопасного кодирования 170 Часть II Everyone (FILE_DELETE_CHILD) Это разрешение отображается в пользовательском интерфейсе Windows как Delete subfolclers and files (Удаление подпапок и файлов) и позволяет пользователю уда лять дочерний объект, например файл, даже если у него нет к нему доступа. Об ладая разрешением FILE_DELETE_CHILD на родительском объекте, вы вправе уда лить любой дочерний объект независимо от разрешений последнего.

Everyone (GENERIC_ALL) Разрешение GENER1C_ALL} или Full Control (Полный доступ), столь же опасно, как и NULL DACL. Лучше его не применять.

Что делать, если нельзя изменить нулевую DACL Мне сложно представить причину, по которой надо создавать объект с нулевой DACL, кроме случая, когда совершенно неважно, скомпрометирован ли объект.

Подобная ситуация мне встретилась лишь однажды: в приложении время от вре мени открывалось диалоговое окно с анекдотом. Для «защиты* использовался мьютекс с пустой DACL, который предотвращал одновременное отображение окна несколькими копиями программы. Даже если взломщик установит на этом объекте запрещающую АСЕ, невелика потеря — пользователь лишиться очередного анек дота, только и всего!

Как обязательный минимум следует создавать ACL, который не позволяет всем пользователям:

• перезаписывать DACL объекта [Even-one (WRITE_DAC)];

• изменять владельца объекта [Everyone (WRJTE_OWNEK)]\ • удалять объект [Even-one (DELETE)], Маска доступа зависит от вида объекта, например, для раздела реестра нужна такая маска:

DWORD dwFlags = KEY_ALL_ACCESS & "WRITE_DAC & -WRITE_OWNER & -DELETE;

а для файла или каталога другая:

DWORD dwFlags = FILE_ALL_ACCESS & ~WRITE_DAC & -WRITE_OWNER & 'DELETE & ~FILE_DELETE_CHILD Другие механизмы управления доступом Списки ACL — полезный, но далеко не единственный метод защиты ресурсов.

Наиболее популярны роли.NET Framework или СОМ+, IP-ограничения, а также триггеры и разрешения SQL Server. От списков их отличает то, что они привяза ны к определенным приложениям, а АС1. — неотъемлемый компонент ОС, ГЛАВА 6 Выбор механизма управления доступом Роли часто применяются в финансовых или бизнес-приложениях для управ ления приложением на основе политик, например для ограничения размера тран закции в зависимости от роли пользователя, ее выполняющего. Верхний предел размера транзакции у рядовых сотрудников невысок, у менеджеров — чуть боль ше, а самый высокий (или вообще неограниченный) — у вице-президентов. Так же часто применяют основанную на ролях систему безопасности, например, ког да для выполнения операции требуется получить «визу» у многих субъектов. По добная ситуация возможна в системе закупок: любой сотрудник вправе ввести запрос на закупку, но только ответственному менеджеру разрешается преобразо вывать запрос в реальный заказ, отправляемый поставщику.

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

В Windows-программах реализованы два механизма создания ролей: в.NET Framework и СОМ+. Сейчас я познакомлю вас с ними поближе, Роли в.NET Framework Основанный на ролях механизм безопасности в.NET Framework поддерживает авторизацию, предоставляя потоку программы информацию об участнике безо пасности (principal). Идентификационная информация (и определяемый ею уча стник безопасности) может быть учетной записью Windows или пользователя, Приложения.NET Framework принимают решения об авторизации на основе идентификационных данных участника безопасности и/или его роли. Роль — это поименованная совокупность участников безопасности, которые обладают оди наковыми привилегиями с точки зрения системы безопасности (например р >ли ^операционист» или «менеджер»). Участник безопасности может обладать одной или несколькими ролями. Таким образом, в приложениях роли позволяют бь-ст ро выяснять права пользователя на выполнение тех или иных операций, Примечание Полное описание ролей.NET Framework выходит за рамки кни ги. За более подробной информацией рекомендую обратиться к книгам следующих авторов (см. библиографический список): Lippert или LaMac • chia, Lange и др, Для простоты и совместимости с механизмами управления доступом к коду основанная на ролях защита в.NET Framework реализована в виде объектов Ptin cipalPermission, в которых CLR-среда (Common Language Runtime) выполняет ав торизацию по такой же схеме, что и проверка безопасности доступа к коду. Класс PrincipalPermission представляет личность или роль пользователя, которая до тж на проходить как декларативные, так и обязательные проверки системой безопас ности. Бы также вправе проверять идентификационную информацию напрямую и, если требуется, выполнять проверку роли и личности прямо из программы, Вот отрывок программы, демонстрирующий применение ролей.NET Framework в Web-сервисе или на Web-странице:

WindowsPrincipal wp = {HttpContext.Current.User as WindowsPrincipal);

if ( wp.lslnRole("Hanagers")) { 172 Часть II Методы безопасного кодирования // Пользователь проходит авторизацию для выполнения функций менеджера.

Аналогичную операцию разрешается выполнять с текущим потоком:

WindowsPrincipal principal = (Thread.CurrentPrincipal as WindowsPrincipal);

if (principal.IsInRoleC'Administrator")) { // Пользователь - администратор, I Заметьте: WindowsPrincipaLIsInRole позволяет выяснить, является ли вызываю щая программа членом Windows-группы, a GenericPrincipallsInRole — определить, разрешена ли программе универсальная роль, причем сведения о составе роли обычно хранятся в специальной базе данных или конфигурационном файле.

Конструктор GenericPrincipal позволяет определить, какие роли выделены участ нику' безопасности. Вот пример на С#, демонстрирующий это:

Genericldentity id = new GenericIdentityC'Blake");

// Список ролей ножет извлекаться из XML-файла или базы данных String[] roles = {"Manager", "Teller"};

GenericPrincipal principal = new GenericPrincipal(id, roles);

Роли в СОМ+ В COM+ роли напоминают группы Windows, однако создает роли и определяет их состав не сетевой администратор. Автор приложения создает роли в процессе разработки, а «населяет» их пользователями администратор приложения при раз вертывании. Таким образом, удается добиться большей гибкости, потому что се тевые группы и роли в приложении хотя и связаны, но, тем не менее, независи мы, что предоставляет администратору огромное пространство для творчества, Роли СОМ+ создаются на прикладном уровне средствами оснастки Component Services (Службы компонентов) или программно методом IsCallerlnRole. Как его вызывать, показано в примере на Visual Basic:

' Получаем контекст вызова подсистемы безопасности.

Dim fAllowed As Boolean Dim objCallCtx As SecurityCallContext Set objCallCtx = GetSecurityCallContextO Выполняем проверку роли.

fAllowed = objCallCtx.IsCallerlnRoleC'Doctor") If (fAllowed) Then ' Действуем в соответствии с полученным результатом.

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

Выбор механизма управления доступом ГЛАВА б flsDoctor = obJCallCtx.IsCallerInRole("Doctor") flsOnDuty = IsCurrentlyOnDuty(szPersonlD) If (flsDoctor And flsOnDuty) Then Выполнение задач, для которых нужен не просто врач, ' а дежурный доктор на посту.

End If Комбинирование бизнес-логики и основанных на ролях разрешений — мощ ная и исключительно полезная возможность.

IP-ограничения Они поддерживаются большинством Web-серверов, в том числе IIS. Разработчи ки или администраторы применяют их для ограничения доступа к отдельным частям Web-сайта, отдельным IP-адресам (например, 192.168.19-23). подсетям (192.168.19.0/24), DNS-именам (www.microsoft.com) и доменным именам (*.mio"Q soft.com). При создании Web-приложений мы рекомендуем не забывать о возмож ности использования IP-ограничений, например для определения круга админи страторов путем ограничения перечня IP-адресов машин, с которых разрешается выполнять администрирование, Если в процессе анализа бизнес-требований и прав доступа вы обнаружите формулировку типа «доступно только с локальной машины» или «запретить до ступ всем пользователям и компьютерам из домена accounting.northwindtraders.com*, мой вам совет: воспользуйтесь IP-ограничениями.

IP-ограничения иногда оказываются как нельзя кстати, если нужно включить какую-то функцию приложения по умолчанию и вместе с тем не позволить взлом щику воспользоваться ею. Задача решается просто: достаточно установить 1Р-ог] >а ничение на специально созданный виртуальный каталог, разрешив исполнение кода только на локальной машине (127.0.0.1).

Внимание! Если надо разрешить по умолчанию опасную Web-функцию, опре деляйте IP-ограничение, разрешающее исполнять код только по IP-ад ресу 127.0.0.1.

В следующем примере на VBScript показано, как установить IP-ограничения на виртуальном каталоге Samples Web-сервера по умолчанию так, чтобы тот оказал ся доступным только с localhost (то есть с зарезервированного адреса 127.0.0.1), Определяем параметры протокола IP.

Dim oVOir Dim oIP Set oVDir = GetObject("IIS://localhost/W3SVC/1/Samples") Set oIP = oVDir.IPSecurity ' Определяем список разрешенных IP-адресов с одним элементом - 127.0.0.1.

Dim IPList(l) IPListd) = "127.0.0.1" oIP.IPGrant = IPList ' Запретить доступ по умолчанию, oIP.GrantByDefault = False 174 Часть II Методы безопасного кодирования ' Сохраняем изменения на IIS ' и обнуляем переменную, oVDir.IPSecurity = oIP oVDir.Setlnfo Set oIP = Nothing Set oVDir = Nothing Триггеры и разрешения сервера SQL Server Триггеры SQL Server позволяют разработчику размещать правила произвольной сложности в таблицах базы данных. Ядро базы данных автоматически вызывает триггеры при добавлении, удалении или изменении данных в таблицах. Имейте в виду: триггеры не работают при чтении данных. Это не очень хорошо, так как хотелось бы предусмотреть в приложении определенную логику управления до ступом на основе триггеров, то есть что-то типа разрешений. Но увы, триггеры не реагируют на чтение.

Разрешения SQL Server — это аналоги списков ACL в Windows, а механизм их действия можно выразить простой фразой: «субъекту разрешается (или запреща ется) выполнять определенные операции над объектом*. Вот примеры: «Пользо вателю Blake разрешается читать данные из таблицы Accounts (счета)*, ^Аудито рам разрешается читать, писать и удалять данные из таблицы AuditLog (журнал аудита)*. Все объекты в SQL Server можно защитить с помощью разрешений.

Пример приложения для поликлиники Сейчас я покажу вам, как применять отличные от ACL методы управления досту пом. Это упрощенный вариант реального ПО для поликлиники. При сборе тре бований выяснилось, что действует следующий порядок обновления докторами медицинской карты пациента.

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

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

В рамках принятых допущений «-поиск» означает то же, что и «чтение*.

Таким образом, мы готовы определить следующую политику доступа к инфор мации о пациентах:

• группа «Доктора»: право на чтение и изменение.

Следующая политика определяется на основе правил обращения с медицинс кими данными пациентов:

ГЛАВА 6 Выбор механизма управления доступом • группа «Доктора»: право на чтение и изменение;

• группа «Старшие медсестры»: право на чтение и изменение;

• группа «Медсестры»: право на чтение.

И последняя политика вытекает из правил работы с журналом аудита:

• группа «Доктора»: запрещено чтение и изменение:

• группа «Аудиторы*: полный доступ;

• группа «Все»: право на запись.

В этом примере для деления по группам докторов, старших и рядовых медсе стер можно воспользоваться группами Windows или SQL Server, а также ролями СОМ+. (Заметьте: при изменении условий разрешения могут также претерпеть изменения.) Здесь важно понять: защиту ресурсов не обязательно реализовыват ь с помощью ACL. Хороший пример — данные, хранимые на SQL Server;

в этом случае вся информация о пациентах размещается в базе данных, как и журнал аудита.

Преимущество способа на основе сценариев использования системы заклю чается в независимости политики управления доступом от реализации. Например, политику можно реализовать одними триггерами таблиц сервера SQL Server. Бот пример триггера, срабатывающего при попытке изменить или удалить данные журнала аудита. Если пользователь не входит в группу «Аудиторы», транзакция откатывается, create trigger checkaudit on tblAuditLog for update, delete as begin if not is_member('Northwindtraders\Auditors') rollback tran id t Обратите внимание, что триггеры не вызываются при записи данных в ж/р нал аудита, так как в соответствии с бизнес-правилами запись разрешается всем.

Однако такое решение не лишено недостатков: читать информацию журнала аудита могут все, так как триггеры не реагируют на чтение. В этом случае логично при менить к таблице разрешение, например: «обычным пользователям (public) раз решается только запись в журнал». «Обычные пользователи» — это то же, что и группа Everyone в Windows. Журналы аудита очень важны, поэтому два уровня за щиты здесь совсем нелишни. Помните: «защита на всех уровнях»! В данном с ;

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

Важное замечание по поводу механизмов управления доступом Не встроенные в операционную систему механизмы управления доступом опас ны тем, что иногда приводят к образованию уязвимых мест. Сейчас поясню по чему. На рис. 6-3 показана схема системы защиты ресурсов с применением IP ограничений Web-сервера.

Часть II Методы безопасного кодирования 1 Взломщик IP-ограничения Служба доступа к файлам и печати Web-сервер Т Ресурсы Рис. 6-3. Защита ресурсов с помощью IP-ограничений Проблема кроется в том. что в системе работает служба доступа к файлам. Если взломщику удастся получить доступ к ней, он сможет обойти IP-ограничения, так как служба ничего «не ведает* о них.

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

Вот наглядный пример. Когда я работал в группе создателей IIS. в одной из групп (не нашей!) организовали частный Web-сайт для закрытого (только для ее чле нов) просмотра фильма «Звездные войны: Эпизод I — Призрачная угроза». Мы посчитали себя обделенными и решили «пригласить* себя на киносеанс самосто ятельно! Ранее, анализируя Web-сервер, мы обнаружили, что логика определения принадлежности пользователя к той или иной группе реализована в коде Web страницы. Немного усилий — и нам удалось выяснить, что Web-страницы хранятся в общем файловом SMB-pecypce. Мы попробовали подключиться к общему ресур су — стоит ли говорить, что списки ACL файлов Web-сайта оказались на удивле ние «слабыми*? Так что мы без проблем зарегистрировали свою группу и с удо вольствием посмотрели фильм!

Внимание! Способов управления доступом в приложении множество: списки ACL, разрешения SQL Server, IP-ограничения и роли. Очень вниматель но выбирайте технологию для своего продукта, а в некоторых случаях совмещайте технологии, размещая их на разных уровнях, например списки ACL и IP-ограничения, — на случай компрометации или непра вильной настройки одного из уровней.

Резюме В отсутствие качественной реализации шифрования и управления ключами списки ACL становятся последним форпостом защиты, способным остановить прорвав шего остальные заслоны взломщика- Иногда один качественный ACL на защища емом объекте предотвращает компрометацию целой сети. Помните о принципах защиты на всех уровнях, описанных в главе 3, и используйте списки ACL для со здания надежной и эффективной многоуровневой системы безопасности, ГЛАВА Принцип минимальных привилегий О области безопасности действует принцип: задачи всегда следует исполнять с минимально возможным набором привилегий. Отпилить кусок пластиковой тру бы можно ножовкой или мощной электропилой — обе годятся, но вторая янно избыточна для такой задачи. Если что-то пойдет не так, электропилой вы безна дежно испортите трубу, ножовка же подойдет идеально. То же верно в отноше нии программных процессов — их рекомендуется выполнять с привилегиями как раз достаточными для конкретной задачи, и не более того.

Принцип минимальных привилегий предполагает также сокращение времени работы с повышенными полномочиями — это уменьшает интервал, когда злоумыш ленник может воспользоваться недостатками системы. В Windows дополнитель ные права разрешается назначить прямо перед выполнением задачи, а после снова отозвать их. В нашем примере постоянная работа с повышенными привилегия ми похожа на постоянно включенную электропилу на кухне. Понятно, что это чрезвычайно опасно!

Любая серьезная программная ошибка (например подверженность перепол нению буфера), обнаруженная и использованная злоумышленником, принесет меньше вреда, если программа работает с низкими привилегиями. Проблемы воз никают, когда пользователи случайно или неумышленно запускают на исполне ние вредоносный код (например «троянцев» во вложениях сообщений электрон ной почты или код, проникающий в систему, используя переполнение буфера), который исполняется с привилегиями самого пользователя. Иначе говоря, про цесс, созданный при запуске «троянца», наследует все права пользователя, кото рый его вызвал. Кроме того, если пользователь является членом локальной груп пы Administrators (Администраторы), вредоносный код в принципе имеет возмож Методы безопасного кодирования 178 Часть II ность получить все системные привилегии и полный доступ к объектам. В этом случае возможность ущерба возрастает многократно, Вы себе не представляете, как часто я сталкиваюсь с программами, которые исполняются в администраторском контексте безопасности или, того хуже, в виде системной службы. Если немного подумать и правильно спроектировать программу.

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

какие права требуются для правильного и безопасного выполнения программы.

Внимание! Некоторым приложениям привилегии администратора все-таки нужны — это средства администрирования и программные средства, которые влияют на работу операционной системы.

В двух словах о вирусах, троянцах и червях.' «Троянец», или троянский конь, — это компьютерная программа с неизае стнмми или скрытыми возможностями, причем обычно разрушительными.

Вирусом называется программа, которая копирует себя и свой вредонос ный код на машины пользователей. А червь — это компьютерная програм ма (обычно СЕШоразмножающзяся, что позволяет ей выживать), которая по ражает компьютеры сети и мешает их работе. Собирательное название та кого рода программ — вредоносное ПО (malware).

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

Ущерб от вредоносного ПО Можно притвориться страусом, воткнуть голову в песок и сделать вид, что ниче го такого не происходит, но на самом деле в Интернете полно «плохих парней», жаждущих «крови» пользователей. Большинство атак закончились бы ничем, если бы программы не запускались с правами привилегированных учетных записей.

Два наиболее популярных вида атак в Интернете сегодня — распространение вирусов или троянцев и уродование страниц (defacement) Web-сайтов. Мы по дробно расскажем о каждой из этих атак и объясним, как наносимый ими урон можно уменьшить, запуская приложения с правами обычных пользователей.

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

Принцип минимальных привилегий ГЛАВА Средство удаленного управления Back Orifice «Поселившись* на компьютере, этот троянец позволяет удаленному злоумышлен нику контролировать компьютер: перегружать, запускать приложения, просмат ривать содержимое файлов — и все это без ведома пользователя. При устаноике Back Orifice пытается выполнить запись в системный каталог Windows и в ряд параметров реестра, в том числе в раздел HKEY_LOCAL_MACHINE\SOFTWARE\Mic rosoft\Windows\CurrentVersion\Run. Обе задачи доступны только администра го рам. Если пользователь не входит в группу администраторов, залезть в систему Back Orifice попросту не сможет.

Средство удаленного управления SubSeven Как и Back Orifice, SubSeven скрыто предоставляет злоумышленнику доступ к к< >м пыотеру через Интернет. В процессе работы SubSeven копирует себя в системный каталог Windows, корректирует файлы Win.ini и System.ini, а также изменяет па раметры служб в ветках реестра HKEY_LOCAL_MACHINE и HKEY CLASSES_ROOT.

Эти операции доступны только администратору. Опять же, если пользователе — не администратор, SubSeven не удастся «прижиться» в системе, Вирус FunLove Вирус FunLove, по классификации Symantec — W32.Funlove.4099, использует ме тод, впервые примененный в вирусе W32.Bolzano. Он изменяет на зараженном компьютере код ядра, ответственный за контроль доступа и, таким образом, пре доставляет пользователям права на все файлы. FunLove записывает файл в систем ный каталог и корректирует ядро Windows NT — Ntoskrnl.exe. Отсутствие у пользо вателя полномочия администратора не позволяет FunLove изменить нужные файлы, и вирусу не удается заразить компьютер, ВирусILoveYou Это, наверное, самый знаменитый из вирусов и троянцев, его еще называют VBS.Lo veletter или The Love Bug. ILove"Vou распространяется за счет недостатков Microsoft Outlook. Вирус действует так копирует себя в системный каталог, а затем пытает ся изменить записи в разделе HKEY_LOCAL_MACHINE реестра. И эта вредоносная программа бессильна, если у пользователя нет привилегий администратора.

Изменение страниц Web-сайтов Порча страниц Web-сайтов, особенно широко известных, — любимое развлече ние вандалов-любителей (script kiddies). Они часто атакуют Web-серверы с Internet Information Services (IIS), используя переполнение буфера в реализации протоко ла печати (Internet Printing Protocol, IPP) в Microsoft Windows 2000.

Опасность здесь в том, что обработчик заданий IPP реализован в виде ISA Pi расширения (Internet Server Application Programming Interface), работающего под учетной записью системы (SYSTEM). Серьезность этой бреши подчеркивается в фрагменте выпущенного Microsoft бюллетеня по безопасности (http://www.micm soft.com/technet/security/bulletin/MSO 1 -O23.asp)- Часть II Методы безопасного кодирования Брешь в защите возникает из-за того, что ISAPI-расширение со держит неконтролируемый буфер в коде обработки входных параметров. Это позволяет соумышленнику атаковать удаленно, вызывая переполнение буфера, и запускать на сервере произволь ный код, который выполняется в контексте безопасности сие темы. Таким образом, злоумышленник получает полный контроль над сервером, Если бы протокол 1РР в Windows 2000 не работал под учетной записью SYSTEM, изуродованных Web-сайтов было бы меньше. Ведь системная учетная запись пре доставляет полный доступ к компьютеру, в том числе полномочия по созданию новых Web-страниц.

Внимание! Запуск программ с высокими привилегиями, как и работа ваших пользователей с такими правами, в лучшем случае представляет потен циальную угрозу, а в худшем — приводит к катастрофическим послед ствиям. Не запускайте приложения с высокими и потому опасными при вилегиями, если в том нет крайней необходимости.

Запомните эту рекомендацию! Прежде чем рассказать вам, как снизить при вилегии, необходимые приложению, мы познакомим вас с управлением доступом и привилегиями в Windows.

Краткий экскурс в управление доступом Защита ресурсов от неавторизованных пользователей в Microsoft Windows NT/2000/ ХР и Windows.NET Server 2003 организована с помощью разграничения доступа:

для этого применяются избирательные таблицы управления доступом (Discre tionary Access Control List, DACL). DACL-таблицы (обычно название сокращается до ACL ) состоят из записей управления доступом (Access Control Entry, АСЕ). Каждая АСЕ содержит идентификатор безопасности (Security ID, SID) участника безо пасности (principal), которым может быть пользователь, группа или компьютер, информацию о нем и действиях, которые он может выполнять с объектом. На пример, одним участникам безопасности можно предоставить доступ на чтение, а другим — полный доступ к объекту, защищенному с помощью ACL Более подробно об ACL рассказано в главе 6.

Коротко о привилегиях Учетные записи пользователей Windows обладают привилегиями, иначе — пра вами, которые позволяют или запрещают выполнять определенные (привилеги рованные) действия над всей системой, а не над отдельными объектами, напри мер правом на вход в систему, отладку программ других пользователей, измене He совсем верно. Списки управления доступом (ACL) к объектам Windows NT/2000/ ХР состоят из избирательной (DA.CL) и системной (SACL) таблиц управления досту пом. Первая используется для регулирования доступа к объект}', а вторая — для управления аудитом. — Прим, перев.

ГЛАВА 7 Принцип минимальных привилегий ние системного времени и т. п. Некоторые исключительно «мощные» привилегии (те, что позволяют выполнять важные и небезопасные действия) перечислены в табл. 7-1.

Таблица 7-1. Наиболее важные привилегии Windows Системное название Название константы Отображаемое название {численное значение) #define (Winnt.h) Backup Files And Directories SE_BACKUPJ4AWE SeBackupPrivilege (16) (Архивирование файлов и каталогов) Restore Files And Directories SeRestorePrivilege (17) SEJtESTORE_NAME (Восстановление файлов и каталогов) Act As Part Of The Operating SeTcbPriuilege (6) SE_TCB_NAME System (Работа в режиме операционной системы) Debug Programs SeDebugPrivilege (19) SE_DEBUG_NAME (Отладка программ) SeAssignPmnaryToken - SE_ASSlGNPRlMARYrOKEN Replace A Process Level Token (Замена маркера NAME Privilege (2) уровня процесса) SeLoadDriverPrivilege (9) SE LOAD DRIVER NAME Load And Unload Device Drivers (Загрузка и выгрузка драйверов устройств) SeTakeOwnershipPmrilege (8) SE_TAKE_OWNERSHIP_NAVE Take Ownership Of Files Or Other Objects (Овладение файлами или иными объектами) Имейте в виду, что область действия этих привилегий ограничена локальной системой, но ее можно расширить на весь домен, назначив соответствующие груп повые политики. Привилегии одного пользователя на двух разных компьютерах иногда сильно отличаются. Настройка локальной политики позволяет предоста вить привилегии только на данном компьютере, но никак не на других компью терах в сети.

Познакомимся детально с каждой из перечисленных привилегий.

Привилегия SeBackupPrivilege Учетной записи с привилегией Backup files and directories доступно чтение фай лов, прямого доступа к которым у нее нет. Так, если пользователю Blake нужно сделать резервную копию файла, он сможет считать файл, несмотря на то, что ACL файла явно запрещает ему доступ обычными средствами. Программа резервного копирования читает файлы, вызывая функцию CreateFile с флагом flLE_FLAG_ BACKUP_SEMANTICS. В этом легко убедиться, выполнив следующие операции, 1. Войдите в систему под учетной записью с привилегией, разрешающей архи вирование файлов и каталогов, например под учетной записью локального администратора или оператора архива, 2. Создайте небольшой текстовый файл Test.txt с произвольным содержимым.

Часть II Методы безопасного кодирования 3. Средством редактирования ACL добавьте в файл запись АСЕ, явно запрещаю щую вам доступ. К примеру, если имя учетной записи Blake, добавьте такую АСЕ:

Blake (Deny All).

4. Скомпилируйте и запустите на исполнение указанный ниже код. Подробнее о функциях, связанных с безопасностью, вы найдете в библиотеке MSDN (http:// msdn.microsoft.com} или в ресурсах Platform SDK Л WOWAccess.cpp */ ttinclude ((Include int EnablePriv (char *szPriv) { HANDLE hToken = 0;

if (!OpenProcessToken(GetCurrentProcess(}, TOKEN_ADJUST_PRIVILEGES, AhToken)) { printf("OpenProcessTokenO завершилась с ошибкой -> Sid", GetLastErrorO);

return -1;

I TOKEN_PRIVILEGES newPrivs;

if (!LookupPrivilegeValue (NULL, szPriv, &newPrivs,Prtvileges[0].Luid)) { printf("LookupPrivilegeValueO завершилась с ошибкой ->*d", GetLastErrorO};

CloseHandle (tiToken);

return -1;

I newPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

newPrivs.PrivilegeCount = 1;

if (IAdjustTokenPrivilegesfhToken, FALSE, &newPrivs,' 0, NULL, NULL)) { printf("AdjustTokenPrivileges() завершилась с ошибкой ->Xd", GetLastErrorO);

CloseHandle (hToken);

return -1;

if (GetLastErrorO == ERROR_NOT_ALL_ASSIGNED) printf("AdjustTokenPrivileges() выполнена успешно, но не все привилегии заданы \п">;

CloseHandle (hToken);

return 0;

I Принцип минимальных привилегий ГЛАВА void Dolt(char *szFileName, DWORD dwFlags) { printf("\n\nClbiTaeMCfl считать Xs с флагами OxXx \ n", szFileName, dwFlags);

HANDLE hFile = С reateFile( szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN.EXISTING, dwFlags, NULL);

if (hFile == INVALID_HANDLE_VALUE) { printf( "Функция CreateFileO завершилась с ошибкой -> Xd GetLastErrorO);

return;

char buff[128];

DWORD cbRead=0, cbBuff = sizeof buff;

ZeroMemoryfbuff, sizeof buff);

if (ReadFile(hFile, buff, cbBuff, AcbRead, NULL)) { printfC'Vcnex, считано Xd байт\п\пТекст: Ks", cbRead, buff);

} else { printf("ReadFile() завершилась с ошибкой - > X d ", GetLastErrorO);

I CloseHandle(hFile);

void main(int argc, char* argv[]) { if (argc < 2) { printfC'UcnoflbsoeaHO: Xs ", argv[0]);

return;

// Сперва добавим привилегию архивирования файлов и каталогов.

If (EnablePriv(SE_BACKUP_NAME) == -1) return;

// Пытаемся без установленного флага архивирования - доступ отсутсвует, Dolt(argv[1], FILE_ATTRIBUTE_NORMAL);

// Пытаемся установить флаг - должно сработать!

Dolt(argv[1], FILE_ATTRIBUTE_NQRMAL | FILE_FLAG_BACKUP_SEMANTICS) ;

I Этот пример кода вы найдете среди других примеров в папке Secureco2\Cba}> terOJ- При работе программа должна вывести в отладочном окне следующее:

Методы безопасного кодирования 184 Часть II Пытаемся считать Test.txt с флагами 0x80.

Функция CreateFileO завершилась с ошибкой -> Пытаемся считать Test.txt с флагами 0x2000080 flags Успех, считано 15 байт.

Текст: Hello, Blakel Как видите, первый вызов CreateFile завершился с ошибкой запрета доступа (ошибка с номером 5), а второй удался, так как мы добавили привилегию, разре шающую архивирование, и установили флаг FILE_FLAG_BACKUP_SEMAt\'TICS.

При работе с SeBackupPrivilege я использовал дополнительный код. Однако, если у пользователя уже есть привилегии SeBackupPrivilege и SeRestorePrivilege, допол нительно ничего делать не придется. Воспользовавшись NTBackup.exe, он сможет прочитать любой файл, для этого следует сделать резервную копию в обход ACL, а затем восстановить файл в месте, где у него прав больше.

Предоставление привилегии SeBackupPrivilege ставит под удар безопасность, Ведь никак не удастся проверить, с какой целью пользователь копирует данные:

делает резервную копию или просто ворует их;

поэтому наделяйте этой приви легией только пользователей, которым доверяете.

Привилегия SeRestorePrivilege Несложно догадаться, что она противоположна привилегии резервного копиро вания. Она позволяет переписывать файлы, в том числе DLL-библиотеки и ЕХЕ файлы, к которым обычного доступа у злоумышленника нет! Кроме того, она предоставляет право поменять владельца объекта, а владелец обладает безгранич ным доступом к объекту, Привилегия SeDebugPrivilege У учетной записи с привилегией Debug Programs есть право подсоединяться к любому процессу, а также просматривать и изменять содержимое принадлежащей ему памяти. Обладая этой привилегией и достаточными знаниями, любой пользо ватель сможет подключить к процессу отладчика и получить доступ к секретным данным программы. Опасность этой привилегии отлично описана в главе 9- В частности, злоумышленник с такой привилегией запросто получит закрытый ключ сеанса SSL/TLS, «перелопатив» память процесса специальными инструментальными средствами, предоставляемыми компанией nCipher {http://www.ncipher.com'').

Кроме того, вызовом функции TerminateProcess пользователю с привилегией Debug Programs удастся завершить любой процесс в системе. Иначе говоря, такой, обычный в других отношениях, пользователь может запросто «уронить* систему, «грохнув» один из ключевых системных процессов, например Lsass.exe, диспет чер локальной безопасности (Local Security Authority, LSA).

И это только цветочки!

Самая пакостная возможность заключается в том, что функция CreateRemote Thread позволяет злоумышленнику с привилегий отладки программ запускать на исполнение код в существующих процессах. Именно так работает хакерский ин струмент LSADUMP2 (hup://razor,bindview.com/tools): уполномоченный пользова тель получает возможность просматривать секретные данные LSA. Для этого в Принцип минимальных привилегий ГЛАВА процесс Lsass.exe внедряется новый поток с кодом, который считывает закрытые данные, уже заботливо расшифрованные диспетчером локальной безопасности, Подробно о секретах LSA рассказано в главе 9 Отличный источник информации о внедрении кода в потоки программ — книга Джеффри Рихтера (Jeffrey Richter) «Programming Applications for Microsoft Windows* (Microsoft Press) (Рихтер Дж. Windows для профессионалов: Создание эффектив ных Win32-приложений с учетом специфики 64-разрядной версии Windows. СПб.:

«'Питер»;

М.: «Русская Редакция*, 2001).

Примечание Вопреки сложившемуся мнению, учетная запись нуждается в при вилегии Debug Programs только для отладки процессов, принадлежащих другим учетным записям. Для отладки собственных процессов таких прав не нужно. Так, пользователю Blake не нужна привилегия для отладки лю бого из своих приложений, но она понадобится для отладки процессов, принадлежащих Cheryl.

Привилегия SeTcbPhvilege Учетную запись с привилегией Act as part of the operating system [ее также часто называют Trusted Computing Base (ТСВ)] можно рассматривать как высоконадеж ный системный компонент. Она предоставляет максимум полномочий и поэтому считается самой опасной в Windows. Вот почему по умолчанию эта привилегия предоставляется только учетной записи SYSTEM.

Внимание! Не следует предоставлять привилегию ТСВ, если нет очень серь езных на то оснований. Надеюсь, прочитав эту главу, вы поймете, что лучше обойтись без нее, Примечание Чаще всего необходимость предоставления привилегии ТСВ обус ловлена необходимостью вызова функций типа LogonUser, которые без нее не работают. Но, начиная с Windows XP, при вызове LogonUser из приложения для входа под пользовательской учетной записью Windows эта привилегия больше не требуется. Тем не менее она нужна при входе под учетной записью Passport или когда параметр GroupSid не равен NULL, Привилегии SeAssignPrimaryTokenPrivilege и SelncreaseQuotaPrivilege Учетная запись с привилегиями Replace A Process Level Token и Increase Quot is позволяет получить доступ к маркеру процесса другого пользователя и создать от его имени новый процесс — так называемые атаки с подменой источника (spoofing) или с целью повышения полномочий.

Привилегия SeLoadDriverPrivilege Исполняемый код ядра считается очень надежным и пользуется практически не ограниченным доверием, поэтому ему доступны любые операции. Для загрузки Часть II Методы безопасного кодирования 1 кода в ядро обязательна привилегия SeLoadDriverPrivilege, так как загруженный код может выполнять множество потенциально опасных действий. Предоставлять эту привилегию рядовому пользователю опасно, поэтому по умолчанию ею облада ют только администраторы.

Замечу, что для загрузки драйверов самонастройки (Plug and Play) эта приви легия не нужна — их загружает системная служба Plug and Play.

Привилегия SeRemoteShutdownPrivilege Ее действие очевидно — она позволяет удаленно завершать работу компьютера, Заметьте: как и в остальных случаях, пользователь должен обладать привилегией на целевом компьютере. А теперь представьте себе, сколько радости вы достави те злоумышленнику, предоставив эту привилегию группе Everyone (Все) на всех компьютерах сети! Никакая успешная распределенная DoS-атака (Denial of Service) не сможет создать такой кавардак!

Привилегия SeTakeOwnershipPrivilege В Windows NT/2000/XP существует понятие владелец объекта (owner). Это лицо (или объект), пользующееся полной и нераздельной властью над всеми объекта ми, которыми владеет. Обладателю этой привилегии ничего не стоит «позаимство вать* объект у «законного» владельца, таким образом получив неограниченный доступ к любому объекту системы.

Примечание Примечательно, что до Windows XP владельцем объекта, создан ного администратором системы, назначалась группа локальных адми нистраторов. В Windows XP и более поздних версиях, включая Windows.NET Server 2003, это не обязательно — владельцем может быть как ло кальная группа Administrators (Администраторы), так и сам создатель объекта, Примечание Bypass Traverse Checking (Обход перекрестной проверки), или SeChangeNotijyPrivilege. — единственная привилегия, необходимая всем учетным записям пользователей. Она требуется для получения инфор мации об изменениях файлов и каталогов. Впрочем, главное преимуще ство этой привилегии по умолчанию в том, что она позволяет избежать процедуры проверки доступа на пути к определенному объекту в любой файловой системе Windows или в реестре. Привилегия применяется при оптимизации файловой системы NTFS, Несколько слов о маркерах После входа в систему Windows NT/2000/XP и успешной аутентификации пользо вателю назначается специально созданная структура данных— маркер (token), который прикрепляется ко всем запускаемым пользователем процессам и пото кам. Кроме прочего маркер содержит SID пользователя, SID-идентификаторы всех групп, членом которых является пользователь, и список его привилегий. В дей Принцип минимальных привилегий ГЛАВА ствительности именно маркер определяет, какие операции на компьютере до ступны пользователю. Как при входе с консоли, так и при удаленном входе в си стему маркер создается только после успешной аутентификации. Любая коррек тировка параметров учетной записи (например, изменение членства в группах или смена привилегий) вступает в силу только после следующего входа в систему.

Начиная с Windows 2000, маркер может содержать информацию обо всех уда ленных или отключенных SID и привилегиях. Такой маркер называется ограни ченным маркером (restricted token). Как они применяются в приложениях, я рас скажу чуть попозже.

Как взаимодействуют маркеры, привилегии, SID, ACL и процессы Все процессы Microsoft Windows NT/2000/XP выполняются в определенном кон тексте безопасности, иначе говоря, маркер закреплен за процессом. Выполняемый процесс обычно отождествляется с запустившим его пользователем. Однако пользо ватель с достаточными привилегиями вправе запускать приложения от имени других пользователей, вызвав функцию CreateProcessAsUser. Как правило, процесс.

обращающийся к CreateProcessAsUser, должен обладать привилегиями SeAssignPn marylbkenPrivilege и SelncreaseQuotaPrivilege. Впрочем, если маркер, переданный функции в качестве первого аргумента, — ограниченная версия основного мар кера пользователя, то привилегия SeAssignPrimaryTokenPrivilege не нужна.

Процесс другого типа — служба — работает в контексте безопасности, опре деляемом в диспетчере служебных программ (Service Control Manager, SCM). Боль шинство служб по умолчанию исполняются как Local System, но можно перена строить службу на работу под другой учетной записью, указав соответствующие имя и пароль в SCM (рис. 7-1).

l Demote Procedure Call (UPC) Lucalai Properties ]local...

• [or tilt hacdwaif profiles lilted M» i Saves Enabled Рис. 7-1. Настройка службы для работы под заданной учетной записью eSCM Примечание Пароли для запуска служб хранятся как секреты LSA. Подробнее с секретными данными LSA мы познакомим вас в главе 9.

Методы безопасного кодирования 188 Часть II За процессом закрепляется маркер учетной записи, который обладает инфор мацией обо всех свойствах и привилегиях пользователя, поэтому процесс иног да можно считать «полномочным представителем» учетной записи — все, что раз решено учетной записи, разрешено и процессу. Это справедливо только при усло вии, что полномочия маркера не урезаются применением ограничивающего мар кера, который представляет собой «урезанную* версию исходного маркера.

Идентификаторы SID и управление доступом, а также привилегии и их проверка Маркер содержит SID и привилегии. Первые служат для управления доступом к ресурсам на основе ACL, а вторые — для выполнения общесистемных операций.

Часто на мой вопрос о том, зачем процессу требуются высокие привилегии, его разработчики отвечают, что программе нужно считывать и изменять записи рее стра. Лишь часть из них в полной мере понимает, что при этом выполняется про верка доступа, а не привилегий. Зачем же давать приложению все эти опасные привилегии? Иногда я слышу такие доводы: «Для работы нашей программы архи вирования нужны административные полномочия». Но для архивирования фай лов требуется привилегия, а не SID члена группы администраторов.

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

Чрезвычайно важно, чтобы вы разобрались в том, как связаны и чем отличаются SID и привилегии.

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

• проблемы, возникающие из-за ACL;

• проблемы, возникающие из-за привилегий;

• использование секретов LSA.

Я познакомлю вас поближе с каждой из них, а затем расскажу о способах ре шения этих проблем.

Проблемы с ACL Пусть в разделе NTFS есть папка со следующими ACL:

• SYSTEM — разрешение Full Control (Полный доступ);

• Administrators (Администраторы) — разрешение Full Control (Полный доступ);

• Everyone (Все) — разрешение Read (Чтение).

Если у вас нет привилегированной учетной записи, такой, как у администра тора или системного процесса, вы сможете только читать файлы этой папки — запись, удаление и другие действия вам недоступны (при попытке выполнить та кую операцию файлового ввода/вывода вы получите ошибку отказа в доступе). За помните: отказ в доступе — это ошибка под номером 5!

ГЛАВА 7 Принцип минимальных привилегий 1 Есть одна очень распространенная проблема: приложения, записывающие данные в защищенные области файловой системы, нормально работают, только получив права администратора. Сколько известных вам игр записывают таблицу результатов в каталог C:\Program Files? Я отвечу за вас: масса. И это действительно создает проблемы, так как играющий должен обладать полномочиями админис г ратора. Во многих играх есть режим работы через Интернет, а ведь при этом открываются сокеты. Вот где широчайшее поле деятельности для хакеров всех мастей! Злоумышленнику достаточно воспользоваться переполнением буфера или другим слабым местом обработчика сокетов, чтобы запустить на компьютере иг рока свой код с правами администратора. И все! Game Over!

Открытие ресурсов в режиме доступа GENERIC ALL Есть и менее очевидная проблема, связанная с ACL, — предоставление доступа к ресурсам с большими, чем требуется, разрешениями. Пусть вышеупомянутый ACL принадлежит файлу и программа открывает файл в режиме доступа GENERIC_AJ,L Под какими учетными записями программа будет работать корректно? Толы-.о администратора или SYSTEM. Режим доступа GENERIC_ALL аналогичен Full Contr Я (Полный доступ). Другими словами, этот режим позволяет открывать файл и со вершать над ним любые действия. Но что, если вашей программе требуется вы полнять только чтение файла. Нужен ли вам режим GENERIC_ALL? Конечно, нет.

Достаточно открыть файл в режиме GENERICjREAD, и файл станет доступным любому пользователю приложения, так как в ACJ, есть запись разрешение Retd (Чтение) для группы Everyone (Все). Такое решение сочетает практичность и бе зопасность: практично, так как приложение работает и выполняет свои операции в режиме «только для чтения», и надежно, потому что файл доступен приложе нию (благодаря наличию соответствующей записи АСЕ) в режиме только для чтения и ничего более.

Запомните: в Windows NT/2000/XP приложению либо предоставляются запр i шиваемые разрешения, либо же возвращается ошибка отказа в доступе. Если при ложение запрашивает полный доступ, a ACL ресурса разрешает только чтение, программа не получит даже права на чтение — система возвратит ошибку отказа в доступе.

Можно пытаться открывать объекты с максимально допустимыми правами, пе редавая в параметре dwDesiredAccess значение MAXIMUM ^ALLOWED, однако результ i ты этих действий непредсказуемы, поэтому без обработки ошибок здесь не обойтис ь, Проблемы с привилегиями Понятно, что учетной записи для выполнения задач типа архивирования файлов нужны соответствующие привилегии. Но будьте осторожны: администратору не рекомендуется предоставлять учетным записям слишком много опасных приви легий, а разработчику — требовать наличия у пользователей программы множе ства ненужных привилегий. Почему — я уже объяснил выше.

Использование секретов LSA LSA может хранить секретные данные других приложений. Для управления сек ретами LSA применяются API-функции LsaStorePrivateData и LsaRetrievePrivateData.

Методы безопасного кодирования 190 Часть II А суть проблемы в том, что использовать секреты LSA могут только члены группы локальных администраторов. Вот что говорится в Platform SDK по поводу LsaStore PrivateData: «Перед записью данные шифруются, a DACL ключа позволяет считы вать данные только создателю и администраторам*. По сути, эти функции LSA доступны только администраторам, и здесь-то и кроется затруднение, если нуж но создать приложение с соблюдением принципа минимальных привилегий и при этом предусмотреть возможность сохранения секретных данных пользователя.

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

Решение проблемы ACL Есть три основных способа «разруливать» затруднительные ситуации с ACL • открывать ресурсы в подходящем режиме доступа;

• сохранять пользовательские данные в местах, доступных пользователю для записи;

• делать ACL более «толерантными».

Во-первых, ресурсам следует назначать только те разрешения, которые действи тельно необходимы. Если необходимо прочитать раздел в реестре, требуйте до ступ только для чтения. При этом выполняются простейшие операции, поэтому вероятность регрессионной ошибки невелика, Следующий способ: не записывайте пользовательские данные в защищенные системные папки ОС, к которым относятся ветвь реестра HKEY_LOCAL_MACHINE, каталоги C:\Program Files (или другой каталог, на который указывает переменная окружения %PROGRAMFJLES%} и C:\Winnt (%SYSTEMRQOT%). Храните пользователь скую информацию в ветви HKEY_CURRENT_USER, а файлы пользователей — в каталоге профиля. Для определения каталога профиля пользователя вставьте сле дующий фрагмент кода:

ttinclude "shlobj.h" TCHAR szPath[MAX_PATH];

{ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL NULL, 0, szPath» HANDLE hFile = CreateFile(szPath,..,);

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

ГЛАВА 7 Принцип минимальных привилегий 1 Ну и наконец, вы можете немного ослабить «непримиримость» записей управ ления доступом (ACL) — это менее рискованно, чем предоставлять пользователям полномочия администратора. Понятно, что делать это нужно предельно осторожно, ведь небезопасная ACL делает систему более уязвимой. Никогда не решайте пр< > блем с привилегиями за счет создания проблем с авторизацией.

Решение проблем с привилегиями Как уже говорилось, если для выполнения задачи действительно требуется при вилегия, тогда деваться некуда, ведь без нее проблему не решить. Но это вовсе не означает, что нужно раздавать привилегии направо и налево просто для того, чтобы все работало! Честно говоря, нет универсального и легкого способа избежать проблем, связанных с привилегиями.

Решение проблем с LSA В Windows 2000/ХР применяется API защиты данных (Data Protection API, DPAP ), Его использование желательно по многим причинам, но для нас наиболее важно то, что в этом случае пользователю не требуются полномочия администратора для доступа к секретным данным, а сами данные защищены закрепленным за польз> > вателем ключом.

Примечание Подробно сведения о использовании DPAPI — в главе 9 Определение оптимального набора привилегий Как говорилось в главе 6, в ACL вы должны быть готовым поручиться за каждь и АСЕ. Это также касается SID и привилегий в маркере. Если приложение требуется выполнять от имени администратора, вы должны быть уверены в каждом SID и привилегии в маркере администратора. Б противном случае лучше убрать сомни тельные разрешительные записи.

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

1. Выясните, какие ресурсы нужны приложению, 2. Создайте перечень всех системных API-функций, задействованных в приложении, 3. Выясните, какая учетная запись требуется для работы приложения.

4. Выясните, какие SID и привилегии есть в маркере доступа.

5. Решите, какие SID и привилегии необходимы для работы приложения.

6. Скорректируйте маркер в соответствии с результатами предыдущего этапа.

Этап 1: выясните, какие ресурсы нужны приложению Прежде всего следует инвентаризовать ресурсы, необходимые для работы прил« > жения: файлы, разделы реестра, сокеты, данные Active Directory, именованные каналы и т. п., а также определить, какой уровень доступа требуется для того или иного ресурса. Например, в рассматриваемом далее иллюстративном Windows приложении ресурсы, необходимые процедурам определения привилегий, пере числены в табл. 7-2.

Часть II Методы безопасного кодирования Таблица 7-2. Ресурсы, используемые в приложении-примере Требуемые права доступа Ресурс Для настройки приложения администраторам Файлы с параметрами конфигурации требуется полный доступ к данным конфигурации.

Остальным пользователям достаточно только считывать данные Данные, поступающие Каналы предоставляются всем для чтения и записи по именованным каналам данных Каталог для хранения файлов Каждому разрешается создавать файлы и выполнять приложения любые действия со своими данными. Любому пользо вателю доступны для чтения файлы остальных пользователей Всем предоставляется доступ для чтения и право Установочный каталог программы запускать приложение. Администраторы могут устанавливать обновления Этап 2: выясните, какими системными API-функциями пользуется приложение Создайте список всех функций, для работы которых необходимы привилегии (табл. 7-3).

Таблица 7-3. Функции Windows и необходимые для их работы привилегии Необходимые привилегии Название функции или членство в группе CreateFtte () с флагом SeBackupPriuilege FILE_FIAG_BACKUP_SEMANTICS - открытие файла для архивации LogonUser ~ вход в систему SeTcbPrivilege (в Windows XP и Windows.NET Server 2003 больше не требуется) SetTokenlnformation — модификация SeTcbPrivilege информации маркера ExitWindowsEx — закрытие окна SeShutdownPrivilege OpenEventLog — открытие журнала SeSecurityPrivuege безопасности BroadcastSystemMessagefEx] — рассылка SeTcbPrivilege системного сообщения во все окна в системе (BSM_AU.,DESKTOPS) SendMessage и PostMessage — пересылка SeTcbPrivilege сообщения на другой компьютер RegisterLogonProcess SeTcbPrivilege IntiiateSystemShutdoum[Ex] -~ завершение SeShutdownPrivilege или работы ОС SeRemoteShutdoumPrivilege SetSystemPowerState — приостановка SeSbutdownPrivilege работы ОС GetFtteSecurity — получение информации SeSecurityPrivilege о параметрах безопасности файла ГЛАВА 7 Принцип минимальных привилегий Таблица 7-3. (окончание) Необходимые привилегии Название функции или членство в группе Функции отладки «чужих» процессов, SeDebugPrivilege в том числе DebugActiveProcess и ReadProcessMemory CreateProcessAsUser — запуск процесса SelncreaseQuotaPrivilege и обычно от имени другого пользователя SeAsstgnPrimaryTokenPrivilege CreatePrivateObjectSecitrityEx — SeSccurityPrivilege создание самодостаточного дескриптора безопасности SetSystemTime — настройка SeSystemtimePrivilege системного времени SeLockMemoryPrivilege VirtualLock и AllocateUserPbysicalPages — закрепление физической памяти за процессом Функции управления сетевыми В большинстве случаев пользователь пользователями и группами, должен входить в определенные группы, например Administrators (Администраторы} такие как NetUserAdd и NetLocalGroupDel или Account Operators (Операторы учета) SeMachineAccountPrivttege NetJoinDomain — присоединение к домену Примечание Не забывайте, что приложение способно вызывать Windows-фун кции косвенно через функции-оболочки (wrappers) и СОМ-интерфейсы, В нашем примере нет функций, которым требуются привилегии, как, впрочем и в большинстве Windows-приложений.

Этап 3: определите, какая требуется учетная запись Опишите учетную запись, необходимую для нормальной работы приложения.

Например, нужны ли полномочия администратора или программа будет работать как служба под учетной записью локальной системы, Разработчики нашего приложения-примера поленились и решили, что програм ме нужны права администратора. Нерадивые тестировщики также проверил i работу программы только под учетной записью администратора. Да и проекта ровщики достойны осуждения: они пошли на поводу у разработчиков и тестироь щиков!

Этап 4: исследуйте содержимое маркера Теперь выясните, какие SID и привилегии содержатся в маркере учетной записи, определенной на предыдущем этапе. Для этого или войдите в систему под это-'i учетной записью, или используйте команду RunAs для запуска нового экземпляр;

!

командного процессора. Так, для выполнения приложения от имени администрг тора введите в командной строке:

RunAs /user:MyMachine\Administrator cmd.exe 194 Часть И Методы безопасного кодирования При этом (если вы знаете пароль администратора) запустится новый экземп ляр командного процессора от имени администратора, и все приложения, запус каемые в нем, будут работать под администраторской учетной записью.

Если вы, будучи администратором, хотите запустить экземпляр командного процессора от имени SYSTEM, рекомендуем для запуска задачи задействовать планировщик задач — просто назначьте запуск в течение следующей минуты. Так, если текущее время 17:01, следующая команда запустит командный процессор чуть меньше, чем через минуту:

At 17:02 /INTERACTIVE "cmd.exe" Созданный экземпляр командного процессора выполняется в контексте Local System.

Теперь, попав в контекст, который следует исследовать, запустите программу тестирования MyToken.cpp. Она предоставляет массу ценной информации о мар кере доступа пользователя, Л MyToken.cpp */ «define SECURITY_WIN (/include "windows.h" «include "security.h" «include "strsafe.h" «define MAX.NAME // Эта функция определяет размер требуемой памяти // и выделяет ее. Память должна освобождаться вызывающей программой.

LPVOID AllocateTokenlnfoBufferf HANDLE hToken, TOKEN_INFORMATION_CLASS InfoClass, DWORD *dwSize) { •dwSize=0;

GetTokenInformation( hToken, InfoClass, NULL, *dwSize, dwSize);

return new BYTE[*dwSize];

;

• // Определяем имя(имена) пользователя, void GetUserNamesO { EXTENDED_NAHE..FOHHAT enf[] = {NameDisplay, NameSamCompatible,NameUserPrincipal};

for (int i=0;

i < sizeof(enf) / sizeof(enf[0]);

char szName[12B];

DWORD cbName = sizeof(szName);

if (GetUserNameEx(enf[i], szName.&cbName)) ГЛАВА 7 Принцип минимальных привилегий printf("HM» (format Kd): Xs\n",enf[l],szName);

// Отображаем информацию 810 и ограничивающих SID.

void GetAllSIDs(HANDLE hToken, TOKEN_INFORMATION_CLASS tic) { DWORD dwSize = 0;

TOKEN_GROUPS *pSIDInfO = (PTOKEN.GROUPS) AllocateTokenInfoBuffer( hToken, tic, SdwSlze);

if (IpSIDInfo) return;

if (!GetTokenInformation(hToken, tic, pSIDInfo, dwSize, &dwSize)) printf("GetTokenInformation Error Яи\п", GetLastErrorO);

if (!pSIDInfo->GroupCount) print f( "\tnycTo! \n");

for (DWORD i=0;

i < pSIDInfo->GroupCount;

i++) { SID_NAME_USE SidType;

char lpName[MAX_NAHE];

char lpOomain[MAX_NAHE];

DWORD dwNameSize = HAX_NAHE;

DWORD dwDomainSize = MAX_NAME;

DWORD dwAttr = 0;

if (! LookupAccoimtSid( NULL, pSIDInfo->Groirps[i].Sid, IpName, &dwNa

else prlntf("LookupAccountSid Error Xu\n", GetLastErrorO);

} else dwAttr = pSIDInfo->Groups[i]. Attributes;

printf(K12s\\Ji-20s\t!Es\n", IpDomain, IpName, (dwAttr & SE_GROUP_USE_FOR_DENY_ONLY) ? " [DENY]" : " ");

delete [] (LPBYTE) pSIDInfo;

} // Отображаем сведения о привилегиях, 196 Часть II Методы безопасного кодирования void GetPrivs(HANDLE hToken) { DWORD dwSize = 0;

TOKEN_PRIVILEGES *pPrivileges = (PTOKEN_PRIVILEGES) AllocateTokenInfoBuffer( hToken, TokenPrivileges, &dwSize);

if (! pPrivileges) return;

BOOL bRes = GetTokenInformation{ hToken, TokenPrivileges, pPrivileges, dwSize, SdwSize);

if (FALSE == bRes) printf("GetTokenInforniation завершилась с ошибкой\п");

for (DWORD i=0;

i < pPrivileges- >PrivilegeCount;

char szPrivilegeName[1283;

DWORD dwPrivilegeNameLength=sizeof(szPrivilegeName);

if (LookupPrivilegeNameCNULL, &pPrivileges->Privileges[i]. Luid, szPrivilegeName, &dwPrivilegeNameLength)} printf("\tXs (Klu)\n", szPrivilegeName, pPrivileges->Privileges[i]. Attributes);

else printf("LookupPrivilegeName завершилась с ошибкой - Xlu\n' GetLastErrorO);

delete [] (LPBYTE) pPrivileges;

int wmain{ ) { if (! ImpersonateSelf(Securitylmpersonation)) { printf("ImpersonateSelf Error Ku\n", GetLastErrorO);

return -1;

HANDLE hToken = NULL;

if ( ! OpenProcessToken(GetCur rentProcess( ), TOKEN_QUERY, ihToken) ) { printf("OpenThreadToken Error Xu\n", GetLastErrorO);

return -1;

printf("\nUser Name\n");

GetUserNamesO;

ГЛАВА 7 Принцип минимальных привилегий 19"i printf("\nSIDS\n");

GetAllSIDs(hToken,TokenGroups);

printf("\nRestrioting SIDS\n");

GetAllSIDs(hToken,TokenRestrictedSids);

printf("\nPrivileges\n");

GetPrivs(nToken);

RevertToSelfO;

CloseHandle(nToken);

return 0;

!

Исходный текст файла MyToken.cpp хранится в папке Secureco2\Chapter07 < примерами. Программа открывает маркер потока и извлекает из него информа цию об имени пользователя, обычных SID. ограничивающих SID и привилегиях Основную работу выполняют функции GetUser, GelAllSIDs и GetPrivs, Существует два варианта GetAllSIDs: для извлечения стандартных и ограничивающих SID. По следние (их наличие не обязательно) добавляются в маркер для снижения уров ня доступа процесса или потока по отношению к доступу пользователя. Об огра ниченных маркерах я расскажу далее в этой же главе, впрочем, как и о SID с про веркой только на запрет (они отмечаются строкой [DENY]}.

Примечание Перед открытием маркера доступа потока для детального иссле дования придется позаимствовать права пользователя, но это не обяза тельно, если вызывать функцию QpenProcessToken, Если вы не хотите сами разрабатывать программу анализа содержимого мар кера, используйте приложение Token Master — оно описано в книге Джеффри Рих тера (Jeffrey Richter) и Джейсона Кларка (Jason Clark) «Programming Server-Side Applications for Microsoft Windows 2000» (Microsoft Press, 2000) (Рихтер Дж, Кларк Дж. Д, Программирование серверных приложений для Microsoft Windows 2000. СПб.:

•[•Питер»;

М.: «Русская редакция», 2001) и содержится на прилагаемом к ней ком пакт-диске. Token Master позволяет вам войти в систему под выбранной учетной записью и исследовать созданный при этом операционной системой маркер, а также получить доступ к работающему процессу и просмотреть содержимое его маркера (рис. 7-2).

Поле Token Information содержит список всех SID и привилегий маркера, а также SID пользователя. Пример-приложение следует запускать от имени администра тора. Анализируя стандартный маркер администратора, MyToken.cpp выявляет следующую информацию:

User NORTHWINDTRADERSXblake NOfiTHWINDTRADERS\Domain Users SIDS \Everyone BUILTIN\Adrninistrators BU3LTIN\Users Часть II Методы безопасного кодирования NT AUTHORITY\INTERACTIVE NT AUTHORITY\Authenticated Users Restricting SIDS None Privileges SeChangeNotifyPrivilege (3) SeSecurityPrlvilege (0) SeBackupPrivilege (0) SeRestorePrivilege (0) SeSystemtimePrivilege (0) SeShutdownPrivilege (0) SeRemoteShutdownPrlvilege (0) SeTakeOwnershipPrivilege (0) SeDebugPrivilege (0) SeSystemEnvironmentPrivilege (0) SeSystemProfilePrivilege (0) SeProfileSingleProcessPrivilege (0) SelncreaseBasePriorityPrivilege (0) SeLoadDriverPrivilege (2) SeCreatePagefilePrivilege (0) SelncreaseQuotaPrivilege (0) SeUndockPrivilege (2) SeManageVolumePrivilege (0) Jat Name Этап Name Pannorf Luncr Trej Logon Prov S-t*35U MnSlD l.-itoreffcs and din ^dronMaot: iSeSecurityPlivilege-Manageamidngand $m*iHam BUI L TIN SeShufdo^nPii-ilege--Shu(doiAn Ihe sjjsle SE_GBQUP EMefiLED,8V DEFAULT Sefctftirfoge-Acl a< nut of Ям ок^йп б^пАкКРиу^дг-Нето^в computer Iro э*1*<*вп1п1нта|1ог-.

S&OltaitQ* iVEvHiwne i^S ^^ l BUILTIN\A*™Ujitai INTbUTHOFtTrtAulhsri Рис. 7-2. Результаты анализа маркера экземпляра Cmd.exe, выполняемого от имени SYSTEM Принцип минимальных привилегий ГЛАВА Обратите внимание на цифры рядом с названиями привилегий — это бито вые маски из значений, указанных в табл. 7-4.

Таблица 7-4. Атрибуты привилегий Атрибут Значение Описание 0x80000000 Привилегия использовалась S1 PRIVILEGED. USED_FOR_ACCESS для получения доступа к объекту '•1 0x00000001 Привилегия предоставлена PRJWLEGE ENABLED _BY_DEFAULT по умолчанию ч 0x00000002 Привилегия предоставлена PRR'ILEGE ENABLED Этап 5: выясните необходимость всех привилегий и SID-идентификаторов Этот этап намного веселее: совместно с представителями групп проектирования, разработки и тестирования проанализируйте каждый SID и привилегию, пытаясь выяснить, без каких вы обойдетесь. Просто сравните список используемых ресурс >в и функций, полученный на этапах 1 и 2, с полученной на этапе 4 информацией о маркере. Если маркер содержит SID иди привилегии, в которых нет необходимо сти, серьезно подумайте об их удалении.

Примечание Наличие некоторых SID, таких как идентификаторы групп Users (Пользователи) и Everyone (Все), вполне обосновано. Не стоит удалять их из маркера.

Наше приложение получает доступ только на основании таблицы ACL, а не привилегий, однако содержит целый «букет* неиспользуемых привилегий! Если в приложении есть ошибка, позволяющая запускать на исполнение посторонний код, он получит все эти привилегии. Из перечисленных наибольшую опасность представляет привилегия отладки программ.

Этап 6: внесите изменения в маркер Последняя операция — ограничение возможностей маркера. На то есть три спо соба:

• разрешить выполнение приложения под другими, менее привилегированны ми, учетными записями;

• использовать ограниченные маркеры (restricted tokens);

• радикально удалить ненужные привилегии.

А теперь поговорим о каждом поподробнее, Разрешите выполнение приложения под непривилегированными учетными записями Такая операция вполне допустима, но при этом придется отключить некоторые функции программы. Например, пользователю доступны 95% возможностей пр< > граммы, исключение составит архивирование файлов.

Часть II Методы безопасного кодирования Примечание Для того чтобы проверить во время выполнения программы, об ладает ли учетная запись нужной привилегией, применяется Windows функция PrivilegeCbeck. Если программа выполняет привилегированную операцию, например архивирование файлов, вы вправе запретить ее для непривилегированного пользователя.

Внимание! С приложением, для работы которого требуются высокие приви легии, могут возникнуть проблемы при внедрении в крупных компани ях. В больших организациях очень не любят предоставлять пользовате лям какие-либо дополнительные права, помимо базовых. Это делается из соображений безопасности и снижения совокупной стоимости вла дения. Получив привилегии, пользователь может изменить части систе мы и вывести ее из строя, а на восстановление придется затратить уси лия службы поддержки. Вывод ясен: из-за необходимости предоставлять высокие привилегии в некоторых случаях клиент может отказаться от вашего приложения.

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

Как-то мне попалось Web-приложение, работающее только как SYSTEM, так как — по заявлению разработчиков — один компонент программы позволял ад министратору добавлять новые учетные записи. Приложение было монолитным, поэтому не только администраторская часть, но и весь процесс приходилось за пускать от имени SYSTEM. Как выяснилось, учетные записи добавлялись доволь но редко, и после долгих дебатов разработчики решили внести изменения в сле дующую версию продукта:

• запускать приложение под специальной непривилегированной учетной записью;

• заставить приложение требовать от администраторов прохождения Windows аутентификации;

• заставить приложение олицетворять пользователя и работать с базой данных учетных записей. В этом случае при отказе в доступе выяснится, что у пользо вателя нет прав на администрирование!

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

С точки зрения безопасности, запуску приложения с минимальными приви легиями альтернативы нет. Если процесс работает как SYSTEM или под другой учет ной записью с высокими привилегиями, а созданный поток олицетворяет пользо вателя и поэтому имеет меньшие возможности, злоумышленник все равно запо лучит права SYSTEM. Внедрив свой код (к примеру, через переполнение буфера) и вызвав функцию RevertToSelf. он сможет отменить олицетворение и получить права процесса-родителя, то есть SYSTEM. Если же приложение всегда работает под не привилегированной учетной записью, вызов RevertToSelf не будет иметь катастро ГЛАВА 7 Принцип минимальных привилегий фических последствий. Возьмем, к примеру, IIS 5. Web-приложения необходимо всегда запускать в процессе, отдельном от основного, [что соответствует высоко му (High) и среднему (Medium) уровням изоляции]. В этом случае приложение вы полняется под учетной записью Г№АМ_<имя_кампыотера>. Не стоит исполнять приложение с правами основного процесса IIS [соответствует низкому (Low) уров ню изоляции], так как последний обладает привилегиями SYSTEM. В первом слу чае возможный ущерб от переполнения буфера незначителен, так как процесс ис полняется под гостевой учетной записью, для которой круг доступных операций значительно ограничен. Замечу также, что в IIS6 вообще нет пользовательского кода, работающего как SYSTEM, поэтому приложение, рассчитанное на привиле гии SYSTEM потока Web-сервера, работать не сможет.

Используйте ограниченные маркеры Одна из новинок Windows 2000/XP — ограничение возможностей маркера. Огра ниченным (restricted) называется исходный маркер или маркер олицетворения, измененный с помощью функции CreateRestictedToken. Процесс или поток с огра ниченным маркером понижается в правах на доступ к защищаемым объектам и на выполнение привилегированных операций, кроме того, потоку доступны только локальные ресурсы. Функция CreateRestictedToken позволяет выполнить одну из тр:.;

х операций ограничения маркера:

• удалить привилегии из маркера;

• внести ограничивающие SID (restricting SID);

• установить в SID-идентификаторе атрибут проверки только на запрет (den y only attribute).

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

Добавление ограничивающих SID Добавляя ограничивающие идентификаторы безопасности в маркер доступа, «от ключают» ненужные SID. Когда ограниченный процесс или поток пытается полу чить доступ к защищаемому объекте, система проводит проверку обоих наборов SID — обычных и ограничивающих — и разрешает доступ, только если обе пр« > верки завершились успешно.

А теперь пример использования ограниченных SID. Пусть ACL файла разрепш т группе Everyone (Все) чтение файла, а группе Administrators (Администраторы) чтение, запись и удаление. Приложению удалять файлы не нужно. Возможность удаления предоставляется только специальным инструментам администриров t ния, которые поставляются в комплекте с приложением. Административные пол номочия предоставлены Brian, менеджеру по сбыту. Его маркер содержит иден тификаторы следующих групп:

• Everyone (Все);

• Authenticated Users (Прошедшие проверку);

• Administrators (Администраторы);

• Marketing.

Часть II Методы безопасного кодирования Приложение не предназначено для администрирования, поэтому необходимо добавить ограничивающий (блокирующий) идентификатор с одной записью для группы Everyone. При запуске приложения от имени рядового пользователя со здается ограниченный маркер. Когда Brian пытается удалить файл с помощью инструмента администрирования, система проверяет, имеет ли он право на уда ление, просматривая первый набор SID. Ответ положительный (он входит в группу Administrators, у которой такая привилегия есть), но это еще не все. ОС просмат ривает список ограничивающих SID, находит там только SID группы Everyone и отказывает в удалении файла, так как для Everyone разрешен доступ только для чтения, Примечание Проще всего понять действие ограничивающих SID, если пред ставить, что сначала выполняется логическое умножение (AND) двух списков SID, а полученный результат служит для проверки доступа. Или же что проверка доступа осуществляется на основе пересечения двух списков SID.

Установка в SID атрибута проверки только на запрет Запрещающие идентификаторы безопасности (deny-only SID) применяются толь ко для запрещения доступа к защищаемому объекту. Их невозможно применить для предоставления доступа. Если в ACL ресурса Marketing имеется АСЕ-запись Deny:

Full Controll (Запретить: Полный доступ), а в маркере содержится SID группы Marketing, то пользователь доступа не получит. Но если у другого ресурса есть АСЕ запись Marketing — Allow: Read (Разрешить: Чтение), а в маркере пользователя SID группы Marketing является запрещающим, запрет распространяется только на чтение объекта.

Подозреваю, что это выглядит слишком сложно, но надеюсь, что табл. 7-5 по может вам разобраться в этом вопросе.

Таблица 7-5. Взаимодействие запрещающих SID и ACL ACL объекта ACL объекта содержит содержит ACL объекта запись запись не содержит «Marketing — «Marketing — АСЕ-записи Allow: Read» Deny: Full Control» для Marketing Доступ разрешен Маркер пользователя Доступ запрещен Доступ зависит содержит SID группы от других АСЕ Marketing объекта Маркер пользователя Доступ запрещен Доступ запрещен Доступ зависит содержит запрещающий от других АСЕ SID группы Marketing объекта Заметьте: простое удаление SID из маркера — и проблема с безопасностью туг как тут, именно поэтому предусмотрена возможность установки в SID атрибута проверки только на запрет. Вот вам пример. Пусть ACL ресурса запрещает доступ к ресурсу группе Marketing. Если программа удаляет SID группы Marketing из мар кера пользователя, пользователь, казалось бы, загадочным, но довольно очевид Принцип минимальных привилегий ГЛАВА ным образом получает доступ к ресурсу! Поэтому не стоит удалять SID — лучше превратить его в запрещающий.

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

• Если программе заведомо не понадобится определенный уровень доступа, можно изменить соответствующие SID так, чтобы они работали только на запрет.

Например, экранной заставке администраторский доступ вообще не нужен, так что администраторские SID стоит переделать в запрещающие. «Родные» экран ные заставки Windows 2000/XP работают именно так.

• Если вы точно знаете, какому минимальному набору пользователей и групп необходим доступ к ресурсу, применяйте ограниченные SID. Так, если для до ступа к рассматриваемому ресурсу достаточно членства в группе Authenticated Users, назначьте ограниченный SID группы Authenticated Users. Это помешает недружелюбным программам, перехватившим ограниченный маркер, получить доступ к личным данным пользователей (например, криптографическим ключам), • Если приложение во время работы запускает на выполнение произвольный код, надежность которого заранее неизвестна, стоит использовать ограниченный маркер. Примеры таких приложений: почтовые программы (точнее, вложения) и программы обмена сообщениями (из-за возможности передачи файлов). Если приложение вызывает функции ShellExecute или CreateProcess для произволь ных файлов, настоятельно рекомендуем применять ограниченный маркер, Программа-пример, демонстрирующая возможности ограниченных маркеро i Передав ограниченный маркер в функцию CreateProcessAsUser, можно создать процесс с ограниченными правами и привилегиями. Это же верно в отношении вызова ImpersonateLoggedOnUser или SetThreadToken с той разницей, что в огра ниченном контексте безопасности вошедшего в систему пользователя работает поток, Вот программа, демонстрирующая, как на основе маркера текущего процесса создавать новый ограниченный маркер: удаляются все привилегии, кроме SeCbang^ NotifyPrivilege (она нужна всем учетным записям). Дтя этого устанавливается фл ir DISABLE_MAX_PRMLEGE, по для топкого выборочного отключения привилегий придется создать собственный список, не говоря уже о превращении SID-иден тификатора локального администратора в запрещающий.

/* Restrict.срр •/ // Создаем SID для группы BUILTIN\Administrators.

BYTE sidBuffer[256];

PSID pAdminSIO = (PSID)sidBuffer;

SID_IDENTIFIERJUJTHORITY SIDAuth = SECURITYJIT.AUTHORITY;

If (!AllocateAndInltializeSid( &SIDAuth, 2, SECURITY_BUILTIN_DOHAIN_RIO, Методы безопасного кодирования 204 Часть II DOMAIN_ALIAS_RID_ADMINS, О, О, О, О, О, О, &pAdminSID) ) { printf("AllocateAndInitializeSid Error Ku\n", GetLastError() );

return -1;

// Превращаем SID локального администратора в запрещающий SID.

SID_AKD_ATTRIBUTES SidToDisable[1 ];

SidToDisable[0].Sid = pAdminSID;

SidToOisable[0]. Attributes = 0;

// Получаем маркер текущего процесса.

HANDLE hOldToken = NULL;

if (! OpenProcessToken( GetCurrentProcessO, TOKEN _ASSIGfCPRIMARY | TOKEN.DUPLICATE | TOKEN_QUERY | TGKEN_ADJuST_DEFAULT, ihOldToken)) { printf("OpenProcessToken завершилась с ошибкой (Xlu)\n", GetLastErro'r() );

return -1;

I // Создаем на основе полученного маркера процесса ограниченный маркер.

HANDLE hNewToken = NULL;

if (! CreateRestrictedTokenfhOldToken, DISABLE_HAX_PRIVILEGE, 1, SidToDisable, 0, NULL, 0, NULL, ShNewToken)) { printf{"CreateRestrictedToken завершилась с ошибкой (Xlu)\n", GetLastErrorC) }:

return -1;

I if (pAdminSID) FreeSid(pAdminSID);

// Следующий код создает новый процесс // с ограниченным маркером.

PROCESS.INFORMATION pi;

STARTUPINFO si;

ZeroMemory(&si, sizeof(STARTUPINFO) );

si.cb = sizeof (STARTUPINFO);

si.lpDesktop = NULL;

// Задаем путь к Cmd.exe, чтобы быть уверенным, // что не выполняем "троянскую" версию Cmd.exe, Char szSysDir[MAX_PATH+1];

if (GetSystemDirectory(szSysDir,MAX_PATH)) { char szCmd[MAX_.PATH+1];

if (StringCchCopy(szCmd,MAX_PATH,szSysDir) == S_OK && ГЛАВА 7 Принцип минимальных привилегий StringCchCat(szCmd,MAX_PATH,"\\") == S_OK && StringCchCat(szCmd, MAX_PATHrcmd.exe") == S_OK) { tf(!CreateProcessAsUser( hNewtoken, szCmd, NULL, NULL,NULL, FALSE, CREATEJJEW.CONSOLE, NULL, NULL, &si,&pi)) printf("CreateProcessAsUser завершилась с ошибкой (Я1и)\п' GetLastErrorO );

CloseHandle(hOldToken);

CloseHandle(hNewToken);

return 0;

Примечание Невозможна сетевая аутентификация маркера с ограниченными SI D в качестве пользователя. Функция IsTokenResticted позволит выяснить, ограничен ли маркер, Внимание! Не меняйте в Restrict.cpp значение STARTUPINFOJpDesktop (в про грамме — NULL} на winstaO\\default. В противном случае при работе с сервером терминалов (Terminal Server) приложение будет выполняться в физической консоли пользователя, а не в сессии Terminal Server, из которой его запустили.

Полный листинг программы вы найдете в папке Secureco2\Chapter07. Программа создает новый экземпляр программы командного процессора — в нем можьо выполнять другие приложения для изучения их поведения в урезанном контек сте безопасности.

Если после выполнения демонстрационного приложения вы взглянете на со держимое маркера процесса (применив программу MyToken.cpp), то получите показанный далее результат. Как вы видите, SID группы Administrators стал запре щающим (deny-only), а все привилегии, кроме SeChangeNotifyPrivilege, удалены.

User NORTHWINDTRADERS\blake SIDS NORTHWINDTRADERS\Domain Users \Everyone BUILTIN\Administrators [DENY] BUILTIN\Users NT AUTHORITY\INTERACTIVE NT AUTHORITY\Authenticated Users Restricting SIDS None Часть II Методы безопасного кодирования Privileges SeChangeNotifyPrivilege (3) Следующий код создает новый процесс, применяя ограниченный маркер. Ана логично создают отдельные потоки. Здесь демонстрируется использование огра ниченного маркера в многопоточном приложении. Для создания потока вызыва ется функция ThreadFunc, она удаляет из маркера потока все привилегии, кроме Bypass Traverse Checking, а затем вызывает функцию DoThreadWork.

((include DWORD WINAPI ThreadFunc(LPVOID IpParam) { DWORD dwErr = 0;

try { if (!ImpersonateSelf(Securitylmpersonatlon)) throw GetLastError();

HANDLE hToken = NULL;

HANDLE hThread = GetCurrentThreadQ;

if ( OpenThreadTokenChThread, !

TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TQKEN.QUERY | TOKEN_IKPERSONATE, TRUE, ShToken» throw GetLastError{);

HANDLE hNewToken = NULL;

if (!CreateRestrictedToken(hToken, DISABLE_MAX_PRIVILEGE, 0, NULL, 0, NULL, 0, NULL, &HNewToken)) throw GetLastError();

if (!SetThreadToken(&hThread, hNewToken)) throw GetLastErrorO;

// DoThreadWork действует в "урезанном" контексте.

DoTh readWorkC hNewToken);

} catch(DWORD d) { dwErr = d;

!

if (dwErr == 0) RevertToSelfO;

return dwErr;

Принцип минимальных привилегий ГЛАВА void main() { HANDLE h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunc, NULL, CREATE_SUSPENDED, NULL);

if (h) ResumeThread(h);

I Применяйте политику ограниченного использования программ и Windows XP В Windows XP добавлена новая возможность под названием Software Restriction Policies (Политики ограниченного использования программ), или SAFER, создан ная для упрощения работы с ограниченными маркерами в приложениях. Мы по говорим о вопросах программирования для SAFER, а не администрирования. По дробнее об администрировании SAFER вы узнаете во встроенной справочной си стеме Windows XP, выполнив поиск по фразе Software Restriction Policies (в pvc ской версии — «Политики ограниченного использования программ*).

SAFER содержит ряд функций (они объявлены в Winsafer.h), которые упроща ют работу с маркерами с низкими привилегиями. Одна из них — SaferCompute TokenFromLevel Получая маркер в качестве аргумента, она модифицирует его, уре (ая права до заранее определенного уровня.

Показанный далее код демонстрирует создание нового процесса для выпол нения от имени NormalUser, учетной записи, не входящей ни в группу Administrators (Администраторы), ни в Power Users (Опытные пользователи). Файл с кодом есть в папке Secureco2\Chapter07. После выполнения программы используйте Му'Го ken.cpp для просмотра изменений SID и привилегий, Л SAFER.срр */ (tinclude ttinclude ^include «include «include void main() { SAFER_LEVEL_HANDLE hAuthzLevel;

// Допустимые программные уровни SAFER:

// SAFER_LEVELID_FULLYTRUSTED (полное доверие) // SAFER_LEVELID_NORMALUSER (обычный) // SAFER_LEVELID_CONSTRAINED (ограниченный) // SAFER_LEVELID_UNTRUSTED (ненадежный) // SAFEH_LEVELID_DISALLOWED (запрещенный) // Создаем уровень обычного пользователя, if (SaferCreateLevel(SAFER_SCOPEID_USER.

SAFER_LEVELin_NORMALUSER, О, &hAuthzLevel, NULL)) { Методы безопасного кодирования 208 Часть II // Создаем ограниченный маркер для дальнейшего использования.

HANDLE hToken = NULL;

if (SaferComputeTokenFromLeveK hAuthzLevel, // Описатель более безопасного уровня.

NULL, // Текущий маркер потока - NULL, &hToken, // Целевой маркер.

О, // Без флагов.

NULL)) { // Зарезервировано, // Определяем путь к Cmd.exe, чтобы быть уверенными в том, // что мы не выполняем "троянскую" версию Cmd.exe char szPath[MAX_PATH+1], szSysDir[MAX_PATH+1];

if (GetSystemDirectoryfszSysDir, sizeof (szSysDir))) { StringCbPrintf(szPath, sizeof (szPath), "Xs\\cmd.exe", szSysDir);

STARTUPINFO si;

ZeroMemoryUsi, sizeof (STARTUPINFO));

si.cb = sizeof(STARTUPINFO);

si.lpDesktop = NULL;

PROCESS.INFORMATION pi;

if (I CreateProcessAsUser( hToken, szPath, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, isi, & p i ) ) printf("CreateProcessAsUser завершилась с ошибкой (*1и)\п' GetLastErrorO );

SaferCloseLevel( hAuthzLevel);

'• Примечание Возможности SAFER очень обширны и не ограничиваются упро щением процедуры создания преопределенных маркеров и выполнением процессов в более безопасном контексте. Рассказ о потенциале SAFER, касающемся политик и развертьшания, выходит за рамки нашей книги, посвященной созданию защищенных приложений. Даже хорошо напи санное приложение может пасть жертвой атаки из-за неграмотного ад министрирования или неправильной установки. Поэтому необходимо, чтобы развертывающие приложение специалисты знали, как надежно и практично устанавливать и использовать технологии, в числе которых SAFER.

ГЛАВА 7 Принцип минимальных привилегий Полностью удаляйте ненужные привилегии В период Windows Security Push (когда сотрудники Microsoft работали не над но выми возможностями, а тестировали и проверяли безопасность старых) мы до бавили к Windows.Net Server 2003 новую функцию — удаление привилегии вы полняемого приложения. Тут есть небольшое отличие от SAFER: подразумевается «чистка» привилегий из первичного маркера процесса, а не порожденного пото ка. Преимущество заключается в том, что приложению запрещенные привилегии в принципе недоступны: как в штатном режиме, так и под атакой.

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

// RemPriv ffifndef SE_PRIVILEGE_REHOVED «define SE_PRIVIIEGE_REMOVED (0x00000004) ffendif DWORD RemovePrivs(LPCTSTR szPrivs[], DWORD cPrivs) { HANDLE hProcessToken = NULL;

if (!OpenProcessToken(GetCu rrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ShProcessToken)) return GetLastErrorO;

DWORD cbBuff = sizeof TOKEN_PRIVILEGES + (sizeof LUID_AN ^ATTRIBUTES * cPrivs);

char *pbBuff = new char[cbBuff];

PTOKENJ>RIVILEGES pTokPrivs = (PTOKEN_PRIVILEGES)pbBuff;

// Удаляем две привилегии.

pTokPrivs->PrivilegeCount = cPrivs;

for (DWORD i=0;

i < cPrivs;

i-н-) { LookupPrivilegeValue(NULL,szPrivs[i], &(pTokPrivs->Privileges[i].Luid));

pTokPrivs->Privileges[i].Attributes = SE.PRIVILEGEJOOVED;

// Удаляем привилегии.

BOOL fRet = AdjustTokenPrivileges(hProcessToken, FALSE, pTokPrivs, 0, NULL, NULL);

DWORD dwErr = GetLastErrorO;

ffifdef.DEBUG printf("AdjustTokenPrivileges() -> Kd\nGetLastError() -> Jd\n", fRet, 210 Часть II Методы безопасного кодирования dwErr);

ttendif if (pbBuff) delete [] pbBuff;

CloseHandle(hProcessToken);

return dwErr;

' int main(int argc, CHAR* argv[]) { LPCTSTR szPrivs[] = {SE_TAKE_OWNERSHIP_NAME, SE_DEBUG_NAME};

if (RemovePrivs(szPrivs, sizeof(szPrivs)/sizeof(szPrivs[0])) == 0) { // Круто! Работает!

} ' Если вы знакомы с AdjustTokenPrivileges, то поймете, что единственное измене ние заключается в появлении нового флага — SE_PRIVILIGE_REMOVED. Запомните:

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

Если вы создали службу для работы в Windows.NET Server 2003 и уверены, что ей никогда не потребуются определенные привилегии, желательно их удалить. Так как подобный код работает только в операционных системах, начиная с Win dows.NET Server 2003, перед его выполнением разумно вызвать функцию GetVer sionEx и выяснить версию ОС.

К примеру, в Windows.NET Server 2003 процесс LSA (LSASS.EXE) лишен приви легий, которые не нужны для выполнения системных задач:

• SeTakeOwnershipPrivUege;

• SeCreatePagefilePrivilege;

• SeLockMemoryPrivilege;

• SeAssignPrimaryTokenPrivilege;

• SelncreaseQuotaPrivilege;

• SelncreaseBasePriorityPrivilege-, • SeCreatePermanentPrivilege-.

• SeSystemEnvironmentPrwilege-, • SeUndockPrivilege-, • SeLoadDriverPriuilege;

• SeProfileSingleProcessPrivilege-.

• SeManageVolumePrivilege.

У службы Smartcard также удалены лишние привилегии:

• SeSecurityPriuilege;

• SeSystemtimePrivilege;

• SeDebugPrivilege;

• SeShutdoumPrivilege-.

• SeUndockPrivilege.

ГЛАВА 7 Принцип минимальных привилегий Доходит даже до того, что у некоторых компонентов убирают все привилегии, кроме SeChangeNotifyPrivilege, которая необходима для работы с NTFS. Следующий код предназначен именно для этого:

/* JettisonPrivs.cpp V «ifndef SE_PRIVILEGE_REMOVED # define SE_PHIVILEGE_REMOVED (0x00000004) ttendif Jtdefine SAKE_LUID(luid1,luid2) \ (luidl.LowPart == Iuid2. LowPart && \ Luidl.HighPart == luid2.HighPart) DWORD JettisonPrivsO { DWORD dwError = 0;

VOID* Tokenlnfo = NULL;

try { HANDLE hToken = NULL;

if (!OpenProcessToken( GetCu rrentProcess( ), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) throw GetLastErrorQ;

DWORD dwSlze=0;

if (!GetTokenInformation( hToken, TokenPrivileges, NULL, 0, &dwSize)) { dwError = GetLastError{);

if (dwError != ERROfl_INSUFFICIENT_BUFFER) throw dwError;

Tokenlnfo = new char[dwSize];

if (NULL == Tokenlnfo) throw ERROR_NOT_ENOUGH_MEHORY;

if (!GetTokenInformation( hToken, TokenPrivileges, Tokenlnfo, dwSize, SdwSize)) throw GetLastError();

212 Часть II Методы безопасного кодирования TOKEN_PRIVILEGES* pTokenPrivs = (TOKEN_PRIVILEGES*) Tokenlnfo;

// Эту привилегию удалять не нужно.

LUID luidChangeNotify;

LookupPrivilegeValue(NULL,SE_CHANGE_NOTIFY_NAHE, MuidChangeNotify);

Pages:     | 1 |   ...   | 2 | 3 || 5 | 6 |   ...   | 12 |



© 2011 www.dissers.ru - «Бесплатная электронная библиотека»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.