API 保护
作为开发人员,在保护 API 时,重点是授权。 若要调用资源的 API,应用程序需要获取应用程序授权。 资源本身必须强制实施授权。 本文介绍通过注册、定义权限和同意以及强制访问来保护 API 的最佳做法,以实现零信任目标。
注册受保护的 API
若要使用 Microsoft Entra ID (Microsoft Entra ID) 保护 API,首先“注册 API”,之后可以“管理已注册的 API”。 在 Microsoft Entra ID 中,API 是具有特定应用注册设置的应用,该设置将其定义为可以授权其他应用程序访问的资源或 API。 在 Microsoft Entra 管理中心,Microsoft 标识开发人员、应用注册是租户中的 API,既可以是业务线 API,也可以是 SaaS 提供商的服务,这些服务都有 Microsoft Entra ID 保护的 API。
在注册期间,可以定义调用应用程序如何引用 API 及其委托权限和应用程序权限。 应用注册可以表示同时具有客户端应用程序和 API 的解决方案。 但在本文中,我们将讨论独立资源公开 API 的情况。
通常情况下,API 不执行身份验证或直接请求授权。 API 验证调用应用提供的令牌。 API 没有交互式登录,因此你不需要注册重定向 URI 或应用程序类型等设置。 API 从调用这些 API 的应用程序获取令牌,而不是通过与 Microsoft Entra ID 交互。 对于 Web API,使用 OAuth2 访问令牌进行授权。 Web API 验证持有者令牌,以授权调用方。 不接受 ID 令牌作为权限证明。
默认情况下,Microsoft Entra ID 会将 User.Read
添加到任何新应用注册的 API 权限。 你将为大多数 Web API 删除此权限。 仅在 API 调用另一个 API 时,Microsoft Entra ID 才需要 API 权限。 如果 API 不调用另一个 API,在注册 API 时删除 User.Read
权限。
需要为 API 指定一个唯一标识符(称为应用程序 ID URI),需要访问 API 的客户端应用会请求该标识符以获得调用 API 的权限。 应用程序 ID URI 需要在所有 Microsoft Entra 租户中是唯一的。 可以使用 api://<clientId>
(门户中的默认建议),其中 <clientId>
是已注册 API 的应用程序 ID。
要为调用 API 的开发人员提供更易记名称,可以使用 API 的地址作为应用程序 ID URI。 例如,可以使用 https://API.yourdomain.com
,其中 yourdomain.com
必须是 Microsoft Entra 租户中已配置发布者域。 Microsoft 验证你是否拥有域的所有权,以便你能将其用作 API 的唯一标识符。 你不需要在此地址有代码。 API 可以随心所欲地使用,但最好是使用 API 的 HTTPS 地址作为应用程序 ID URI。
定义具有最低特权的委托权限
如果 API 由具有用户的应用程序调用,则你必须至少定义一个委托权限(请参阅应用注册“公开 API”中的添加范围)。
API 提供对组织数据存储的访问权限,会吸引想要访问该数据的攻击者注意。 使用“最低权限访问”的零信任原则设计权限,而不是只拥有一个委派权限。 如果所有客户端应用都以完全特权访问权限开始,则之后很难进入最低特权模型。
通常,开发人员会陷入使用单个权限的模式,如“以用户身份访问”或“用户模拟”(这是一个常见短语,尽管在技术上并不准确)。 此类的单个权限仅允许对 API 进行完全特权访问。
声明最低特权范围,使应用程序不会容易受到入侵或被用于执行你从未想过的任务。 在“API 权限”中定义多个范围。 例如,将范围与读取和更新数据分开并考虑提供只读权限。 “写入访问权限”包括执行创建、更新和删除操作的特权。 客户端永远不应要求对只读数据提供写入访问权限。
你的 API 处理敏感数据时,请考虑使用“标准”和“完全”访问权限。 限制敏感属性,使标准权限不允许访问(例如 Resource.Read
)。 然后实现“完全”访问权限(例如 Resource.ReadFull
),这会返回属性和敏感信息。
始终评估你所请求的权限,确保这些权限是完成工作而需要的绝对最低的一组特权。 避免请求更高的特权权限。 而是为每个核心场景创建单个权限。 有关此方法的良好示例,请参阅 Microsoft Graph 权限参考。 找到并使用适当数量的权限来满足你的需求。
定义用户同意和管理员同意
作为范围定义的一部分,确定在特定范围内执行操作的范围是否需要管理员同意。
作为 API 设计师,可以提供有关哪些范围可以安全地要求用户同意的指导。 但是,租户管理员可以配置租户,以便所有权限都需要获得管理员同意。 如果将范围定义为需要管理员同意,则该权限始终需要管理员同意。
决定用户同意还是管理员同意时,有两个主要注意事项:
权限背后的操作范围是否影响多个用户。 如果权限允许用户选择哪个应用程序只能访问他们自己的信息,则用户同意可能是合适的。 例如,用户可以同意为电子邮件选择首选应用程序。 但是,如果权限背后的操作涉及多个用户(例如,查看其他用户的完整用户配置文件),则将该权限定义为需要管理员同意。
权限背后的操作范围是否范围广泛。 例如,广泛的范围是指,当某个权限使应用能够更改租户中的所有内容,或执行可能不可逆的内容时。 广泛的范围表示你需要管理员同意,而不是用户同意。
如果你有疑问,请采取保守的做法并要求管理员同意。 清楚、简洁地描述权限字符串中同意的后果。 假设读取说明字符串的个人对 API 或产品不熟悉。
避免以更改权限语义的方式将 API 添加到现有权限。 重载现有权限会削弱客户端以前授予同意的理由。
定义应用程序权限
生成无用户应用程序时,无法提示某个用户输入用户名和密码或进行多重身份验证 (MFA)。 如果没有用户的应用程序(例如工作负载、服务和守护程序)调用 API,则必须为 API 定义应用程序权限。 定义应用程序权限时,可以使用应用程序角色而不是使用范围。
与委托权限一样,可以提供精细的应用程序权限,以便调用 API 的工作负载可以遵循最低特权访问权限并符合零信任原则。 避免仅发布一个应用角色(应用权限)和一个范围(委托的权限),或向每个客户端公开所有操作。
工作负载使用“客户端凭证”进行身份验证,并使用“.default
范围”请求令牌,如以下示例代码所示。
// With client credentials flows the scopes is ALWAYS of the shape "resource/.default", as the
// application permissions need to be set statically (in the portal or by PowerShell),
// and then granted by a tenant administrator
string[] scopes = new string[] { "https://kkaad.onmicrosoft.com/webapi/.default" };
AuthenticationResult result = null;
try
{
result = await app.AcquireTokenForClient(scopes)
.ExecuteAsync();
Console.WriteLine("Token acquired \n");
}
catch (MsalServiceException ex) when (ex.Message.Contains("AADSTS70011"))
{
// Invalid scope. The scope has to be of the form "https://resourceurl/.default"
// Mitigation: change the scope to be as expected
Console.WriteLine("Scope provided is not supported");
}
当应用前没有用户,并且应用程序权限启用广泛的操作时,权限需要管理员同意。
强制实施访问权限
确保 API 通过验证和解释调用应用程序在 HTTPS 请求的授权标头中作为持有者令牌提供的访问令牌来强制实施访问权限。 可以通过验证令牌、管理元数据刷新,以及强制实施范围和角色来强制实施访问权限,如以下部分所述。
验证令牌
API 收到令牌后,必须“验证令牌”。 验证可确保令牌来自未采样且未修改的正确发放者。 检查签名,因为你没有像使用 ID 令牌那样,直接从 Microsoft Entra ID 获取令牌。 在 API 从网络上不受信任的来源接收令牌后验证签名。
由于 JSON Web 令牌签名验证存在已知漏洞,请使用维护良好的成熟标准令牌验证库。 身份验证库(例如 Microsoft.Identity.Web)使用适当的步骤并缓解已知漏洞。
(可选)扩展令牌验证。 使用租户 ID (tid
) 声明来限制 API 可以获取令牌的租户。 使用 azp
和 appid
声明以筛选可调用 API 的应用。 使用对象 ID (oid
) 声明进一步缩小对单个用户的访问范围。
管理元数据刷新
始终确保令牌验证库能有效地管理所需的元数据。 在这种情况下,元数据就是 Microsoft 用来签署 Microsoft Entra 令牌的公钥(私钥对)。 当库验证这些令牌时,它们会从已知的 Internet 地址获取我们发布的公共签名密钥列表。 确保宿主环境有适当的时机来获取这些密钥。
例如,旧库有时会被硬编码为每 24 小时更新一次这些公开签名密钥。 请考虑 Microsoft Entra ID 必须快速轮换这些密钥,并且你下载的密钥不包含新的轮换密钥的情况。 API 可能在等待其元数据刷新周期时,一天处于离线状态。 参考特定的元数据刷新指南,以确保正确获取元数据。 如果使用库,确保它在合理的时间范围内处理该元数据。
强制实施范围和角色
验证令牌后,API 会查看令牌中的声明以确定其工作原理。
对于委托的权限令牌,让 API 检查范围 (scp
) 声明,以查看应用程序同意执行的操作。 检查对象 ID (oid
) 或主题密钥 (sub
) 声明,以查看应用程序正为其工作的用户。
然后让 API 检查,以确保用户也有权限访问请求的操作。 如果 API 定义了分配到用户和组的角色,请让 API 检查令牌中是否存在任何角色声明,并采取相应的操作。 应用程序权限令牌没有范围 (scp
) 声明。 相反,请让 API 检查角色声明以确定工作负载收到了哪些应用程序权限。
在 API 验证令牌和范围并处理对象 ID (oid
)、使用者密钥 (sub
) 和角色声明后,API 可以返回结果。
后续步骤
- 受 Microsoft 标识同意框架保护的 API 示例可帮助你设计最低特权应用程序权限策略,以获得最佳用户体验。
- 从另一个 API 调用 API 可帮助你在一个 API 需要调用另一个 API 时确保零信任,并在应用程序代表用户工作时安全地开发应用程序。
- 自定义令牌介绍了可以在 Microsoft Entra 令牌中接收的信息。 它说明如何自定义令牌以提高灵活性和控制,同时提高最低特权下的应用程序零信任安全性。
- 在令牌中配置组声明和应用角色介绍了如何使用应用角色定义来配置应用,以及如何将安全组分配给应用角色。 这些方法有助于提高灵活性和控制,同时提高最低特权下的应用程序零信任安全性。
- 获取访问资源的授权有助于了解在获取应用程序的资源访问权限时如何最好地确保零信任。
- 请求需要管理同意的权限介绍了当应用程序权限需要管理同意时的权限和同意体验。
- 在本快速入门:使用 Microsoft 标识平台保护 Web API 中,了解如何通过限制只有授权帐户才能访问其资源,以保护 ASP.NET Web API。
- 在此教程 - 在 Azure API Management 中转换和保护 API 中,了解如何配置常用策略,以隐藏 API 的 HTTP 响应中的技术堆栈信息,或原始 URL。