使用 AD FS 2019 风险评估模型构建插件

现在你可以生成自己的插件,以便在各个阶段(接收请求、身份验证前和身份验证后)阻止身份验证请求或为其分配风险评分。 可以使用 AD FS 2019 中引入的新风险评估模型来实现此目的。

什么是风险评估模型?

风险评估模型是一组接口和类,使开发人员能够读取身份验证请求标头并实现自己的风险评估逻辑。 然后,实现的代码(插件)遵循 AD FS 身份验证过程运行。 例如,使用该模型附带的接口和类,可以根据请求标头中包含的客户端 IP 地址实现代码,以阻止或允许身份验证请求。 AD FS 将为每个身份验证请求执行代码,并按照实现的逻辑采取相应的操作。

该模型允许在 AD FS 身份验证管道三个阶段中的任何一个阶段插入代码,如下所示:

Diagram that shows the three stages of A D F S authentication.

  1. 接收请求阶段 – 启用生成插件以在 AD FS 收到身份验证请求时(即在用户输入凭据之前)允许或阻止请求。 可以使用在此阶段提供的请求上下文(例如客户端 IP、Http 方法、代理服务器 DNS 等)来执行风险评估。 例如,可以生成一个插件以从请求上下文中读取 IP,如果该 IP 位于预定义的风险 IP 列表中,则阻止身份验证请求。

  2. 身份验证前阶段 – 启用生成插件以在用户提供凭据时(但在 AD FS 评估凭据之前)允许或阻止请求。 在此阶段,除了请求上下文之外,你还将获得可在风险评估逻辑中使用的,有关安全上下文(例如用户令牌、用户标识符等)和协议上下文(例如身份验证协议、clientID、resourceID 等)的信息。 例如,可以生成一个插件来防止密码喷洒攻击,方法是从用户令牌中读取用户密码,如果该密码在预定义的风险密码列表中,则阻止身份验证请求。

  3. 身份验证后 – 启用生成插件以在用户提供凭据并且 AD FS 执行身份验证之后评估风险。 在此阶段,除了请求上下文、安全上下文和协议上下文之外,你还将获得有关身份验证结果(成功或失败)的信息。 插件可以根据可用信息评估风险评分,并将风险评分传递给声明和策略规则以做进一步评估。

为了更好地了解如何生成风险评估插件并根据 AD FS 过程运行它,让我们生成一个示例插件来阻止来自某些识别为有风险 Extranet IP 的请求,将该插件注册到 AD FS,最后测试功能。

注意

或者,可以生成风险用户插件,该示例插件利用 Microsoft Entra ID 保护确定的用户风险级别来阻止身份验证或强制执行多重身份验证 (MFA)。 此处提供了生成风险用户插件的步骤。

生成示例插件

注意

本演练仅演示如何创建示例插件。 我们要创建的解决方案并非企业就绪的解决方案。

先决条件

下面是生成此示例插件所要满足的先决条件列表:

  • 已安装并配置 AD FS 2019
  • .NET Framework 4.7 和更高版本
  • Visual Studio

生成插件 dll

以下过程将引导你生成示例插件 dll:

  1. 下载示例插件,使用 Git Bash 并键入以下命令:

    git clone https://github.com/Microsoft/adfs-sample-RiskAssessmentModel-RiskyIPBlock
    
  2. 在 AD FS 服务器上的任意位置创建一个 .csv 文件(在本例中,我在 C:\extensions 中创建了 authconfigdb.csv 文件)并将要阻止的 IP 添加到此文件。

    示例插件将阻止来自此文件中列出的 Extranet IP 的任何身份验证请求。

    注意

    如果你有 AD FS 场,可以在任意或所有 AD FS 服务器上创建该文件。 任何文件都可用于将风险 IP 导入 AD FS。 下面的将插件 dll 注册到 AD FS 部分将详细介绍导入过程。

  3. 使用 Visual Studio 打开项目 ThreatDetectionModule.sln

  4. 从解决方案资源管理器中删除 Microsoft.IdentityServer.dll,如下所示:
    Screenshot that highlights the Remove menu option.

  5. 添加对 AD FS 的 Microsoft.IdentityServer.dll 的引用,如下所示:

    a. 在“解决方案资源管理器”中右键单击“引用”,然后选择“添加引用...”

    Screenshot that highlights the Add Reference menu option.

    b. 在“引用管理器”窗口中选择“浏览”。 在“选择要引用的文件...”对话框中,从 AD FS 安装文件夹(在本例中为 C:\Windows\ADFS)中选择 Microsoft.IdentityServer.dll,然后单击“添加”。

    注意

    在本例中,我将在 AD FS 服务器本身上生成插件。 如果你的开发环境位于其他服务器上,请将 AD FS 服务器上 AD FS 安装文件夹中的 Microsoft.IdentityServer.dll 复制到开发箱中。

    Screenshot that shows the file you should copy.

    c. 在确保选中 Microsoft.IdentityServer.dll 复选框后,单击“引用管理器”窗口中的“确定”。

    Screenshot that shows the Microsoft dot Identity Server dot d l l checkbox.

  6. 所有类和引用现已准备就绪,接下来可以执行生成。 但是,由于此项目的输出是一个 dll,因此必须将其安装到 AD FS 服务器的全局程序集缓存 (GAC) 中,并且首先需要为该 dll 签名。 可以按以下步骤来完成:

    a. 右键单击项目名称 ThreatDetectionModule。 在菜单中单击“属性”。

    Screenshot that highlights the Properties menu option.

    b. 在“属性”页中单击左侧的“签名”,然后选中标有“为程序集签名”的复选框。 在“选择强名称密钥文件:”下拉菜单中选择“<新建...>”。

    Screenshot that shows the Sign the assembly checkbox.

    c. 在“创建强名称密钥”对话框中为密钥键入一个名称(可以选择任意名称),并取消选中“使用密码保护我的密钥文件”复选框。 然后单击“确定”。

    Screenshot that shows Protect my key file with password checkbox.

    d. 如下所示保存项目:

    Screenshot that shows where to save your project.

  7. 依次单击“生成”、“重新生成解决方案”来生成项目,如下所示:

    Screenshot that shows the Rebuild Solution menu option.

    检查屏幕底部的“输出”窗口,查看是否出现任何错误。

    Screenshot that shows the output from the rebuilt solution.

该插件 (dll) 现在可供使用,它位于项目文件夹的 \bin\Debug 文件夹中(在本例中,其路径为 C:\extensions\ThreatDetectionModule\bin\Debug \ThreatDetectionModule.dll)。

下一步是将此 dll 注册到 AD FS,使其遵循 AD FS 身份验证过程运行。

将插件 dll 注册到 AD FS

我们需要在 AD FS 服务器上使用 Register-AdfsThreatDetectionModule PowerShell 命令将 dll 注册到 AD FS 中。 但在注册之前,需要获取公钥令牌。 此公钥令牌是在创建密钥并使用该密钥为 dll 签名时创建的。 若要了解 dll 的公钥令牌是什么,可以使用 SN.exe,如下所示:

  1. 将 \bin\Debug 文件夹中的 dll 文件复制到另一个位置(本例将其复制到 C:\extensions)。

  2. 启动 Visual Studio 的开发人员命令提示符,并转到包含 sn.exe 的目录(在本例中,该目录为 C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7.2 Tools)。

    Screenshot that shows the Developer Command Prompt for Visual Studio.

  3. 结合 -T 参数和文件位置(在本例中为 SN -T "C:\extensions\ThreatDetectionModule.dll")运行 SN 命令。

    Screenshot that shows how to run the S N command.

    该命令将为你提供公钥令牌(在本例中,公钥令牌为 714697626ef96b35)

  4. 将 dll 添加到 AD FS 服务器的全局程序集缓存中。最佳做法是为项目创建适当的安装程序,然后使用该安装程序将文件添加到 GAC。 另一种解决方案是在开发计算机上使用 Gacutil.exe(此处提供了有关 Gacutil.exe 的详细信息)。 由于我的 Visual Studio 与 AD FS 位于同一服务器上,因此我将使用 Gacutil.exe,如下所示:

    a. 打开 Visual Studio 的开发人员命令提示符,并转到包含 Gacutil.exe 的目录(在本例中,该目录为 C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7.2 Tools)。

    b. 运行 Gacutil 命令(在本例中为 Gacutil /IF C:\extensions\ThreatDetectionModule.dll):

    Screenshot that shows how to run the Gacutil command.

    注意

    如果你有 AD FS 场,则需要在该场中的每个 AD FS 服务器上执行上述操作。

  5. 打开 Windows PowerShell 并运行以下命令以注册 dll:

    Register-AdfsThreatDetectionModule -Name "<Add a name>" -TypeName "<class name that implements interface>, <dll name>, Version=10.0.0.0, Culture=neutral, PublicKeyToken=< Add the Public Key Token from Step 2. above>" -ConfigurationFilePath "<path of the .csv file>"
    

    在本例中,命令为:

    Register-AdfsThreatDetectionModule -Name "IPBlockPlugin" -TypeName "ThreatDetectionModule.UserRiskAnalyzer, ThreatDetectionModule, Version=10.0.0.0, Culture=neutral, PublicKeyToken=714697626ef96b35" -ConfigurationFilePath "C:\extensions\authconfigdb.csv"
    

    注意

    即使你有 AD FS 场,也只需注册 dll 一次。

  6. 注册 dll 后重启 AD FS 服务。

好了,dll 现已注册到 AD FS 并可供使用!

注意

如果对插件进行任何更改并重新生成项目,则需要重新注册更新的 dll。 在注册之前,需要使用以下命令取消注册当前 dll:

UnRegister-AdfsThreatDetectionModule -Name "<name used while registering the dll in 5. above>"



在本例中,命令为:

UnRegister-AdfsThreatDetectionModule -Name "IPBlockPlugin"

测试插件

  1. 打开先前创建的 authconfig.csv 文件(在本例中,其位置为 C:\extensions)并添加要阻止的 Extranet IP。 每个 IP 应独行输入,末尾不能包含空格。

    Screenshot that shows how to add the extranet I P lines.

  2. 保存并关闭该文件。

  3. 运行以下 PowerShell 命令将更新的文件导入 AD FS:

    Import-AdfsThreatDetectionModuleConfiguration -name "<name given while registering the dll>" -ConfigurationFilePath "<path of the .csv file>"
    

    在本例中,命令为:

    Import-AdfsThreatDetectionModuleConfiguration -name "IPBlockPlugin" -ConfigurationFilePath "C:\extensions\authconfigdb.csv")
    
  4. 使用在 authconfig.csv 中添加的 IP 从服务器发起身份验证请求。

    对于本演示,我将使用 AD FS Help Claims X-Ray 工具发起请求。 如果你要使用 X-Ray 工具,请按照说明操作

    输入联合服务器实例并点击“测试身份验证”按钮。

    Screenshot that shows the Test Authentication button.

  5. 身份验证将被阻止,如下所示。

    Screenshot that shows that authentication is blocked.

了解如何生成和注册插件后,让我们演练插件代码,了解如何使用该模型中引入的新接口和类来实现。

插件代码演练

使用 Visual Studio 打开项目 ThreatDetectionModule.sln,然后在屏幕右侧的“解决方案资源管理器”中打开主文件 UserRiskAnalyzer.cs

model

该文件包含实现抽象类 ThreatDetectionModule 和接口 IRequestReceivedThreatDetectionModule 的主类 UserRiskAnalyzer,以从请求上下文中读取 IP,将获取的 IP 与从 AD FS DB 加载的 IP 进行比较,如果 IP 匹配,则阻止请求。 让我们更详细地了解这些类型

ThreatDetectionModule 抽象类

此抽象类将插件加载到 AD FS 管道中,以便可以遵循 AD FS 过程运行插件代码。

public abstract class ThreatDetectionModule
{
    protected ThreatDetectionModule();

    public abstract string VendorName { get; }
    public abstract string ModuleIdentifier { get; }

    public abstract void OnAuthenticationPipelineLoad(ThreatDetectionLogger logger, ThreatDetectionModuleConfiguration configData);
    public abstract void OnAuthenticationPipelineUnload(ThreatDetectionLogger logger);
    public abstract void OnConfigurationUpdate(ThreatDetectionLogger logger, ThreatDetectionModuleConfiguration configData);
}

该类包含以下方法和属性:

方法 类型 定义
OnAuthenticationPipelineLoad Void 将插件加载到 AD FS 管道中时由 AD FS 调用
OnAuthenticationPipelineUnload Void 从 AD FS 管道中卸载插件时由 AD FS 调用
OnConfigurationUpdate Void 更新配置时由 AD FS 调用
Property Type 定义
VendorName String 获取拥有该插件的供应商的名称
ModuleIdentifier 字符串 获取该插件的标识符

在示例插件中,我们将使用 OnAuthenticationPipelineLoadOnConfigurationUpdate 方法从 AD FS DB 读取预定义的 IP。 将插件注册到 AD FS 时调用 OnAuthenticationPipelineLoad,在使用 Import-AdfsThreatDetectionModuleConfiguration cmdlet 导入 .csv 时调用 OnConfigurationUpdate

IRequestReceivedThreatDetectionModule 接口

使用此接口可以在 AD FS 收到身份验证请求时、但在用户输入凭据之前(即身份验证过程的接收请求阶段)实现风险评估。

public interface IRequestReceivedThreatDetectionModule
{
    Task<ThrottleStatus> EvaluateRequest (
    ThreatDetectionLogger logger,
    RequestContext requestContext );
}

该接口包含 EvaluateRequest 方法,通过该方法可以使用在 requestContext 输入参数中传递的身份验证请求上下文编写风险评估逻辑。 requestContext 参数的类型为 RequestContext

传递的另一个输入参数是 ThreatDetectionLogger 类型的记录器。 该参数可用于将错误、审核和/或调试消息写入 AD FS 日志。

该方法向 AD FS 返回 ThrottleStatus(0 表示未评估,1 表示阻止,2 表示允许),AD FS 随后会阻止或允许该请求。

在示例插件中,EvaluateRequest 方法实现分析 requestContext 参数中的 clientIpAddress,并将其与从 AD FS DB 加载的所有 IP 进行比较。 如果找到匹配项,方法将返回 2(表示阻止),否则返回 1(表示允许)。 AD FS 根据返回的值阻止或允许请求。

注意

上面所述的示例插件仅实现 IRequestReceivedThreatDetectionModule 接口。 但是,风险评估模型提供了两个附加接口 – IPreAuthenticationThreatDetectionModule(在身份验证前阶段实现风险评估逻辑)和 IPostAuthenticationThreatDetectionModule(在身份验证后阶段实现风险评估逻辑)。 下面提供了有关这两个接口的详细信息。

IPreAuthenticationThreatDetectionModule 接口

使用此接口可以在用户提供凭据时、但在 AD FS 评估凭据之前(即身份验证前阶段)实现风险评估逻辑。

public interface IPreAuthenticationThreatDetectionModule
{
    Task<ThrottleStatus> EvaluatePreAuthentication (
    ThreatDetectionLogger logger,
    RequestContext requestContext,
    SecurityContext securityContext,
    ProtocolContext protocolContext,
    IList<Claim> additionalClams
  );
}

该接口包含 EvaluatePreAuthentication 方法,可以通过该方法使用在 RequestContext requestContextSecurityContext securityContextProtocolContext protocolContextIList<Claim> additionalClams 输入参数中传递的信息编写身份验证前风险评估逻辑。

注意

有关使用每个上下文类型传递的属性列表,请访问 RequestContextSecurityContextProtocolContext 类定义。

传递的另一个输入参数是 ThreatDetectionLogger 类型的记录器。 该参数可用于将错误、审核和/或调试消息写入 AD FS 日志。

该方法向 AD FS 返回 ThrottleStatus(0 表示未评估,1 表示阻止,2 表示允许),AD FS 随后会阻止或允许该请求。

IPostAuthenticationThreatDetectionModule 接口

使用此接口可以在用户提供凭据并且 AD FS 执行身份验证之后(即身份验证后阶段)实现风险评估逻辑。

public interface IPostAuthenticationThreatDetectionModule
{
    Task<RiskScore> EvaluatePostAuthentication (
    ThreatDetectionLogger logger,
    RequestContext requestContext,
    SecurityContext securityContext,
    ProtocolContext protocolContext,
    AuthenticationResult authenticationResult,
    IList<Claim> additionalClams
  );
}

该接口包含 EvaluatePostAuthentication 方法,可以通过该方法使用在 RequestContext requestContextSecurityContext securityContextProtocolContext protocolContextIList<Claim> additionalClams 输入参数中传递的信息编写身份验证后风险评估逻辑。

注意

有关使用每个上下文类型传递的属性的完整列表,请参阅 RequestContextSecurityContextProtocolContext 类定义。

传递的另一个输入参数是 ThreatDetectionLogger 类型的记录器。 该参数可用于将错误、审核和/或调试消息写入 AD FS 日志。

该方法返回可在 AD FS 策略和声明规则中使用的风险评分

注意

要使该插件正常工作,主类(在本例中为 UserRiskAnalyzer)需要派生 ThreatDetectionModule 抽象类,并且应至少实现上述三个接口中的一个。 注册 dll 后,AD FS 将检查实现了哪些接口,并在管道中的相应阶段调用这些接口。

常见问题解答

为何要生成这些插件?
答:这些插件不仅提供额外的功能来保护环境免受密码喷洒之类的攻击,而且让你可以根据自己的要求灵活生成自己的风险评估逻辑。

在何处捕获日志?
答:可以使用 WriteAdminLogErrorMessage 方法将错误日志写入“AD FS/管理”事件日志,使用 WriteAuditMessage 方法将审核日志写入“AD FS 审核”安全日志,使用 WriteDebugMessage 方法将调试日志写入“AD FS 跟踪”调试日志。

添加这些插件是否会增大 AD FS 身份验证过程的延迟?
答:延迟影响取决于你要实现的风险评估逻辑所需的执行时间。 建议在将插件部署到生产环境之前评估延迟影响。

为何 AD FS 不能建议有风险 IP、用户等的列表?
答:虽然暂时还不可以,但我们正在努力生成智能功能,以在可插入风险评估模型中建议有风险的 IP、用户等。 我们很快就会告知推出日期。

还有其他哪些示例插件可用?
答:可以使用以下示例插件:

名称 说明
风险用户插件 根据 Microsoft Entra ID 保护确定的用户风险级别阻止身份验证或强制执行 MFA 的示例插件。