АйТи Фреш
Главная / Статьи / Windows и рабочие места
Windows и рабочие места

Ужесточили пароли админам, а они остались прежними: как на самом деле считается результирующая PSO в Active Directory

Автор: Семёнов Евгений Сергеевич, директор ООО «АйТи-Фреш» · · ~21 мин чтения
Ужесточили пароли админам, а они остались прежними: как на самом деле считается результирующая PSO в Active Directory
Иллюстрация к статье «Ужесточили пароли админам, а они остались прежними: как на самом деле считается результирующая PSO в Active Directory».

После аудита ИБ в домене создают для админов гранулярную парольную политику, ставят ей Precedence=10, привязывают к группе и отчитываются. Через месяц я делаю выборочную проверку, и она показывает — у двух доменных админов пароль как был восемь символов на 42 дня, так и остался. В контейнере политик всё на месте, приоритет самый низкий из всех, значит самый сильный. А учётка живёт по политике с приоритетом 100. Разбираю, почему так происходит, в каком порядке контроллер домена на самом деле выбирает результирующую политику, как за десять минут снять по домену реальную картину, и как я строю схему PSO, чтобы она не разъезжалась через год.

Приоритет — это последнее, на что смотрит контроллер домена

Главная ошибка в том, что администратор мысленно выстраивает все PSO домена в один список и сортирует по msDS-PasswordSettingsPrecedence. Так это не работает. Числа приоритета сравниваются только внутри одного уровня, и уровня этих ровно два: политики, назначенные объекту пользователя напрямую, и политики, прилетевшие через глобальные группы безопасности. Между уровнями никакого сравнения чисел нет — прямое назначение выигрывает всегда, хоть с приоритетом 100, хоть с приоритетом 1000.

Microsoft описывает алгоритм в документации на Get-ADUserResultantPasswordPolicy предельно прямо: если с пользователем связана ровно одна PSO — она и есть результирующая; если связано несколько, результирующей становится та, что применена к пользователю напрямую; если напрямую применено несколько — выигрывает наименьшее значение msDS-PasswordSettingsPrecedence, и это событие пишется предупреждением в журнал службы каталогов; и только если прямых назначений нет вообще — сравниваются PSO глобальных групп безопасности, членом которых является пользователь, и там уже побеждает меньшее число.

Отдельная тонкость, о которую спотыкаются при попытке «разрулить приоритетами»: если два кандидата на одном уровне имеют одинаковый Precedence, побеждает объект с меньшим GUID. То есть исход определяется тем, какую политику случайно раньше создали. Это не гипотетика, я такое видел в реальных доменах: два PSO с Precedence=50, обе применены к пересекающимся группам, и результат для конкретного человека — лотерея, которую невозможно объяснить руководству.

Прямое назначение PSO на пользователя — это не баг и не самодеятельность предшественника. Это штатный механизм, Microsoft называет такие политики exceptional PSO: способ вывести одного человека из-под групповой политики. Проблема не в механизме, а в том, что такие исключения никто не документирует и не снимает, и через три года они молча съедают ваше ужесточение. Архивное руководство Microsoft отдельно предупреждает ещё об одном эффекте: при копировании учётной записи атрибут msDS-PSOApplied не копируется, а членство в группах копируется. Новый сотрудник, созданный копированием «исключительного» коллеги, молча получает групповую политику вместо той, что была у образца.

Precedence=10 у групповой политики НЕ перебивает Precedence=100, назначенный пользователю напрямую. Сначала уровень назначения, только потом число.

Где эти политики физически лежат и какие атрибуты смотреть

PSO — это объект класса msDS-PasswordSettings, живущий в контейнере CN=Password Settings Container,CN=System,DC=... Контейнер создаётся вместе с доменом, его нельзя переименовать, перенести или удалить. В ADAC он лежит в узле System → Password Settings Container. Требования: сам механизм FGPP появился на функциональном уровне домена Windows Server 2008, но актуальная инструкция Microsoft Learn для текущих версий Windows Server указывает в предварительных требованиях уровень не ниже Windows Server 2012 — на него и ориентируюсь; членство в Domain Admins для создания PSO (право можно делегировать) и установленный RSAT — либо ADAC, либо модуль Active Directory для PowerShell.

Связей четыре, и путать их не надо. msDS-PSOAppliesTo — атрибут самой политики, перечисляет её субъекты. msDS-PSOApplied — обратная ссылка на объекте пользователя или группы, показывает, какие PSO назначены прямо на этот объект. msDS-PasswordSettingsPrecedence — то самое число. И msDS-ResultantPSO — конструируемый атрибут пользователя, который контроллер домена вычисляет на лету по алгоритму выше. Именно его отдаёт Get-ADUserResultantPasswordPolicy и показывает пункт View Resultant Password Settings в панели задач ADAC.

Отдельно про то, к чему PSO вообще применимы: только к объектам пользователей (и inetOrgPerson) и к глобальным группам безопасности. Ни к подразделениям, ни к компьютерам. Привязать PSO к доменно-локальной, универсальной или группе рассылки технически можно, но при расчёте результирующей политики такие связи игнорируются. Если у вас люди разложены по OU и вы рассчитываете, что политика приедет по дереву как обычная GPO, — не приедет. Нужны теневые группы: глобальная группа безопасности, в которую вручную или скриптом складываются пользователи из нужного OU.

Проверить, что у вас вообще есть, — одна команда. Я всегда начинаю разбор именно с неё, до любых гипотез:

Get-ADFineGrainedPasswordPolicy -Filter * |
  Select-Object Name, Precedence, MinPasswordLength, MaxPasswordAge,
                LockoutThreshold, ComplexityEnabled,
                @{n='AppliesTo';e={ $_.AppliesTo -join '; ' }} |
  Sort-Object Precedence | Format-Table -AutoSize
PSO, созданные в собственном, а не в стандартном Password Settings Container, логикой Resultant Set of Policy НЕ учитываются вообще. Такой объект выглядит настроенным и не делает ничего.
Ужесточили пароли админам, а они остались прежними: как на самом деле считается результирующая PSO в Active Directory — схема
Схема к статье. Открыть схему в полном размере

Разбор кейса: «Гофромир», производство упаковки, 46 рабочих мест

Производитель гофроупаковки, домен gofromir.local, два контроллера на Windows Server 2022 (DC01 и DC02), функциональный уровень домена 2016, 58 включённых учёток пользователей: 46 сотрудников офиса и производства плюс технические записи под 1С, сканеры и станки. Административные права у пятерых — трое айтишников и двое совместителей из бухгалтерии и технологической службы, которым когда-то «временно» выдали доступ. Пришли после проверки ИБ со стороны крупного сетевого заказчика с формулировкой «привилегированные учётные записи должны иметь пароль не короче 16 символов и срок жизни не более 90 дней». Задача выглядела на полчаса.

Сделали ровно то, что предписывает документация. Создали политику и привязали её к группе SEC-Admins, в которой лежали все пятеро:

$p = @{
  Name                     = 'PSO-Admins-Strict'
  Precedence               = 10
  ComplexityEnabled        = $true
  MinPasswordLength        = 16
  MinPasswordAge           = '1.00:00:00'
  MaxPasswordAge           = '90.00:00:00'
  PasswordHistoryCount     = 24
  LockoutThreshold         = 5
  LockoutDuration          = '00:30:00'
  LockoutObservationWindow = '00:30:00'
  ReversibleEncryptionEnabled     = $false
  ProtectedFromAccidentalDeletion = $true
}
New-ADFineGrainedPasswordPolicy @p
Add-ADFineGrainedPasswordPolicySubject PSO-Admins-Strict -Subjects 'SEC-Admins'

Через месяц контрольная проверка. Из пяти админов трое сменили пароль на длинный, двое — нет, и система у них смены не требовала. Прогнали Get-ADUserResultantPasswordPolicy по всем пятерым, и картина стала очевидной за полминуты: у троих результирующая PSO-Admins-Strict, у двоих — PSO-Legacy-Integration с Precedence=100, MinPasswordLength=8, MaxPasswordAge=42.00:00:00 и LockoutThreshold=0.

PS C:\> Get-ADUserResultantPasswordPolicy -Identity a.morozov

Name                : PSO-Legacy-Integration
Precedence          : 100
MinPasswordLength   : 8
MaxPasswordAge      : 42.00:00:00
LockoutThreshold    : 0
ComplexityEnabled   : True
AppliesTo           : {CN=a.morozov,OU=IT,DC=gofromir,DC=local,
                       CN=svc.1c-exchange,OU=Service,DC=gofromir,DC=local}
DistinguishedName   : CN=PSO-Legacy-Integration,CN=Password Settings Container,
                      CN=System,DC=gofromir,DC=local

Причина нашлась в истории объектов. В 2021 году, когда настраивали обмен 1С с EDI-площадкой торговой сети, эти две учётки работали ещё и как технические — их прописали в планировщик и в строку подключения. Чтобы пароль не протухал каждые 42 дня и обмен не вставал, тогдашний админ назначил на них PSO напрямую. Не на группу — на объекты пользователей. Атрибут msDS-PSOApplied у обоих был непустой. Прямое назначение всегда бьёт групповое, и никакой Precedence=10 тут не помогает.

Попутно вскрылось ещё два сюрприза. Первый: политика PSO-Contractors лежала в самодельном контейнере CN=PSO-External,CN=System — кто-то аккуратно «навёл порядок» и вынес её отдельно. Логика вычисления результирующей политики такие контейнеры игнорирует, поэтому шесть учёток подрядчиков (наладчики линий и внешний бухгалтер) два года жили по Default Domain Policy с семью символами, при том что в отчётах для проверяющих значились под усиленной политикой. Второй: две политики с одинаковым Precedence=50 на пересекающихся группах — исход для конкретного пользователя определялся GUID. Итог по домену: шесть PSO, реально влияли на кого-либо три.

Чинили так. Прямые назначения сняли через Remove-ADFineGrainedPasswordPolicySubject, технические функции с персональных учёток вынесли на отдельные сервисные записи (для одной подошла gMSA, вторую оставили обычной учёткой с длинным паролем и своей PSO с приоритетом 20). PSO-Contractors пересоздали в штатном контейнере. Дубли по приоритету развели на 40 и 60. После этого повторный прогон дал ожидаемое: у всех пяти админов результирующая PSO-Admins-Strict, у подрядчиков — PSO-Contractors, все принудительные смены прошли в течение недели.

Если у вас в домене живут учётки, которые одновременно и персональные, и технические, — ищите прямые назначения PSO именно на них. В моей практике это источник проблемы номер один.
Цифры и версии: Разбор кейса: «Гофромир», производство упаковки, 46 рабочих мест — схема
Цифры и версии: Разбор кейса: «Гофромир», производство упаковки, 46 рабочих мест. Открыть схему в полном размере

Как за десять минут снять реальную картину по домену

Первым делом — не гипотезы, а факты. Нужны три среза: какие PSO вообще есть, кому что назначено напрямую и какая политика реально действует на привилегированных пользователей. Прямые назначения удобнее всего собирать обходом самих политик по атрибуту AppliesTo с проверкой класса объекта — так сразу видно, где субъектом оказался человек, а не группа:

# 1. Все прямые назначения PSO на объекты пользователей
Get-ADFineGrainedPasswordPolicy -Filter * -Properties msDS-PSOAppliesTo | ForEach-Object {
    $pso = $_
    foreach ($dn in $pso.AppliesTo) {
        $o = Get-ADObject -Identity $dn -Properties objectClass
        if ($o.objectClass -eq 'user') {
            [pscustomobject]@{
                PSO        = $pso.Name
                Precedence = $pso.Precedence
                DirectUser = $o.Name
            }
        }
    }
} | Format-Table -AutoSize

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

# 2. Что реально действует на привилегированные учётки
$admins = Get-ADGroupMember 'Domain Admins' -Recursive |
          Where-Object objectClass -eq 'user'

foreach ($a in $admins) {
    $r = Get-ADUserResultantPasswordPolicy -Identity $a.SamAccountName -ErrorAction SilentlyContinue
    [pscustomobject]@{
        User        = $a.SamAccountName
        EffectivePolicy = if ($r) { $r.Name } else { 'Default Domain Policy' }
        MinLength   = if ($r) { $r.MinPasswordLength } else { (Get-ADDefaultDomainPasswordPolicy).MinPasswordLength }
        MaxAge      = if ($r) { $r.MaxPasswordAge }    else { (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge }
    }
} | Sort-Object EffectivePolicy | Format-Table -AutoSize

Важный момент по чтению вывода: если Get-ADUserResultantPasswordPolicy не вернул ничего — это не ошибка. Это значит, что с пользователем не связана ни одна PSO и он живёт по парольной политике домена, которую надо смотреть отдельно через Get-ADDefaultDomainPasswordPolicy. Именно поэтому в скрипте выше есть ветка с подстановкой доменных значений: без неё половина людей просто выпадет из отчёта, а вы решите, что у них всё хорошо. И ещё практическая деталь для русскоязычных доменов: если контроллер разворачивали с русской локалью, группа называется «Администраторы домена», и Get-ADGroupMember 'Domain Admins' вернёт ошибку. Надёжнее обращаться к группе по известному RID 512: Get-ADGroup -Filter "SID -eq '$((Get-ADDomain).DomainSID)-512'".

Если PowerShell под рукой нет или нужно показать картинку не-техническому человеку — в ADAC есть штатный путь: открыть dsac.exe, найти пользователя, в панели задач выбрать View Resultant Password Settings. Откроется свойства той самой политики, которая действует. Это ровно тот же msDS-ResultantPSO, только в графике. Ещё один способ проверить точечно — запросить конструируемый атрибут напрямую:

Get-ADUser a.morozov -Properties 'msDS-ResultantPSO','msDS-PSOApplied' |
  Select-Object Name,'msDS-ResultantPSO','msDS-PSOApplied'
msDS-ResultantPSO — конструируемый атрибут. Ключ -Properties * его не вернёт, имя надо указывать явно. Это регулярно вводит в заблуждение при написании своих отчётов.

Пять ловушек, из-за которых результирующая политика не та, что вы думаете

Первая — основная группа. Domain Users у большинства учёток является primary group, а primary group не отображается в атрибуте memberOf. Если вы собираете отчёт скриптом через memberOf и надеетесь так вычислить, какие PSO прилетают человеку через группы, вы гарантированно промахнётесь мимо политики, привязанной к Domain Users. Как именно контроллер домена учитывает основную группу, в документации расписано скупо, поэтому я не гадаю, а сверяю отчёт с выводом Get-ADUserResultantPasswordPolicy для пары учёток. Ещё один довод в пользу того, чтобы не изобретать логику, а спрашивать у DC готовый ответ.

Вторая — вложенные группы. Здесь единого мнения нет, и я честно скажу: в документации Microsoft поведение результирующей PSO при вложенности глобальных групп внятно не описано, а форумные обсуждения и сторонние руководства расходятся. Я это просто не использую: PSO привязываю к плоской группе, куда пользователи входят напрямую, и проверяю результат командой. Один запуск Get-ADUserResultantPasswordPolicy надёжнее любой теории про вложенность.

Третья — блокировка учётной записи. Многие помнят, что PSO задаёт длину и срок пароля, и забывают, что LockoutThreshold, LockoutDuration и LockoutObservationWindow тоже приходят из результирующей PSO. Если человек попал под политику с LockoutThreshold=0, у него блокировки нет вообще, сколько бы ни было настроено на уровне домена. Ровно этот случай был в «Гофромире»: у двух админов, заходивших снаружи через RDP-шлюз, счётчик неудачных входов не работал, и брутфорс по ним шёл бесконечно. Формально политика паролей была «усилена», фактически — самые ценные учётки оказались слабее рядовых.

Четвёртая — область действия. PSO не применяются к объектам компьютеров, к подразделениям и к интервалу автоматической смены пароля управляемых сервисных учётных записей (MSA/gMSA). Не применяются они и к локальным учётным записям на серверах — это вообще другая история, там нужна политика локальной безопасности или отдельная GPO. И пятая, самая обидная: флаг «срок действия пароля не ограничен» в userAccountControl отменяет MaxPasswordAge любой PSO — так же, как отменял его для Default Domain Policy. Ужесточили политику, проверили результирующую, всё сходится — а пароль всё равно не меняется, потому что на объекте стоит PasswordNeverExpires. Проверяйте его отдельным запросом, он не виден в выводе результирующей политики.

Проверьте отдельно командой `Search-ADAccount -PasswordNeverExpires -UsersOnly`. В доменах, которым больше пяти лет, там почти всегда находятся сюрпризы среди привилегированных учёток.
Памятка: Пять ловушек, из-за которых результирующая политика не та, что вы думаете — схема
Памятка: Пять ловушек, из-за которых результирующая политика не та, что вы думаете. Открыть схему в полном размере

Как я проектирую схему PSO, чтобы она не разъехалась через год

Правило первое и главное: никаких прямых назначений на объекты пользователей. Совсем. Если нужно исключение для одного человека — создаётся группа под это исключение, пусть даже с единственным членом, и PSO привязывается к ней. Разница принципиальная: группа видна в обычном аудите членства, у неё есть имя и описание, её попадание в отчёт объяснимо. Прямое назначение не видно нигде, кроме атрибута msDS-PSOApplied, который никто никогда не смотрит.

Правило второе: приоритеты назначаю с шагом 10 — 10, 20, 30, 40. Не 1, 2, 3. Через год понадобится вклинить политику между двумя существующими, и при шаге 1 придётся перенумеровывать всё, что почти всегда делается второпях и с ошибкой. И никогда не оставляю два PSO с одинаковым приоритетом: это либо ошибка проектирования, либо тикающая лотерея по GUID.

Правило третье: имя PSO содержит имя целевой группы, а поле Description — дату создания, номер задачи и фамилию того, кто согласовал. PSO-Admins-Strict привязан к SEC-Admins, PSO-Contractors — к SEC-Contractors. Когда через два года новый администратор откроет контейнер, ему не придётся раскапывать AppliesTo, чтобы понять замысел. Плюс обязательно ProtectedFromAccidentalDeletion — политику удаляют случайно чаще, чем кажется, и обнаруживается это только на следующем аудите.

Правило четвёртое, про честность: FGPP — не панацея, и продавать её как полное решение парольной безопасности нельзя. Она умеет только длину, сложность в понимании Windows, историю, сроки и блокировку. Она не умеет запрещать словарные и скомпрометированные пароли — «Зима2026!» проходит проверку сложности в любой PSO. Для этого нужен отдельный слой: Microsoft Entra Password Protection с агентами на контроллерах домена или сторонний парольный фильтр. И FGPP не имеет режима аудита: применили — и оно сразу действует, поэтому перед ужесточением обязательно прогоняйте отчёт по результирующим политикам, иначе узнаете о проблеме от пользователей в понедельник утром.

И про приоритеты работ. Если ресурсов мало, начинайте не с рядовых сотрудников. Первая волна — административные группы и всё, что доступно снаружи: RDP-шлюз, VPN, веб-публикации. Вторая — сервисные учётные записи, у них обычно самая печальная картина и одновременно самая высокая цена компрометации. Рядовых пользователей в первой итерации можно отложить: доменной политики с восемью-десятью символами и включённой сложностью им хватит, пока вы закрываете действительно опасные места.

У FGPP нет режима аудита: изменение вступает в силу мгновенно. Отчёт по msDS-ResultantPSO до применения — это не бюрократия, а единственный способ узнать, кого вы сейчас заблокируете.

План на первый час

Порядок действий такой. Выгрузить все PSO домена с приоритетами и субъектами. Найти прямые назначения на объекты пользователей — это ваш главный кандидат в виновники. Прогнать результирующую политику по членам Domain Admins, Enterprise Admins, Schema Admins и по всем, у кого есть доступ снаружи. Отдельно проверить контейнер System на предмет самодельных Password Settings Container. Отдельно снять список учёток с PasswordNeverExpires. Это буквально час работы, из которого сорок минут — чтение вывода.

Дальше по результатам. Если нашлись прямые назначения — не спешите их сносить одной командой. Сначала выясните, зачем их поставили: за прямым назначением почти всегда стоит техническая функция на персональной учётке. Снесёте политику — через 42 дня пароль протухнет и встанет обмен, а виноваты будете вы. Правильная последовательность: вынести техническую функцию на сервисную учётку или gMSA, убедиться, что интеграция работает, и только потом снимать назначение.

Если нашёлся PSO в самодельном контейнере — пересоздайте его в штатном Password Settings Container и предупредите затронутых пользователей, что при следующей смене пароля требования вырастут. И заложите в регламент квартальную проверку: те же два скрипта, десять минут, результат в тикет. Домены разъезжаются не одномоментно, а по одному исключению за раз — регулярная сверка ловит это на первом же.

Отдельно скажу, где риск преувеличен. Сама по себе схема FGPP надёжная: она работает с Windows Server 2008, и описание алгоритма в справке по командлету для Windows Server 2025 совпадает с архивным руководством. PSO — обычные объекты домена и реплицируются вместе с ним. Все проблемы, которые я разбираю в этой статье, рукотворные: исключения без документации, дубликаты приоритетов и «наведение порядка» в контейнерах. Чинятся они не за деньги, а за внимание.

Не снимайте прямое назначение PSO, пока не разобрались, какая интеграция за ним стоит. Через 42 дня протухший пароль остановит обмен, и связь с вашей правкой уже никто не проведёт.
Порядок действий: План на первый час — схема
Порядок действий: План на первый час. Открыть схему в полном размере

Частые вопросы

Почему PSO с Precedence=10 не сработала, а сработала со 100?

Потому что политика со 100 назначена пользователю напрямую, а с 10 — прилетает через группу. Контроллер домена сначала смотрит уровень назначения и только потом сравнивает числа. Прямое назначение выигрывает всегда, независимо от приоритета. Проверьте атрибут msDS-PSOApplied на объекте пользователя.

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

`Get-ADUserResultantPasswordPolicy -Identity <sam>` в PowerShell или View Resultant Password Settings в панели задач ADAC. Оба показывают одно и то же — конструируемый атрибут msDS-ResultantPSO. Если команда не вернула ничего, значит на пользователя не действует ни одна PSO и работает парольная политика домена: смотрите Get-ADDefaultDomainPasswordPolicy.

Что будет, если у двух PSO одинаковый Precedence?

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

Можно ли назначить PSO на подразделение (OU)?

Нет. Гранулярные парольные политики применяются только к объектам пользователей (и inetOrgPerson) и к глобальным группам безопасности. Для OU нужна теневая глобальная группа, в которую пользователи из этого подразделения складываются вручную или скриптом по расписанию.

Почему PSO не действует, хотя она создана и субъекты назначены?

Три частые причины. Политика лежит в самодельном контейнере, а не в штатном Password Settings Container — логика RSoP такие объекты игнорирует. Субъектом назначена доменно-локальная или универсальная группа вместо глобальной — такие связи при расчёте игнорируются. Либо на учётке стоит флаг PasswordNeverExpires, который отменяет MaxPasswordAge из любой политики.

Настройки блокировки учётной записи тоже берутся из PSO?

Да. LockoutThreshold, LockoutDuration и LockoutObservationWindow приходят из результирующей PSO и перекрывают доменные значения. Если пользователь попал под политику с LockoutThreshold=0, блокировки у него нет вообще, сколько бы ни было настроено в Default Domain Policy.

Сохранится ли исключительная PSO, если создать нового сотрудника копированием учётки?

Нет. При копировании пользователя атрибут msDS-PSOApplied не переносится, а членство в группах переносится. Новая учётка получит групповую политику, а не прямое назначение образца. Это ещё один довод назначать исключения через отдельную группу.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 💬 MAX: +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи