使用 AD FS 2019 风险评估模型生成插件

现在可以构建自己的插件,以在各个阶段阻止或为身份验证请求分配风险分数 - 请求接收、预身份验证和身份验证后。 这可以使用 AD FS 2019 中引入的新风险评估模型来实现。

什么是风险评估模型?

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

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

关系图显示 A D F S 身份验证的三个阶段。

  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 Protection 确定的用户风险级别来阻止身份验证或强制实施多重身份验证(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
  1. 在 AD FS 服务器上的任何位置创建 .csv 文件(在本例中,我在 C:\extensions 上创建了 authconfigdb.csv 文件),并将要阻止的 IP 添加到此文件。

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

注释

如果您拥有一个 AD FS 农场,可以在任意或所有 AD FS 服务器上创建该文件。 任何文件都可用于将有风险的 IP 导入 AD FS。 我们将在下面的 “使用 AD FS 注册插件 dll” 部分详细讨论导入过程。

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

  2. 从解决方案资源管理器中删除 Microsoft.IdentityServer.dll 下的对象,如下所示:
    突出显示“删除”菜单选项的屏幕截图。

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

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

突出显示“添加引用”菜单选项的屏幕截图。

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

注释

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

显示应复制的文件的屏幕截图。

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

显示 Microsoft.IdentityServer.dll 复选框的屏幕截图。

  1. 现在,所有类和引用都已到位,可以进行构建。 但是,由于此项目的输出是 dll,因此必须首先将它安装到 AD FS 服务器的 全局程序集缓存或 GAC 中,并且需要先对 dll 进行签名。 可以按以下步骤来完成:

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

突出显示“属性”菜单选项的屏幕截图。

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

显示“为程序集签名”复选框的屏幕截图。

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

显示“使用密码保护密钥文件”复选框的屏幕截图。

d。 保存项目,如下所示:

显示保存项目的位置的屏幕截图。

  1. 单击“ 生成 ”,然后单击“ 重新生成解决方案 ”生成项目,如下所示:

显示“重新生成解决方案”菜单选项的屏幕截图。

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

显示重新生成解决方案的输出的屏幕截图。

插件(dll)现已可供使用,位于项目文件夹 的 \bin\Debug 文件夹中(在本例中,即 C:\extensions\ThreatDetectionModule\bin\Debug\ThreatDetectionModule.dll)。

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

将插件 dll 注册到 AD FS

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

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

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

显示 Visual Studio 开发人员命令提示符的屏幕截图。

  1. 使用 -T 参数和文件的位置运行 SN 命令(在本例SN -T "C:\extensions\ThreatDetectionModule.dll"中)。

显示如何运行 S N 命令的屏幕截图。

此命令将提供公钥令牌(对我来说, 公钥令牌为 714697626ef96b35

  1. 将 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中):

显示如何运行 Gacutil 命令的屏幕截图。

注释

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

  1. 打开 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 一次。

  1. 注册 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 都应位于单独的行上,末尾不应有空格。

显示如何添加 Extranet IP 行的屏幕截图。

  1. 保存并关闭该文件。

  2. 通过运行以下 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")
  1. 使用 authconfig.csv中添加的相同 IP 从服务器启动身份验证请求。

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

显示“测试身份验证”按钮的屏幕截图。

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

显示阻止身份验证的屏幕截图。

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

插件代码演示

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

模型

该文件包含主类 UserRiskAnalyzer,该类实现抽象类 ThreatDetectionModule 和接口 IRequestReceivedThreatDetectionModule 以从请求上下文读取 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 无效 当插件加载到其管道中时由 AD FS 调用
OnAuthenticationPipelineUnload 无效 从 AD FS 管道中卸载插件时由 AD FS 调用
OnConfigurationUpdate 更新 无效 配置更新时由 AD FS 调用
属性 类型 定义
供应商名称 字符串 获取拥有插件的供应商的名称
模块标识符 字符串 获取插件的标识符

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

IRequestReceivedThreatDetectionModule 接口

通过此 接口 ,你可以在 AD FS 收到身份验证请求时实现风险评估,但在用户输入凭据之前,即在身份验证过程的“已接收请求”阶段。

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

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

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

该方法返回ThrottleStatus(0 表示 NotEvaluated,1 表示 Block,2 表示 Allow)到 AD FS,然后该系统会根据返回结果来阻止或允许请求。

在我们的示例插件中,EvaluateRequest 方法实现从 requestContext 参数分析 clientIpAddress,并将其与从 AD FS DB 加载的所有 IP 进行比较。 如果找到匹配项,则方法返回 2 表示 Block,否则返回 1 表示 Allow。 根据返回的值,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 日志。

该方法返回ThrottleStatus(0 表示 NotEvaluated,1 表示 Block,2 表示 Allow)到 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/Admin”事件日志,使用 WriteAuditMessage 方法将审核日志写入“AD FS 审核”安全日志,并使用 WriteDebugMessage 方法将调试日志写入“AD FS 跟踪”调试日志。

添加这些插件会增加 AD FS 身份验证过程延迟吗?
一个: 延迟影响取决于执行所实现的风险评估逻辑所花费的时间。 建议在部署插件生产环境中之前评估延迟影响。

为什么 AD FS 无法建议有风险 IP、用户等的列表?
一个: 虽然目前不可用,但我们正在努力构建智能,以在可插入风险评估模型中建议有风险的 IP、用户等。 我们将很快共享启动日期。

有哪些其他示例插件可用?
一个: 以下示例插件可用:

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