Продолжая разговор об аутентификации в семействе Windows NT/2000/XP/Server 2003, начавшийся с обсуждения GINA.DLL, на этот раз я расскажу о средствах, позволяющих контролировать процесс создания и изменения паролей пользователей. Проблема неустойчивых к взлому паролей, наверное, набила оскомину уже всем системным администраторам, заботящимся о безопасности информации как в сети, так и на отдельных компьютерах. Но, действительно, как объяснить пользователю, что пароли, совпадающие с именем учетной записи, а также 1, secret, sex и еще несколько всем известных вариантов, подвергают риску корпоративную информацию?
В состав операционных систем входят базовые средства для решения этой проблемы. Оснастка «Локальные политики безопасности» в MMC отчасти предоставляет необходимые инструменты. Рекомендуется разрешить политику Passwords must meet complexity requirements (см. Экран 1).
Это приведет к тому, что:
- пароли не будут содержать имя или любую часть полного имени пользователя;
- пароли пользователей должны будут иметь длину не менее шести символов;
- в составе паролей обязательны символы трех из четырех групп.
Тип символов | Пример |
Английские заглавные буквы | A, B, C, ... Z |
Английские прописные буквы | a, b, c, ... z |
Цифры | 0, 1, 2, ... 9 |
Специальные символы | $,!,%,^ |
Администратору может показаться, что требования к паролям, предъявляемые Windows, недостаточны. В этом случае можно воспользоваться API Password Filters, позволяющим расширять и изменять требования к политике безопасности, а также контролировать процесс изменения паролей. Кроме того, по тем или иным причинам может потребоваться отслеживать момент смены пароля пользователем. Впервые возможность применить это расширение появилась в Windows NT SP2.
В Platform SDK входит пример, реализующий модуль Password Filter (...SamplesWinBaseSecurityWinNTPwdFilt), который выполняет как стандартные ограничения, так и описанные выше. Когда приходит запрос на изменение пароля, LSA вызывает модули Password Filters, зарегистрированные в системе. Эти модули представляют собой dll, экспортирующие три функции.
Нужно иметь в виду, что механизм действует только по отношению к локальной базе SAM, т. е. если меняется пароль доменной учетной записи, то будут вызываться функции на модуле, работающем на соответствующем DC. Если же администратор меняет локальную учетную запись, то и модуль должен выполняться на локальном компьютере. По этой причине в функции модулей Password Filter не передается имя домена, в котором находится учетная запись.
В процессе изменения пароля каждый модуль вызывается дважды. Первый раз — для проверки подлинности идентификационной информации. Это делается вызовом функции PasswordFilter. А второй раз, после того как все фильтры подтвердили корректность этой информации, для извещения модуля фильтра об успешной операции вызывается функция PasswordChangeNotify (см. Рисунок 1).
Рисунок 1. Взаимодействие Password Filter и LSA. |
Описание модуля Password Filter
Модуль Password Filter устроен несложно. Он представляет собой стандартную библиотеку dll. Библиотека Password Filter, как уже отмечалось выше, экспортирует три функции с прототипами, приведенными в Листинге 1.
Листинг 1. Прототипы библиотеки Password Filter.
BOOLEAN NTAPI InitializeChangeNotify(); NTSTATUS NTAPI PasswordChangeNotify( PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword ); BOOLEAN NTAPI PasswordFilter( PUNICODE_STRING AccountName, PUNICODE_STRING FullName, PUNICODE_STRING Password, BOOLEAN SetOperation );
Процесс блокируется до тех пор, пока эти функции не вернут управление. Строковые параметры передаются в функции в виде указателей на структуру UNICODE_STRING. В заголовочном файле Ntsecapi.h она определяется следующим образом:
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING;
typedef LSA_UNICODE_STRING UNICODE_STRING, *PUNICODE_STRING;
И назначение этих функций, и смысл передаваемых им параметров достаточно очевидны.
InitializeChangeNotify. Подсистема LSA вызывает эту функцию, чтобы, с одной стороны, дать библиотеке возможность провести процесс инициализации, а с другой, получить от нее подтверждение о готовности к работе. Если данная функция возвращает FALSE, в ходе дальнейшей работы она вызываться не будет.
PasswordFilter. Данная функция вызывается в тот момент, когда предпринимается попытка изменения пароля. Это происходит в тех случаях, когда создается учетная запись, и пользователь или администратор изменяют пароль для уже существующей учетной записи. Если функция по тем или иным причинам возвращает FALSE, то LSA — значение ERROR_ILL_FORMED_PASSWORD. Если же процесс проверки пароля прошел успешно, то LSA поочередно вызывает остальные зарегистрированные в системе модули Password Filter.
В функцию передаются следующие параметры:
AccountName — [in] имя учетной записи, для которой выполняется проверка;
FullName — [in] полное имя пользователя;
Password — [in] новое значение пароля в виде открытого текста;
SetOperation — [in] TRUE, если пароль изменяется для уже существующей учетной записи;
FALSE — если создается новая учетная запись.
PasswordChangeNotify. Подсистема LSA вызывает эту функцию для каждого из зарегистрированных в системе модулей Password Filter, чтобы уведомить их о том, что проверка пароля прошла успешно, и он изменен. Т. е. названная функция вызывается уже после того, как новый пароль внесен в базу данных SAM.
В функцию передаются следующие параметры:
UserName — [in] имя учетной записи, для которой выполняется проверка;
RelativeId — [in] значение Relative identifier (RID) учетной записи, определенной в UserName. RID представляет собой часть SID, определяющую учетную запись в базе данных SAM;
NewPassword — [in] новое значение пароля в виде открытого текста учетной записи, определенной в UserName.
Что необходимо помнить при написании кода?
В MSDN приводится ряд требований, которые необходимо учитывать при написании модулей PasswordFilter.
- Информация передается в виде открытого текста. Передача паролей через сеть в таком виде может привести к перехвату паролей с помощью сетевых сканеров.
- Для всех областей динамической памяти перед уничтожением необходимо использовать функцию ZeroMemory, ведь функции освобождения памяти физически не уничтожают ее содержимое, а лишь высвобождают дескрипторы.
- Все буферы, которые передаются процедурам, доступны только для чтения.
- Все процедуры должны быть рассчитаны на работу в многопоточной среде. Необходимо использовать Critical Sections или другие методы синхронизации для доступа к данным.
- Фильтры паролей должны быть установлены как на PDC, так и на BDC Windows NT. Для Windows 2000 на все контроллеры доменов должны быть установлены фильтры паролей.
- Модули Password Filter выполняются в контексте учетной записи LocalSystem.
- Модули Password Filter поддерживают уведомления об изменении паролей, приходящие только от Windows 95, Windows 98, Windows Me, Windows NT, Windows 2000 и Windows XP, Windows 2003 Server.
Следуя этим правилам, администратор сможет избежать ситуации, при которой модуль станет новой «дырой» в системе безопасности операционной системы, вместо того чтобы послужить ее более надежной защитой.
Среда разработки
Пример, приведенный в статье, разработан и откомпилирован в MSVC 6.0. Для написания модуля PasswordFilter не нужны какие-либо дополнительные библиотеки, помимо входящих в MS Visual C++ 6.0 и старше. В проект необходимо включить заголовочный файл Ntsecapi.h.
Установка модуля Password Filter
Установить созданный фильтр несложно. Процедура установки состоит из трех этапов.
- Необходимо скопировать dll-библиотеку в папку Windows %SYSTEMROOT% или %SYSTEMROOT%SYSTEM32.
- Далее требуется изменить параметр Notification Packages в разделе реестра HKEY_LOCAL_MACHINESYSTEM CurrentControlSetControlLsa. Если такой параметр отсутствует, его необходимо добавить. Его тип - REG_MULTI_SZ. Если же он есть, то находящиеся в нем строки удалять нельзя, можно лишь добавить новую. Она должна представлять собой имя модуля Password Filter без расширения. Так, если наш модуль называется rtPwdFltr.dll, то в реестр следует добавить rtPwdFltr. Скорее всего, в списке будет одно значение - scecli. Эта библиотека реализует многие функции защиты, и удалять ее нельзя.
- Последнее действие состоит в изменении локальной политики безопасности. Необходимо открыть оснастку Local Security Policy, выбрать Account Policy, Password Policy и установить значение Passwords must meet complexity requirements в Enabled.
Eсли написанный модуль работает некорректно и приводит к сбою системы, можно воспользоваться консолью восстановления (потребуется пароль учетной записи администратора) и удалить файл модуля. Затем компьютер следует перезагрузить.
Александр Эпштейн — независимый разработчик и консультант по проблемам низкоуровневого программирования; руководил департаментами разработки программного обеспечения в нескольких крупных российских компаниях. С ним можно связаться по адресу: alex_ep@hotmail.com.