排查 Windows Server 上的 AD FS 中的帐户锁定问题

本文提供了在 Windows Server 上的 Microsoft Active Directory 联合身份验证服务 (AD FS) 中排查帐户锁定问题的步骤。

原始产品版本: Windows Server 2019、Windows Server 2016、Windows Server 2012 R2、Windows Server 2012
原始 KB 数: 4471013

你可能会在 Windows Server 上的 AD FS 中遇到帐户锁定问题。 若要排查此问题,请先检查以下几点:

使用 Connect Health 生成用户登录活动的数据

可以使用 Connect Health 生成有关用户登录活动的数据。 Connect Health 生成有关 AD FS 场上发生的顶级错误密码尝试的报告。

请参阅本文的信息,以分析错误密码尝试的用户帐户和 IP 列表。 然后,转到 “分析受密码尝试错误影响的帐户的 IP 和用户名”。

从 AD FS 和 Web 应用程序代理服务器收集 AD FS 事件日志

步骤 1:从 AD FS 和 Web 应用程序代理 服务器收集 AD FS 事件日志

若要收集事件日志,必须先配置 AD FS 服务器进行审核。 如果有 AD FS 场的负载均衡器,则必须在场中的每个 AD FS 服务器上启用审核。 无需在 Web 应用程序代理 服务器上配置审核。

若要配置 AD FS 服务器进行审核,可以使用以下方法:

步骤 2:搜索 AD FS 日志

对于 Windows Server 2012 R2 或 Windows Server 2016 AD FS,请在所有 AD FS 服务器的安全事件日志中搜索“事件 ID 411 源 AD FS 审核”事件。 请注意有关“411 事件”的以下信息:

  • 可以下载 ADFS 帐户锁定和错误的 Cred 搜索(AD FSBadCredsSearch.ps1)PowerShell 脚本,以搜索 AD FS 服务器中的“411”事件。 该脚本提供一个 CSV 文件,其中包含提交者的 UserPrincipalName、IP 地址,以及向 AD FS 场提交所有错误的凭据提交的时间。 在 Excel 中打开 CSV 文件,并按用户名、IP 地址或时间快速筛选。
  • 这些事件包含目标用户的用户主体名称(UPN)。
  • 这些事件包含消息“令牌验证失败”消息,该消息指出该事件指示密码尝试错误还是帐户锁定。
  • 如果服务器显示“411”事件,但 IP 地址字段不在事件中,请确保已将最新的 AD FS 修补程序应用到服务器。 有关详细信息,请参阅 MS16-020:用于解决拒绝服务的Active Directory 联合身份验证服务安全更新:2016 年 2 月 9 日。

对于 Windows Server 2008 R2 或 Windows Server 2012 AD FS,你将没有必要的事件 411 详细信息。 请改为下载并运行以下 PowerShell 脚本,以关联安全事件 4625(密码尝试错误)和 501(AD FS 审核详细信息),以查找有关受影响的用户的详细信息。

  • 可以下载 ADFS 安全审核事件分析器(ADFSSecAuditParse.ps1) PowerShell 脚本,以搜索 AD FS 服务器中的事件。 该脚本提供一个 CSV 文件,其中包含提交者的 UserPrincipalName、IP 地址,以及向 AD FS 场提交所有错误的凭据提交的时间。
  • 还可以使用此方法调查“411”事件中用户成功的连接。 可以在 AD FS“501”事件中搜索更多详细信息。
  • 运行 PowerShell 脚本以搜索事件时,传递“411”事件中标识的用户的 UPN,或按帐户锁定报告进行搜索。
  • 恶意提交者的 IP 地址显示在“501”事件的两个字段中之一。
  • 对于基于 Web 的方案和大多数应用程序身份验证方案,恶意 IP 将位于 x-ms-client-ip 字段中。

分析受密码尝试错误影响的帐户的 IP 和用户名

枚举 IP 地址和用户名后,确定用于访问意外位置的 IP。

是否尝试从外部未知 IP 进行?

使用最新修补程序更新 AD FS 服务器

若要确保 AD FS 服务器具有最新功能,请为 AD FS 和 Web 应用程序代理服务器应用最新的修补程序。 此外,Windows Server 2012 R2 上需要修补程序 3134222 才能在事件 411 中记录稍后使用的 IP 地址。

检查 Extranet 锁定是否已启用

使用 Get-ADFSProperties 检查是否启用了 Extranet 锁定。

检查锁定状态的步骤

对于 Windows Server 2012 R2 或更高版本

智能锁定是一项新功能,即将在 AD FS 2016 和 2012 R2 中通过更新提供。 在功能可用后,此部分将更新为启用智能锁定的相应步骤。 然后,转到 “检查 Extranet 锁定”和“内部锁定阈值”。

对于 Windows Server 2008 R2 Windows 或更高版本

建议将 AD FS 服务器升级到 Windows Server 2012 R2 或 Windows Server 2016。 有关详细信息,请参阅升级到 Windows Server 2016 中的 AD FS。 然后,按照 Windows Server 2012 R2 或更高版本的步骤进行操作。

步骤 1:检查 Extranet 锁定和内部锁定阈值

确保正确配置 Extranet 锁定和内部锁定阈值。 有关详细信息,请参阅 建议的安全配置。 通常, ExtranetLockoutThreshold 应小于 AD 的锁定阈值,以便用户仅锁定 Extranet 访问,而不会在 Active Directory 中锁定内部访问。

步骤 2:启用新式身份验证和基于证书的身份验证

建议启用新式身份验证、基于证书的身份验证,以及此步骤中列出的其他功能,以降低暴力攻击的风险。

部署新式身份验证

除了删除当前通过 Exchange Online 使用的攻击途径之一外,为 Office 客户端应用程序部署新式身份验证使组织能够受益于多重身份验证。 Windows、iOS 和 Android 平台中的所有最新Office 应用复制都支持新式身份验证。

有关详细信息,请参阅 如何为 Office 365 部署新式身份验证。

由于尽管我们的主动和反应防御措施,但基于用户名和密码的访问请求将继续易受攻击,因此组织应计划尽快采用基于密码的访问方法。

以下非基于密码的身份验证类型可用于 AD FS 和 Web 应用程序代理。

  • 基于证书的身份验证

    当基于证书的身份验证用作用户名和密码访问的替代方法时,用户帐户和访问权限将受到以下保护:

    • 由于用户不通过 Internet 使用其密码,因此这些密码更容易泄露。 可以在防火墙中完全阻止用户名和密码终结点。 这消除了锁定或暴力攻击的攻击途径。

    • 即使用户名和密码终结点在防火墙中可用,导致锁定的恶意用户名和密码请求也不会影响使用证书的访问请求。 因此,将保留合法用户的访问权限。

      有关 Microsoft Entra ID 和 Office 365 的基于证书的身份验证的详细信息,请参阅 此Microsoft Entra 标识博客文章

  • Azure 多重身份验证 (MFA)

    Azure MFA 是另一种基于密码的访问方法,可以采用与基于证书的身份验证相同的方式使用,以避免完全使用密码和用户名终结点。

    Azure MFA 可用于在以下方案中保护帐户。

    方案 优点
    通过 Extranet 使用 Azure MFA 作为其他身份验证 将 Azure MFA 或任何其他身份验证提供程序添加到 AD FS,并要求将其他方法用于 Extranet 请求,从而通过使用被盗或暴力破解的密码来保护帐户免受访问。 这可以在 AD FS 2012 R2 和 2016 中完成。
    使用 Azure MFA 作为主要身份验证 这是 AD FS 2016 中的一项新功能,使用 Azure MFA 而不是密码启用无密码访问。 这可防范密码泄露和锁定。

    有关如何使用 AD FS 配置 Azure MFA 的详细信息,请参阅 配置 AD FS 2016 和 Azure MFA

  • Windows Hello for Business

    Windows 10 中提供了Windows Hello 企业版。 Windows Hello 企业版基于绑定到用户和设备的强大加密密钥,从 Extranet 中启用无密码访问。

    Windows Server 2016 中的 AD FS 支持Windows Hello 企业版。 请参阅通过Windows Hello 企业版对不使用密码的标识进行身份验证。

步骤 3:禁用旧式身份验证和未使用的终结点

禁用 EAS 客户端通过 Exchange Online 使用的旧终结点,如下所示:

/adfs/services/trust/13/usernamemixed endpoint

注意

执行此操作可能会中断某些功能。 但是,它可以帮助减少可供攻击者利用的图面向量。 此外,建议禁用未使用的终结点。

检查是否解决了问题。

确保在服务或应用程序中更新凭据

如果用户帐户用作服务帐户,则可能不会更新服务或应用程序的最新凭据。 在这种情况下,服务可能会继续使用错误的凭据进行身份验证。 这会导致锁定条件。

若要解决此问题,请检查服务或应用程序中的服务帐户配置,确保凭据正确。 否则,请按照下一步操作。

清除应用程序中的缓存凭据

如果某个应用程序中缓存了用户凭据,重复的身份验证尝试可能会导致帐户锁定。 若要解决此问题,请清除应用程序中的缓存凭据。 检查是否解决了问题。

PARAM ($PastDays = 1, $PastHours)
#************************************************
# ADFSBadCredsSearch.ps1
# Version 1.0
# Date: 6-20-2016
# Author: Tim Springston [MSFT]
# Description: This script will parse the ADFS server's (not proxy) security ADFS
#  for events which indicate an incorrectly entered username or password. The script can specify a
#  past period to search the log for and it defaults to the past 24 hours. Results will be placed into a CSV for 
#  review of UPN, IP address of submitter, and timestamp.
#************************************************

cls
if ($PastHours -gt 0)
 {$PastPeriod = (Get-Date).AddHours(-($PastHours))}
 else
  {$PastPeriod = (Get-Date).AddDays(-($PastDays)) }
$Outputfile = $Pwd.path + "\BadCredAttempts.csv"
$CS = get-wmiobject -class win32_computersystem
$Hostname = $CS.Name + '.' + $CS.Domain
$Instances = @{}
$OSVersion = gwmi win32_operatingsystem
[int]$BN = $OSVersion.Buildnumber 
if ($BN -lt 9200){$ADFSLogName = "AD FS 2.0/Admin"}
 else {$ADFSLogName = "AD FS/Admin"}

$Users = @()
$IPAddresses = @()
$Times = @()
$AllInstances = @()
Write-Host "Searching event log for bad credential events..."
if ($BN -ge 9200) {Get-Winevent  -FilterHashTable @{LogName= "Security"; StartTime=$PastPeriod; ID=411} -ErrorAction SilentlyContinue | Where-Object  {$_.Message -match "The user name or password is incorrect"} |  % {
 $Instance = New-Object PSObject
 $UPN = $_.Properties[2].Value
 $UPN = $UPN.Split("-")[0]
 $IPAddress = $_.Properties[4].Value
 $Users += $UPN
 $IPAddresses += $IPAddress
 $Times += $_.TimeCreated
 add-member -inputobject $Instance -membertype noteproperty -name "UserPrincipalName" -value $UPN
 add-member -inputobject $Instance -membertype noteproperty -name "IP Address" -value $IPAddress
 add-member -inputobject $Instance -membertype noteproperty -name "Time" -value ($_.TimeCreated).ToString()
 $AllInstances += $Instance
 $Instance = $null
 }
}


$AllInstances | select * | Export-Csv -Path $Outputfile -append -force -NoTypeInformation 
Write-Host "Data collection finished. The output file can be found at $outputfile`."
$AllInstances = $null


ADFS 安全审核事件分析器

PARAM ($SearchCriteria, $PastDays = 1, $PastHours)
#************************************************
# ADFSSecAuditParse.ps1
# Version 1.0
# Date: 2-2-2016
# Author: Tim Springston [MSFT]
# Description: This script will parse an ADFS Security event log file (EVTX)
#  and search for audit events related to a specific user or other criteria.
#  The script will work for the each ADFS login instance for a given criteria during a stated time frame.
#  If you need to locate a second then filter and save the event log to focus in.
# Return an array of initial instance IDs with the criteria, run the search function against each and output
# a unique text file for each.
#************************************************

cls
if ($PastHours -gt 0)
 {
 $PastPeriod = (Get-Date).AddHours(-($PastHours))
 }
 else
  {$PastPeriod = $PastDays}
 
$CS = get-wmiobject -class win32_computersystem
$Hostname = $CS.Name + '.' + $CS.Domain
$Instances = @()
Get-Winevent -ComputerName $Hostname -LogName Security  | Where-Object {(($_.ID -eq 501) `
-and ($_.Properties.Value -contains $SearchCriteria) -and ($_.TimeCreated -gt $PastPeriod))} | % { $Instances += $_.Properties[0].Value}

function FindADFSAuditEvents  { 
 param ($valuetomatch, $counter, $instance, $PastPeriod)
  $Results = $pwd.Path + "\" + $SearchCriteria + "-ADFSSecAudit" + '-' + $Counter + ".txt" 
  $SearchString = $SearchCriteria + " and instance " + $Instance + " in Security event log."
  "Security Audit Events which match $SearchString" | Out-File $Results -Encoding UTF8 
  Get-WinEvent -ComputerName $Hostname -LogName Security  -WarningAction SilentlyContinue | `
  Where-Object -ErrorAction SilentlyContinue {($_.TimeCreated -gt $PastPeriod) -and (($_.Properties -contains $ValueToMatch) -or ($_.Properties[0].Value -match $Instance))}  | % {
  $Event = New-object PSObject
  add-member -inputobject $Event -membertype noteproperty -name "Event ID" -value $_.ID
  add-member -inputobject $Event -membertype noteproperty -name "Provider" -value $_.ProviderName
  add-member -inputobject $Event -membertype noteproperty -name "Machine Name" -value $_.MachineName
  add-member -inputobject $Event -membertype noteproperty -name "User ID" -value $_.UserID
  add-member -inputobject $Event -membertype noteproperty -name "Time Created " -value $_.TimeCreated  
  $Event | FL *
  $Event | Out-File $Results -Encoding UTF8  -Append
  $_.Properties | FL *
  $_.Properties | Out-File $Results -Encoding UTF8  -Append
  $DateTimeExport = $_.TimeCreated
  }
 $DateTime = (($DateTimeExport.ToShortDateString()).Replace('/','-') + '@' + (($DateTimeExport.ToShortTimeString()).Replace(' ','')))
 $DateTime = $DateTime.Replace(':','')
 $Results2 = $pwd.Path + "\" + $SearchCriteria + '-' + $DateTime + "-ADFSSecAudit" + $Counter + ".txt"
 Rename-Item -Path $Results -NewName $Results2
 } 

$Counter = 1
foreach ($instance in $Instances)
 {
 FindADFSAuditEvents -ValueToMatch $SearchCriteria  -Instance $Instance -PastPeriod $PastPeriod -Counter $Counter
 $Counter++
 }

联系我们寻求帮助

如果你有任何疑问或需要帮助,请创建支持请求联系 Azure 社区支持。 你还可以将产品反馈提交到 Azure 反馈社区