2024年API网关全面指南:性能优化与安全实践
为什么应该从静态 API 令牌迁移到 OAuth 2.0
长期以来,静态 API 标记一直用于调用外部 API 和访问第三方(如软件供应商)的资源。随着 API 标准的发展,OAuth 2.0 应运而生,它提供了更强大的安全性、更大的灵活性和更好的开发人员体验。让我们来探讨一下摒弃静态令牌转而使用 OAuth 2.0 的优势。
目录:
- 静态 API 令牌格局
- 从静态 API 令牌转向 OAuth 2.0 以提高安全性
- 拥抱 OAuth 2.0 以提高安全性
- 服务与服务之间的交互
- 基于任务的自动化
- 用户通过网络应用访问
- 逐步淘汰静态 API 标记,使用 OAuth 2.0
- API 授权迁移过程中的最佳实践
- 使用 OAuth 2.0 确保 API 调用安全
- 使用 OAuth 2.0 的下一步
静态 API 令牌格局
在安全的软件系统中,所有允许读取或修改资源访问权限的调用都必须经过授权。在调用受保护的应用程序接口时,应用程序接口需要某种方式来验证授权。静态 API 标记是一种在没有用户上下文的情况下使用记忆值向 API 提供授权信息的方法。因此,它们为开发人员提供了一种直接(但有限)的方法,用于向另一个应用程序授予 API 访问权:生成令牌,存储一次,然后继续执行任务。
如果我们看一下对 Okta 的 HTTP 调用示例,使用静态 API 令牌的请求可能如下所示:
GET /api/products HTTP/1.1
Authorization: SSWS <api_token_value>
Accept: application/json
Host: dev-1234567.okta.com
API的调用者(又称客户端)将令牌值添加到调用中。根据调用的供应商系统,API可能会将静态令牌值添加到 HTTP 标头,如授权(使用标准授权方案或专有授权方案,如 Okta 的 SSWS 方案),或添加到查询参数中。
从静态 API 标记转向 OAuth 2.0,以提高安全性
与许多技术一样,这种易用性也会相应增加风险。与寿命较短的 OAuth 2.0 令牌不同,静态令牌的寿命通常较长。设置在查询参数中的 API 标记尤其危险。浏览器会将 URL 保存在历史记录中,日志系统可能会记录整个 URL,从而比使用 HTTP 标头更容易暴露令牌。
为了更好地理解与静态 API 令牌相关的安全问题,让我们来详细研究它们的一些特性,并与作为替代方案的 OAuth 2.0 进行对比。
使用静态API令牌时的访问风险
静态令牌可能落入坏人之手,导致未经授权的访问。例如,静态令牌可能无意中被检查到源代码控制系统中,从而无意中将令牌暴露给版本库查看器和源代码控制历史。由于静态令牌的寿命较长,因此令牌暴露的风险尤其令人担忧,因为被盗令牌的后果会造成严重影响——被盗的静态令牌在手动停用或最终过期之前一直处于激活状态,从而允许未经授权的访问。
代币轮换问题
旋转静态令牌是一项具有挑战性的任务。由于令牌值会在调用者的环境或软件系统中持续存在,因此要在保持生产环境正常运行的情况下快速释放令牌、生成新的令牌并保存令牌,这对轮换的时间安排提出了挑战。
难以限制范围
为静态令牌分配有限的作用域并不简单。静态令牌通常授予对所调用 API 的全部访问权限–一种全有或全无的访问级别。为有限范围定义访问权限(如只读访问权限与写入访问权限)以及其他细粒度访问控制措施,可能无法通过静态 API 标记来实现或难以管理。
用户生成的令牌中的继承权限
用户生成的访问令牌通常采用生成令牌的用户账户权限。如果一个管理员生成了令牌,那么它就可以完全访问管理员可以执行的所有资源和操作。为了减轻这种担忧,有时企业会设置范围有限的服务账户来生成令牌。然而,这样做会导致用户管理费用增加,并有可能占用许可证。
审计挑战
跟踪静态令牌的使用情况非常棘手。审计的核心需求是跟踪用户与 API 的交互。但是,静态 API 标记通常没有用户上下文,而且对 API 的所有调用都使用相同的静态标记,因此可能无法识别特定用户对 API 的操作。
静态令牌很脆弱
当用户退出组织时,与用户账户关联的令牌就会失效。有些组织使用服务账户来抵消这一措施,但这会带来访问风险和管理开销。
有一种更好、更安全的方法可以访问 API,从而降低与静态 API 标记相关的风险:使用行业标准 OAuth 2.0 流程。
拥抱 OAuth 2.0 以提高安全性
OAuth 2.0 几乎可以满足任何场景的需求,以取代静态令牌(如 Okta 的 SSWS 令牌)。但与静态令牌不同的是,OAuth 规范包括令牌轮换、基本授权决定等日常使用案例所必需的定义和安全实践,例如:
- 服务与服务之间的自动交互,用于在没有用户上下文的情况下处理幕后 API 调用
- 通过命令行操作实现基于任务的自动化
- 根据用户的访问级别限制应用程序数据和操作,以保持授权安全性
OAuth 2.0 通过向集中式授权服务器发送请求中的配置属性,支持上述每种日常使用情况。一般的交互是这样一个多步骤过程:
- 调用者向授权服务器请求一个访问令牌,并传递用例所需的配置属性。授权服务器返回一个短期访问令牌。令牌的寿命取决于用例。有些用例本质上更安全,因此使用期限会有所不同。下面的 HTTP 请求显示了一个检索令牌的请求示例,但不包括所需的配置属性。你将在接下来的示例中看到必要的配置属性。
POST /token HTTP/1.1
Host: authorization-server.com
<plus required configuration properties + credentials>
- 调用者使用 API 请求中的令牌值作为 HTTP 请求的授权头中的值,并使用承载器方案。HTTP 请求的内容如下:
GET /api/products HTTP/1.1
Authorization: Bearer <api_token_value>
Accept: application/json
Host: dev-1234567.okta.com
OAuth 2.0 的内容非常丰富,因此请查看本文章末尾的资源以了解更深入的内容。我们不会在这篇文章中深入探讨所有细节,但会针对所提到的用例提供具体信息,并为每个用例提供高级摘要。
让我们看看每个用例,了解 OAuth 2.0 如何支持这些用例。
服务与服务之间的互动
您可能有调用其他应用程序接口获取数据或执行操作的后端流程。OAuth 2.0 支持服务对服务或机器对机器的请求,而不需要使用称为 “客户端凭证 “的授权流来关联调用的用户上下文。
使用客户端凭据相对于 API 令牌的优势
与静态 API 密钥相比,使用 OAuth 2.0 进行服务间交互具有巨大优势,尤其是在称为作用域的访问级别方面。在 OAuth 2.0 中,您可以将访问令牌获取的作用域作为调用配置属性的一部分来识别,以检索您的令牌。您需要确保您调用的 API 支持基于作用域的访问,并且您已经授予了所需作用域的访问权限。例如,Okta API为资源API定义了多个作用域,如okta.users.read和okta.users.write。您可以授予其中一个或两个作用域的访问权限。
客户端凭证流程与使用服务账户进行 API 授权不同。OAuth 2.0 支持以标准格式进行机器到机器的交互,而无需占用用户席位来模拟自动化用户。请注意,当你定义作用域时,是在服务应用程序的配置和授权服务器中定义的,而不是在用户级别定义的。这样做的好处是,你可以控制整个服务应用程序的资源访问,而不是通过应用程序中的某个用户。您不必再担心服务账户的管理问题,而且可以确信应用程序访问的范围不会超过您所允许的范围。
使用客户端凭据检索访问令牌
要检索此流程的访问令牌,需要发送指定授权流程类型、凭据和访问令牌作用域的属性。定义授权流的 OAuth 2.0 术语称为 grant_type;对于客户凭据流,你要传递的值是 client_credentials。
凭证可以是用客户端私钥签名的加密安全 JSON Web 令牌(JWT),也可以是从授权服务器生成的秘密值。私钥 JWT 更为安全,因为你不会冒着暴露秘密值的风险,从而意外地创建与静态 API 令牌类似的访问问题。您将生成一对公钥/私钥,并在 JSON 网络密钥集 (JWKS) 中注册公钥或将其上传到授权服务器,这样授权服务器就有了验证 JWT 签名的公钥。有关此过程的更多信息,请参阅通过服务应用程序为 Okta 实施 OAuth 文档。
用于检索访问令牌的 HTTP 调用示例可能如下所示:
POST /token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Accept: application/json
Host: authorization-server.com
grant_type=client_credentials
&scope=okta.users.read
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=<generated_jwt>
获得访问令牌后,您可以将其作为标头添加到 API 调用中:Authorization: Bearer access_token
了解更多有关使用 .NET 进行服务对服务 API 调用和在 Python 中创建私有 JWT 的信息,请参阅《保护您的 .NET 6 Web API》和《在三个 Python 命令中设置私钥 JWT 流程》。
基于任务的自动化
命令行终端(如 Bash 和 PowerShell)是在终端窗口内运行单个命令或脚本的无浏览器进程。与之前的服务间交互不同,该流程涉及用户上下文。您希望在授权上下文下运行命令行操作,这意味着您只能访问您应该拥有的 API 资源。OAuth 2.0 通过一个名为设备流(Device Flow)的流程支持这种用例。
在此流程中,获取访问令牌是一个多步骤过程。在像服务对服务用例那样直接请求访问令牌之前,首先要将客户 ID 发送到授权服务器上的一个特殊端点,从而获得唯一的验证码:
POST /device/authorize HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: authorization-server.com
client_id=<client_ID>
/device/authorize 调用的响应会返回两个唯一代码(设备代码和用户代码)和一个验证 URI。通过浏览器导航到验证 URI 并输入用户代码,确认授权请求。
要检索访问令牌,需要在调用中添加 grant_type 属性,以指定用例的 OAuth 2.0 流程和设备代码作为配置属性:
POST /token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: authorization-server.com
client_id=<client_ID>
&grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code=device_code
在 Authorization.Bearer access_token 头信息中使用响应中的访问令牌:Bearer access_token 头信息。
有关命令行设备代码流程的更多信息,请参阅《使用 Java 从命令行进行身份验证》(Authenticate from the Command Line with Java)。
用户通过 Web 应用程序访问
最后一个用例确保用户的授权适用于使用网络应用程序时的 API 访问。OAuth 2.0 支持将应用程序的操作限制在用户可以进行的操作和看到的数据的子集范围内。在此流程中,您可以使用用户上下文来申请资源授权,此外,有了用户上下文,对单个用户调用的审计和跟踪也会变得更加简单。此用例的 OAuth 2.0 流程结合了标准流程、授权代码流程和一个额外安全层的扩展,称为代码交换证明密钥(PKCE)。
带有 PKCE 的授权代码流需要多个步骤才能检索访问令牌,并涉及通过同意和验证进行的用户交互,从而实现多因素身份验证。在此流程中,您将把用户重定向到授权服务器,在请求访问令牌之前获取授权码。
重定向 URL 包含特定于请求的属性,以便用户完成登录,包括使用 response type 属性指定请求类型和客户端 ID 等属性。
https://<identity_provider_uri>/authorize?response_type=code&client_id=<client_ID>&+remaining props
当用户成功完成登录后,身份提供程序会重定向到应用程序,并在授权响应中提供授权代码。要检索访问令牌,需要输入该流程的授权类型、授权代码和其他特定于请求的元数据。
POST /token HTTP/1.1
Host: authorization-server.com
grant_type=code
&code=<code_response>
&client_id=<client_ID>
有了访问令牌后,网络应用程序就可以在 “授权”(Authorization:Bearer access_token 标头。
有关授权代码流的更多信息,请参阅《SPA 的身份验证和授权工作原理》和《将 PKCE 与 OAuth 2.0 和 Spring Boot 结合使用以提高安全性》。
逐步淘汰静态 API 令牌并使用 OAuth 2.0
软件迁移需要时间和规划,制定一个好的游戏计划会有所帮助。考虑以小步渐进的方式过渡到 OAuth 2.0。
从静态 API 标记迁移的第一步是确保您使用的 API 支持 OAuth 2.0。如果要调用第三方 API,请向供应商咨询。如果您的 API 是内部 API,则需要添加 OAuth 支持。一旦验证您调用的 API 支持 OAuth,您就需要 OAuth 应用程序的客户端 ID,并启用访问措施(如作用域)。
以管理员身份登录到 Okta 管理控制台后,您就可以确定是否在 Okta 环境中使用静态 API 标记。在侧边栏中,导航到 “安全性”>”API”,然后选择 “令牌 “选项卡,就可以看到为 Okta 组织创建的所有令牌。通过该视图可以了解 API 令牌的使用情况、用例和访问级别(例如,超级管理员创建的令牌具有完全访问权)。您需要对每个令牌进行评估,并将其转换为使用 OAuth。
让我们重点关注自动服务对服务调用的用例。将 OAuth 2.0 集成到您的应用程序接口中是一件非常困难的事情,因此让我们来看看如何逐步实现这一目标。在进行任何代码修改之前,您可以在本地手动尝试这些步骤,并使用您最喜欢的 HTTP 客户端请求一个令牌,然后使用 OAuth 2.0 调用 API。本例使用带有私钥 JWT 的客户端凭证流。步骤如下
- 生成公钥/私钥对
- 使用公钥/私钥对生成私钥 JWT
- 请求访问令牌
- 将访问令牌添加到 API 请求中
- 将 OAuth 纳入应用系统
生成公钥/私钥对
您可以按照以下说明生成一对公钥/私钥。如果您不想自己生成公钥对,Okta 的管理控制台可以生成一个公钥对/私钥对用于测试。
生成私钥JWT
按照 “使用服务应用程序为 Okta 实施 OAuth “文档中的说明,使用公钥/私钥对创建私钥 JWT。在这一步,更重要的是了解这一切是如何工作的,而不是自动执行步骤,因此您可以将公/私钥对存储在本地,使用安装在您机器上的工具,并将包含您公钥的 JWKS 粘贴到测试 JWT 生成器网站上。
请求访问令牌
使用你的私钥 JWT 请求访问令牌,在你喜欢的 HTTP 客户端中发出 HTTP 请求,不要添加任何作用域。在没有适当作用域的情况下使用访问令牌时应该会出错,但测试出错和路径正确是好事。访问令牌会在某个时间过期,你会在访问令牌响应中看到过期时间。访问令牌是响应的一部分。您将使用整个字符串作为访问令牌。
将访问令牌添加到您的 API 请求中
使用 HTTP 客户端,使用访问令牌调用 API。您将添加一个授权标头并使用承载器方案,这样您的 API 调用就会有如下格式的标头:
Authorization: Bearer <access_token>
调用 API 时,您应该会收到 HTTP 状态为 403 的错误信息。耶!成功了吗?是的,一次成功的错误案例测试。现在您知道如果配置范围时出现错误会出现什么错误了。让我们把作用域添加到访问令牌请求中,然后再试一次。
这次,使用私钥 JWT 申请访问令牌,并为 API 调用添加作用域。在 API 调用中使用访问令牌。真相时刻!你应该看到一个成功的响应。如果再次出现 403 错误,就说明存在配置错误。如果出现这种情况,请仔细检查请求作用域,并确保 OAuth 2.0 应用程序已启用您请求的作用域。
在系统中添加OAuth 2.0授权
现在,您已经成功地手动完成了服务器到服务调用的 OAuth 2.0 授权,可以将这些步骤的各个方面引入您的系统。在开发或测试环境中,尝试使用访问令牌。您需要更改 API 以使用带承载器方案格式的授权标头,并用手动生成的访问令牌替换现有的静态 API 令牌,以确保您的 API 调用在不自动请求访问令牌的情况下也能正常工作。
当您确信 API 系统能成功处理访问令牌时,就可以在代码中添加请求访问令牌的步骤了。您的技术栈可能有 SDK,至少可以协助完成部分(或全部)请求步骤。Okta 的管理 SDK 可以代表您处理 OAuth 2.0 握手。Java Spring 用户可以查看 Spring Security 文档,.NET Core 用户可以查看 Microsoft.AspNetCore.Authentication 作为示例。请记住,在进行过程中要测试您的工作是否顺利、是否存在错误案例以及是否正确处理了访问令牌过期等情况。
在将更改部署到生产环境之前,请确保使用生产质量工具在内部生成 JWKS 并安全地管理私钥。
API授权迁移期间的最佳实践
立即从静态令牌过渡并不总是可行,特别是在大型组织中或者如果您有大量 API 需要更新。如果您仍在 Okta 中使用 SSWS 令牌,那么您希望在过渡到 OAuth 2.0 之前降低风险。最佳实践包括:
- 使用 OAuth 2.0 实施新的 API 调用或代码。
- 遵循最小特权原则。例如:
不要使用个人帐户建立服务帐户。
对服务帐户使用自定义管理员角色,仅授予基本权限。
使用组和全局会话策略来防止服务帐户的交互式登录。
使用 OAuth 2.0 保护 API 调用
从静态令牌转向 OAuth 2.0 不仅仅是为了拥抱新技术,更是为了转向一个更安全、更灵活、对开发人员更友好的生态系统。虽然静态令牌有其存在的时间和位置,但很明显,未来将是动态的、以用户为中心的、安全的 OAuth 2.0 解决方案。
请记住,随着技术的进步,保持更新并适应更好的实践不仅有益,而且至关重要。迁移到 OAuth 2.0,踏上更安全、更简化、更高效的开发之旅。
OAuth 2.0 的后续步骤
如果您觉得这篇文章有趣,您可能会喜欢这些帖子。
查看 Okta 的API 安全开发人员指南,开始迁移到 OAuth 2.0:
- 本指南讨论如何创建发送者限制的访问令牌,这是一种应用程序级机制,用于防止不同端点处的令牌重放。
- 本指南介绍如何使用范围内的 OAuth 2.0 访问令牌与 Okta API 进行交互。 为 Okta 配置 OAuth:服务应用程序
- 本指南介绍如何使用服务应用程序的作用域 OAuth 2.0 访问令牌与 Okta API 进行交互。
- 使用 Okta 添加授权来保护您的 API。完成后,您将拥有一个安全的 REST API 应用程序,用于验证传入请求。
- 本指南介绍如何配置 Okta 组织以接收来自第三方提供商的风险事件。使用 OAuth 2.0 组织之间的安全 API 连接
- 本指南介绍如何在多租户解决方案中使用 OAuth 2.0 安全地设置 Okta 中心和辐射组织以同步用户和组。使用Org2Org 集成应用程序的 OAuth 2.0 客户端凭证流将 Okta org API 访问连接到范围数据。
- 本指南说明如何在授权请求中包含 acr_values 参数以增强最终用户的保证。
原文链接:Why You Should Migrate to OAuth 2.0 From Static API Tokens