条件访问:令牌保护(预览)
令牌保护(在行业中有时称为令牌绑定)尝试通过确保只能从预期设备使用令牌来减少使用窃取的令牌进行的攻击。 当攻击者能够通过劫持或重播来窃取令牌时,他们可以在令牌过期或吊销之前模拟其受害者。 令牌失窃被认为是一个相对罕见的事件,但由此造成的损害可能是巨大的。
令牌保护在令牌与接受令牌的设备(客户端密码)之间创建加密安全关联。 如果没有客户端密码,绑定令牌将毫无用处。 当用户在 Microsoft Entra ID 中注册 Windows 10 或更高版本的设备时,其主标识将会绑定到该设备。 这意味着:策略可以确保应用程序在请求访问资源时仅使用绑定的登录会话(或刷新)令牌,又称主刷新令牌 (PRT)。
重要
令牌保护目前为公共预览版。 有关预览版的详细信息,请参阅联机服务的通用许可条款。 通过此预览版,我们将支持创建条件访问策略,以要求对特定服务的登录令牌(刷新令牌)进行令牌保护。 我们在 Windows 设备上支持对通过桌面应用程序访问 Exchange Online 和 SharePoint Online 的条件访问中的登录令牌进行令牌保护。
重要
自首次公开预览版发布以来,令牌保护进行了如下更改:
- 登录日志输出:在 2023 年 6 月下旬,“enforcedSessionControls”和“sessionControlsNotSatisfied”中使用的字符串值从“Binding”更改为了“SignInTokenProtection”。 应更新对登录日志数据的查询来反映这一更改。
注意
可以在此内容中交换登录令牌和刷新令牌。
要求
此预览版支持以下配置,便于访问应用了令牌保护条件访问策略的资源:
- 已加入 Microsoft Entra、已加入 Microsoft Entra 混合和已注册 Microsoft Entra 的 Windows 10 或更新版设备。
- OneDrive 同步客户端版本 22.217 或更高版本
- Teams 本机客户端版本 1.6.00.1331 或更高版本
- Power BI Desktop 版本 2.117.841.0(2023 年 5 月)或更高版本
- 使用“Windows 身份验证代理”登录选项时的 Visual Studio 2022 或更高版本
- 不支持 Office 永久客户端
已知的限制
- 外部用户 (Microsoft Entra B2B) 不受支持,因此不应将其包含在条件访问策略中。
- 以下应用程序不支持使用受保护的令牌流登录,并且在用户访问 Exchange 和 SharePoint 时会阻止用户:
- 访问 Exchange 或 SharePoint 提供的 Exchange、SharePoint 或 Microsoft Graph 范围的 PowerShell 模块
- 适用于 Excel 的 PowerQuery 扩展
- 用于访问 Exchange 或 SharePoint 的 Visual Studio Code 的扩展
- 由于 bug,新的 Teams 2.1 预览版客户端在注销后遭到阻止。 在将来的服务更新中应会修复此 bug。
- 不支持以下 Windows 客户端设备:
- Windows Server
- Surface Hub
- 基于 Windows 的 Microsoft Teams 会议室 (MTR) 系统
许可要求
使用此功能需要 Microsoft Entra ID P2 许可证。 要根据需要查找合适的许可证,请参阅比较 Microsoft Entra ID 的正式发布功能。
注意
令牌保护强制措施是 Microsoft Entra ID 保护的一部分,并且将在正式发布时成为 P2 许可证的一部分。
部署
对于用户来说,在已注册设备和兼容应用程序上使用兼容客户端平台时,用于强制实施令牌保护的条件访问策略的部署应该是不可见的。
为了最大程度地降低因应用或设备不兼容而导致用户中断的可能性,我们强烈建议:
- 从试点用户组开始,并随着时间的推移扩展。
- 在移动到强制令牌保护之前,在仅报告模式下创建条件访问策略。
- 同时捕获交互式和非交互式登录日志。
- 分析这些日志足够长的时间,以涵盖正常的应用程序使用。
- 将已知的良好用户添加到强制策略。
此过程有助于评估用户的客户端和应用兼容性,以强制实施令牌保护。
创建条件访问策略
执行特权访问安全级别中所述的专用角色的用户可能是此功能的目标。 建议先通过一小部分开展试点。
后续步骤可帮助创建条件访问策略,以要求对 Windows 设备上的 Exchange Online 和 SharePoint Online 进行令牌保护。
- 以条件访问管理员、安全管理员或全局管理员的身份登录到 Azure 门户。
- 浏览到“Microsoft Entra ID”>“安全性”>“条件访问”。
- 选择“创建新策略”。
- 为策略指定名称。 建议组织为其策略的名称创建有意义的标准。
- 在“分配”下,选择“用户或工作负载标识” 。
- 在“包括”下面,选择将要测试此策略的用户或组。
- 在“排除”下选择“用户和组”,然后选择组织的紧急访问帐户或不受限帐户。
- 在“目标资源”>“云应用”>“包括”>“选择应用”下
在“选择”下,选择预览版支持的以下应用程序:
- Office 365 Exchange Online
- Office 365 SharePoint Online
警告
应仅为这些应用程序配置条件访问策略。 选择“Office 365”应用程序组可能会导致意外失败。 对于应在条件访问策略中选择“Office 365”应用程序组的一般规则而言,这是一个例外。
选择“选择”。
- 在“条件”下,执行以下操作:
- 在“设备平台”下:
- 将“配置”设置为“是”。
- 依次选择“包括”>“选择设备平台”>“Windows”。
- 选择“完成” 。
- 在“客户端应用”下:
- 将“配置”设置为“是”。
警告
未配置客户端应用条件或将“浏览器”保持选中状态可能会导致使用 MSAL.js 的应用程序(如 Teams Web)被阻止。
- 在“新式身份验证客户端”下面,仅选择“移动应用和桌面客户端”。 将其他项目保留为未选中状态。
- 选择“完成”。
- 将“配置”设置为“是”。
- 在“设备平台”下:
- 在“访问控制”>“会话”下面,选择“要求对登录会话使用令牌保护”,然后选择“选择”。
- 确认设置,然后将“启用策略”设置为“仅限报告”。
- 选择“创建” ,以便创建启用策略所需的项目。
管理员在确认使用仅报告模式的设置后,可将“启用策略”切换开关从“仅报告”移至“开”。
捕获日志和分析
在强制实施之前和之后监视令牌保护的条件访问强制实施。
登录日志
在“仅报告”模式或“已启用”模式下,使用 Microsoft Entra 登录日志验证令牌保护强制策略的结果。
- 最低以条件访问管理员身份登录到 Microsoft Entra 管理中心。
- 浏览到“标识”>“监视和运行状况”>“登录日志”。
- 选择特定请求以确定是否已应用策略。
- 根据状态转到“条件访问”或“仅报告”窗格,然后选择需要进行令牌保护的策略名称。
- 在“会话控制”下,检查以确认是否已满足策略要求。
Log Analytics
还可以使用 Log Analytics 查询登录日志(交互式和非交互式),以查询由于令牌保护强制失败而被阻止的请求。
下面是搜索过去七天非交互式登录日志的示例 Log Analytics 查询,其中突出显示了应用程序所阻止和允许的请求。 这些查询只是示例,可能会发生更改。
备注
登录日志输出:在 2023 年 6 月下旬,“enforcedSessionControls”和“sessionControlsNotSatisfied”中使用的字符串值从“Binding”更改为了“SignInTokenProtection”。 应更新对登录日志数据的查询来反映这一更改。 示例涵盖了这两个值以包含历史数据。
//Per Apps query
// Select the log you want to query (SigninLogs or AADNonInteractiveUserSignInLogs )
//SigninLogs
AADNonInteractiveUserSignInLogs
// Adjust the time range below
| where TimeGenerated > ago(7d)
| project Id,ConditionalAccessPolicies, Status,UserPrincipalName, AppDisplayName, ResourceDisplayName
| where ConditionalAccessPolicies != "[]"
| where ResourceDisplayName == "Office 365 Exchange Online" or ResourceDisplayName =="Office 365 SharePoint Online"
//Add userPrinicpalName if you want to filter
// | where UserPrincipalName =="<user_principal_Name>"
| mv-expand todynamic(ConditionalAccessPolicies)
| where ConditionalAccessPolicies ["enforcedSessionControls"] contains '["Binding"]' or ConditionalAccessPolicies ["enforcedSessionControls"] contains '["SignInTokenProtection"]'
| where ConditionalAccessPolicies.result !="reportOnlyNotApplied" and ConditionalAccessPolicies.result !="notApplied"
| extend SessionNotSatisfyResult = ConditionalAccessPolicies["sessionControlsNotSatisfied"]
| extend Result = case (SessionNotSatisfyResult contains 'SignInTokenProtection' or SessionNotSatisfyResult contains 'SignInTokenProtection', 'Block','Allow')
| summarize by Id,UserPrincipalName, AppDisplayName, Result
| summarize Requests = count(), Users = dcount(UserPrincipalName), Block = countif(Result == "Block"), Allow = countif(Result == "Allow"), BlockedUsers = dcountif(UserPrincipalName, Result == "Block") by AppDisplayName
| extend PctAllowed = round(100.0 * Allow/(Allow+Block), 2)
| sort by Requests desc
上一个查询的结果应类似于以下屏幕截图:
以下查询示例查看了过去 7 天的非交互式登录日志,其中突出显示了用户所阻止和允许的请求。
//Per users query
// Select the log you want to query (SigninLogs or AADNonInteractiveUserSignInLogs )
//SigninLogs
AADNonInteractiveUserSignInLogs
// Adjust the time range below
| where TimeGenerated > ago(7d)
| project Id,ConditionalAccessPolicies, UserPrincipalName, AppDisplayName, ResourceDisplayName
| where ConditionalAccessPolicies != "[]"
| where ResourceDisplayName == "Office 365 Exchange Online" or ResourceDisplayName =="Office 365 SharePoint Online"
//Add userPrincipalName if you want to filter
// | where UserPrincipalName =="<user_principal_Name>"
| mv-expand todynamic(ConditionalAccessPolicies)
| where ConditionalAccessPolicies ["enforcedSessionControls"] contains '["Binding"]' or ConditionalAccessPolicies ["enforcedSessionControls"] contains '["SignInTokenProtection"]'
| where ConditionalAccessPolicies.result !="reportOnlyNotApplied" and ConditionalAccessPolicies.result !="notApplied"
| extend SessionNotSatisfyResult = ConditionalAccessPolicies.sessionControlsNotSatisfied
| extend Result = case (SessionNotSatisfyResult contains 'SignInTokenProtection' or SessionNotSatisfyResult contains 'SignInTokenProtection', 'Block','Allow')
| summarize by Id, UserPrincipalName, AppDisplayName, ResourceDisplayName,Result
| summarize Requests = count(),Block = countif(Result == "Block"), Allow = countif(Result == "Allow") by UserPrincipalName, AppDisplayName,ResourceDisplayName
| extend PctAllowed = round(100.0 * Allow/(Allow+Block), 2)
| sort by UserPrincipalName asc