Applies ToWindows Server 2008 Windows Server 2008 R2 Windows Server 2012 Windows Server 2012 R2 Windows Server 2016 Windows Server 2019, all editions

摘要

林信任为 Active Directory 林中的资源提供一种方式来信任来自另一个林的身份。 可在两个方向上配置此信任。 受信任的林是用户身份的来源。 信任林包含用户进行身份验证的资源。 受信任的林可以对信任林的用户进行身份验证,而不允许反向操作。

不受约束 Kerberos 委派是一种机制,用户将其凭据发送到服务,以使服务能够代表用户访问资源。要启用不受约束 Kerberos 委派,此服务在 Active Directory 中的帐户必须标记为信任委派。 如果用户和服务属于不同的林,则会产生问题。 此服务林负责允许委派。 委派包括用户林中用户的凭据。

允许一个林做出影响其他林帐户的安全决策会违反林之间的安全边界。拥有信任林的攻击者可以请求从受信任林中委派 TGT 来获取身份,从而授予其访问受信任林中资源的权限。 这不适用于 Kerberos 约束委派 (KCD)。

Windows Server 2012 引入了 Kerberos 完全委派的林边界的强制实施。 此功能向受信任域添加了一个策略,以基于每个信任禁用不受约束委派。 此功能的默认设置允许不受约束委派并且不安全。

存在为下列版本的 Windows Server 提供安全加固的更新:

  • Windows Server 2019

  • Windows Server 2016

  • Windows Server 2012 R2

  • Windows Server 2012

此功能和安全加固方面的更改被反向移植到以下版本:

  • Windows Server 2008 R2

  • Windows Server 2008

这些安全更新会进行以下更改:

  • 在安装 5 月 14 日更新和以后的更新后,默认在新林和新外部信任上禁用不受约束 Kerberos 委派。

  • 在安装 2019 年 7 月 9 日更新和以后的更新后,在林(新林和现有林)和外部信任上禁用不受约束 Kerberos 委派。

  • 管理员可以通过使用 NETDOM 和 AD PowerShell 模块的 5 月或之后的版本启用不受约束 Kerberos 委派。

这些更新可能会导致当前需要跨林或外部信任的不受约束委派的应用程序的兼容性冲突。 对于默认启用隔离标志(也称为 SID 筛选)的外部信任尤其如此。 具体而言,请求新票证时,对列出的信任类型使用不受约束委派的服务的身份验证请求将失败。

有关发布日期,请参阅更新时间线

替代方法

要在具有 Kerberos 完全委派的林边界的强制实施功能的 Windows Server 版本上提供数据和帐户安全,在跨内向信任安装 2019 年 3 月更新后,可以通过将 netdom 标志“EnableTGTDelegation”设置为“否”来阻止 TGT 委派,如下所示:

netdom.exe trust fabrikam.com /domain:contoso.com /EnableTGTDelegation:No

在分别安装 2019 年 5 月和 7 月更新后,TGT 委派将在新林和现有林以及外部信任上被阻止。

要跨信任重新启用委派并返回到最初的不安全配置,直至可以启用约束委派或基于资源的委派,请将“EnableTGTDelegation”标志设置为“是”。

启用 TGT 委派的 NETDOM 命令行如下所示:

netdom trust <TrustedDomainName > /domain:<TrustingDomainName > /EnableTgtDelegation:Yes

可以从概念上考虑用于启用 TGT 委派的 NETDOM 语法,如下所示:

netdom trust <domain that you are administering> /domain:<domain whose trust NETDOM is modifying> /EnableTgtDelegation:Yes

要在 contoso.com 服务器上启用 fabrakam.com 用户的 TGT 委派的 NETDOM 语法如下所示:

netdom.exe trust fabrikam.com /domain:contoso.com /EnableTGTDelegation:Yes

注意

  • 应在受信任域(本例中为 fabrikam.com)中为每个信任域(例如 contoso.com)设置“EnableTGTDelegation”标志。 设置此标志后,受信任域将不再允许将 TGT 委派给信任域。

  • EnableTGTDelegation 的安全状态为“否”。

  • 当以手动或编程方式将 EnableTGTDelegation 设置为“是”时,依赖于跨林的不受约束委派的任何应用程序或服务都将失败。 在安装 2019 年 5 月和 2019 年 7 月更新后,EnableTGTDelegation 在新林和现有林上默认为“否”。 有关如何检测此故障的更多信息,请参阅查找依赖于不受约束委派的服务。 有关影响此解决方法应用方式的更改时间线,请参阅更新时间线

  • 有关 NETDOM 的更多信息,请参阅 Netdom.exe 文档

  • 如果必须在信任上启用 TGT 委派,则建议你通过在客户端计算机上启用 Windows Defender Credential Guard 来缓解风险。 这可以防止从已启用且正在运行 Windows Defender Credential Guard 的计算机进行所有不受约束委派。

  • 如果你拥有林或外部信任,或者配置为已隔离,由于这两个标志具有相反的语义,无法启用 TGT 委派。 隔离位加强了参与域之间的安全边界。 通过授予信任域对受信任域中用户凭据的访问权限,启用 TGT 委派可清除域之间的安全边界。 不能两者兼得。

    如果当前启用了“quarantine”标记,请将“quarantine:no”标记添加到 NETDOM 命令行语法。

  • 如果已将“EnableTGTDelegation”更改为“是”,请根据需要删除原始和中间调用方上的 Kerberos 票证。 要删除的相关票证是客户在相关信任中的引荐 TGT。 这可能涉及多个设备,具体取决于给定环境中的委派跃点数。

有关此过程的更多信息,请参阅以下 Windows IT 专业人员中心文章:

使用 Windows Defender Credential Guard 保护派生的域凭据

更新时间线

2019 年 3 月 12 日

Kerberos 完全委派林边界的强制实施将作为更新提供,以便在本文顶部的“适用范围”部分列出的所有受支持的 Windows Server 版本上启用此功能。 我们建议你基于内向林信任设置该功能。

此更新会将 Kerberos 完全委派的林边界的强制实施功能添加到下列系统:

  • Windows Server 2008 R2

  • Windows Server 2008

2019 年 5 月 14 日

发布了一个为新林和外部信任添加了新的安全默认配置的更新。 如果要求跨信任委派,则应在安装 2019 年 7 月 9 日更新之前将“EnableTGTDelegation”标志设为置“是”。 如果不需要跨信任委派,则不应设置“EnableTGTDelegation”标志。 在安装 2019 年 7 月 9 日更新之前,将忽略“EnableTGTDelegation”标志,以便管理员有时间在需要时重新启用不受约束 Kerberos 委派。

作为该更新的一部分,对于任何新创建的信任,“EnableTGTDelegation”标志将默认设置为“否”。 这与之前的行为相反。 我们建议管理员改为重新配置受影响的服务以使用基于资源的约束委派。

有关如何检测兼容性问题的更多信息,请参阅查找依赖于不受约束委派的服务

2019 年 7 月 9 日

发布了一个强制执行林和外部信任入站端的新默认行为的更新。 将对列出的信任类型使用不受约束委派的服务的身份验证请求进行身份验证,但不进行委派。 尝试运行委派的操作时,该服务将失败。

有关缓解措施,请参阅“解决方法”部分。

查找依赖于不受约束委派的服务

若要扫描具有允许 TGT 委派的内向信任的林,并查找允许不受约束委派的任何安全主体,请在脚本文件中运行以下 PowerShell 脚本(例如,Get-RiskyServiceAccountsByTrust.ps1 -Collect):

注意

你还可以传递“-ScanAll”标志,以搜索不允许 TGT 委派的信任。


[CmdletBinding()]  
Param  
(  
    [switch]$Collect, 
    [switch]$ScanAll 
) 
 
if ($Debug) {  
    $DebugPreference = 'Continue'  
} 
else { 
    $DebugPreference = 'SilentlyContinue'  
} 

function Get-AdTrustsAtRisk 
{ 
    [CmdletBinding()]  
    Param  
    (  
        [string]$Direction = "Inbound", 
        [switch]$ScanAll 
    ) 
 
    if ($ScanAll) { 
        return get-adtrust -filter {Direction -eq $Direction} 
    } 
    else { 
        return get-adtrust -filter {Direction -eq $Direction -and TGTDelegation -eq $false} 
    } 
} 
 
function Get-ServiceAccountsAtRisk 
{ 
    [CmdletBinding()]  
    Param  
    (  
        [string]$DN = (Get-ADDomain).DistinguishedName, 
        [string]$Server = (Get-ADDomain).Name 
    ) 
 
    Write-Debug "Searching $DN via $Server" 
 
    $SERVER_TRUST_ACCOUNT = 0x2000  
    $TRUSTED_FOR_DELEGATION = 0x80000  
    $TRUSTED_TO_AUTH_FOR_DELEGATION= 0x1000000  
    $PARTIAL_SECRETS_ACCOUNT = 0x4000000    
 
    $bitmask = $TRUSTED_FOR_DELEGATION -bor $TRUSTED_TO_AUTH_FOR_DELEGATION -bor $PARTIAL_SECRETS_ACCOUNT  
  
$filter = @"  
(& 
  (servicePrincipalname=*) 
  (| 
    (msDS-AllowedToActOnBehalfOfOtherIdentity=*) 
    (msDS-AllowedToDelegateTo=*) 
    (UserAccountControl:1.2.840.113556.1.4.804:=$bitmask) 
  ) 
  (| 
    (objectcategory=computer) 
    (objectcategory=person) 
    (objectcategory=msDS-GroupManagedServiceAccount) 
    (objectcategory=msDS-ManagedServiceAccount) 
  ) 
) 
"@ -replace "[\s\n]", ''  
  
    $propertylist = @(  
        "servicePrincipalname",   
        "useraccountcontrol",   
        "samaccountname",   
        "msDS-AllowedToDelegateTo",   
        "msDS-AllowedToActOnBehalfOfOtherIdentity"  
    )  
 
    $riskyAccounts = @() 
 
    try { 
        $accounts = Get-ADObject -LDAPFilter $filter -SearchBase $DN -SearchScope Subtree -Properties $propertylist -Server $Server 
    } 
    catch { 
        Write-Warning "Failed to query $Server. Consider investigating seperately. $($_.Exception.Message)" 
    } 
              
    foreach ($account in $accounts) {  
        $isDC = ($account.useraccountcontrol -band $SERVER_TRUST_ACCOUNT) -ne 0  
        $fullDelegation = ($account.useraccountcontrol -band $TRUSTED_FOR_DELEGATION) -ne 0  
        $constrainedDelegation = ($account.'msDS-AllowedToDelegateTo').count -gt 0  
        $isRODC = ($account.useraccountcontrol -band $PARTIAL_SECRETS_ACCOUNT) -ne 0  
        $resourceDelegation = $account.'msDS-AllowedToActOnBehalfOfOtherIdentity' -ne $null  
      
        $acct = [PSCustomobject] @{  
            domain = $Server 
            sAMAccountName = $account.samaccountname  
            objectClass = $account.objectclass          
            isDC = $isDC  
            isRODC = $isRODC  
            fullDelegation = $fullDelegation  
            constrainedDelegation = $constrainedDelegation  
            resourceDelegation = $resourceDelegation  
        }  
 
        if ($fullDelegation) {  
            $riskyAccounts += $acct    
        } 
    }  
 
    return $riskyAccounts 
} 
 
function Get-RiskyServiceAccountsByTrust  
{ 
    [CmdletBinding()]  
    Param  
    ( 
        [switch]$ScanAll 
    ) 
     
    $riskyAccounts = @() 
 
    $trustTypes = $("Inbound", "Bidirectional") 
 
    foreach ($type in $trustTypes) { 
 
        $riskyTrusts = Get-AdTrustsAtRisk -Direction $type -ScanAll:$ScanAll 
 
        foreach ($trust in $riskyTrusts) { 
            $domain = $null 
     
            try { 
                $domain = Get-AdDomain $trust.Name -ErrorVariable eatError -ErrorAction Ignore 
            } catch { 
                write-debug $_.Exception.Message 
            } 
 
            if($eatError -ne $null) { 
                Write-Warning "Couldn't find domain: $($trust.Name)" 
            } 
 
            if ($domain -ne $null) { 
                $accts = Get-ServiceAccountsAtRisk -DN $domain.DistinguishedName -Server $domain.DNSRoot 
 
                foreach ($acct in $accts) { 
                    Write-Debug "Risky: $($acct.sAMAccountName) in $($acct.domain)" 
                }             
 
                $risky = [PSCustomobject] @{  
                    Domain = $trust.Name 
                    Accounts = $accts 
                } 
 
                $riskyAccounts += $risky 
            } 
        } 
    } 
 
    return $riskyAccounts 
} 
 
if ($Collect) { 
   Get-RiskyServiceAccountsByTrust -ScanAll:$ScanAll | Select-Object -expandProperty Accounts | format-table 
}

PowerShell 脚本的输出将列出为来自执行域(未配置不受约束委派)的内向信任配置的域中的 Active Directory 安全主体。 输出将类似于以下示例。

domain

sAMAccountName

objectClass

partner.fabrikam.com

dangerous

user

partner.fabrikam.com

labsrv$

computer

通过 Windows 事件检测不受约束委派

发出 Kerberos 票证时,Active Directory 域控制器会记录以下安全事件。 这些事件包含有关目标域的信息。 你可以使用这些事件来确定是否在内向信任之间使用了不受约束委派。

注意

检查包含与受信任域名匹配的 TargetDomainName 值的事件。

事件日志

事件源

事件 ID

详细信息

安全性

Microsoft-Windows-Security-Auditing

4768

分发了 Kerberos TGT。

安全性

Microsoft-Windows-Security-Auditing

4769

分发了 Kerberos 服务票证。

安全性

Microsoft-Windows-Security-Auditing

4770

续订了 Kerberos 服务票证。

解决身份验证失败问题

禁用不受约束委派后,如果应用程序依赖于不受约束委派,则应用程序可能会遇到与这些更改的兼容性问题。 应将这些应用程序配置为使用基于资源的约束委派。 有关更多信息,请参阅 Kerberos 约束委派概述

使用约束委派不支持依赖于跨信任的往返身份验证的应用程序。 例如,如果林 A 中的用户对林 B 中的应用程序进行身份验证,并且林 B 中的该应用程序尝试将票证委派回林 A,则委派将失败。

需要更多帮助?

需要更多选项?

了解订阅权益、浏览培训课程、了解如何保护设备等。

社区可帮助你提出和回答问题、提供反馈,并听取经验丰富专家的意见。