亚马逊AWS官方博客

使用 AWS IAM 出站身份联合验证简化对外部服务的访问



在构建跨多个云提供商或与外部服务集成的应用程序时,开发人员面临着持续的挑战:安全地管理凭证。传统方法需要存储 API 密钥和密码等长期凭证,从而产生安全风险和运营开销。

今天,我们宣布推出一项名为 AWS Identity and Access Management(IAM)出站身份联合验证的新功能,客户可以使用该功能将其 Amazon Web Services(AWS)身份安全地联合到外部服务,而无需存储长期凭证。现在,您可以使用短期 JSON Web 令牌(JWT),通过各种第三方提供商、软件即服务(SaaS)平台和自托管应用程序对您的 AWS 工作负载进行身份验证。

借助此特征,IAM 主体(例如 IAM 角色和用户)能够获得经加密签名的 JWT,以维护其 AWS 身份。外部服务,例如第三方提供商、SaaS 平台和本地应用程序,可以通过验证其签名来验证令牌的真实性。成功验证后,您可以安全地访问外部服务。

工作原理
使用 IAM 出站身份联合验证,您可以将 AWS IAM 凭证交换为短期 JWT。这可以减轻与长期凭证相关的安全风险,同时启用一致的身份验证模式。

我们看看在 AWS 上运行的应用程序需要与外部服务进行交互的场景。要访问外部服务的 API 或资源,您的应用程序调用 AWS Security Token Service(AWS STS)`GetWebIdentityToken` API 来获取 JWT。

下图显示了此流程:

  1. 在 AWS 上运行的应用程序通过调用 GetWebIdentityToken API 向 AWS STS 请求令牌。应用程序使用其从底层平台获得的现有 AWS 凭证(例如 Amazon EC2 实例配置文件、AWS Lambda 执行角色或其他 AWS 计算服务)对此 API 调用进行身份验证。
  2. AWS STS 返回经过加密签名的 JSON Web 令牌(JWT),该令牌断言您的应用程序的身份。
  3. 您的应用程序将 JWT 发送到外部服务进行身份验证。
  4. 外部服务从 JSON Web 密钥集(JWKS)端点获取验证密钥,进而验证令牌的真实性。
  5. 外部服务使用这些验证密钥验证 JWT 的签名,并确认该令牌是真实的,并且是由 AWS 颁发的。
  6. 成功验证后,外部服务将 JWT 交换为自己的凭证。然后,您的应用程序可以使用这些凭证来执行其预期操作。

设置 AWS IAM 出站身份联合验证
要开始使用此特征,我需要为我的 AWS 账户启用出站身份联合验证。我导航到 IAM,然后在左侧导航窗格中的访问管理下选择账户设置

启用该特征后,AWS 为我的 AWS 账户生成一个唯一的颁发者 URL,该账户托管 OpenID Connect(OIDC)发现端点,位于 /.well-known/openid-configuration/.well-known/jwks.json。OpenID Connect(OIDC)发现端点包含令牌验证所需的密钥和元数据。

接下来,我需要配置 IAM 权限。我的 IAM 主体(角色或用户)必须拥有 sts:GetWebIdentityToken 权限才能请求令牌。

例如,以下身份策略指定了对 STS GetWebIdentityToken API 的访问权限,从而使 IAM 主体能够生成令牌。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sts:GetWebIdentityToken",
      "Resource": "*",
    }
  ]
}

在此阶段,我需要配置外部服务,进而信任并接受我的 AWS 账户颁发的令牌。具体步骤因服务而异,但通常涉及:

  1. 将我的 AWS 账户颁发者 URL 注册为可信身份提供者
  2. 配置要验证的声明(受众、主题模式)
  3. 将令牌声明映射到外部服务中的权限

我们开始吧
现在,我向您介绍一个示例,该示例同时显示了客户端令牌生成和服务器端验证过程。

首先,我调用 STS GetWebIdentityToken API 来获取一个可以断言我的 AWS 身份的 JWT。在调用 API 时,我可以将目标受众、签名算法和令牌生命周期指定为请求参数。

  • Audience:在 JWT 中填充 `aud` 声明,识别令牌的预期接收者(例如,“my-app”)
  • DurationSeconds:以秒为单位的令牌生命周期,从 60 秒(1 分钟)到 3600 秒(1 小时)不等,默认为 600 秒(5 分钟)
  • SigningAlgorithm:选择 ES384(使用 P-384 和 SHA-384 的 ECDSA)或 RS256(使用 SHA-256 的 RSA)
  • Tags(可选):在令牌中以自定义声明形式显示的键值对数组,您可以使用该数组包含其他上下文,使外部服务能够实现精细的访问控制

以下是使用适用于 Python 的 Amazon SDK(Boto3)获取身份令牌的示例。我也可以使用 AWS 命令行界面(AWS CLI)执行此操作。


import boto3

sts_client = boto3.client('sts')
response = sts_client.get_web_identity_token(
    Audience=['my-app'],
    SigningAlgorithm='ES384',  # or 'RS256'
    DurationSeconds=300
)
jwt_token = response['IdentityToken']
print(jwt_token)

这将返回一个已签名的 JWT,我可以使用任何 JWT 解析器对其进行检查。

{
eyJraWQiOiJFQzM4NF8wIiwidHlwIjoiSldUIiwiYWxnIjoiRVMzODQifQ.hey<REDACTED FOR BREVITY>...

我可以使用任何 JWT 解析器来解码令牌,比如这个 JWT Debugger。令牌标头显示该令牌是使用 ES384(ECDSA)签名的。


{
  "kid": "EC384_0",
  "typ": "JWT",
  "alg": "ES384"
}

此外,有效载荷包含标准的 OIDC 声明以及 AWS 特定的元数据。标准的 OIDC 声明包括主题(“sub”)、受众(“aud”)、颁发者(“iss”)等。

{
  "aud": "my-app",
  "sub": "arn:aws:iam::ACCOUNT_ID:role/MyAppRole",
  "https://sts.amazonaws.com/": {
    "aws_account": "ACCOUNT_ID",
    "source_region": "us-east-1",
    "principal_id": "arn:aws:iam::ACCOUNT_ID:role/MyAppRole"
  },
  "iss": "https://abc12345-def4-5678-90ab-cdef12345678.tokens.sts.global.api.aws",
  "exp": 1759786941,
  "iat": 1759786041,
  "jti": "5488e298-0a47-4c5b-80d7-6b4ab8a4cede"
}

AWS STS 还通过特定身份的声明(例如账户 ID、组织 ID 和主体标签)和会话上下文来丰富令牌。这些声明提供了有关发起令牌请求的计算环境和会话的信息。根据请求主体的会话上下文,AWS STS 会在适用时自动包含这些声明。您还可以通过将请求标签传递给 API 调用来向令牌添加自定义声明。要了解有关 JWT 中提供的声明的更多信息,请访问文档页面

注意 iss(颁发者)的声明。这是特定于您的账户的颁发者 URL,外部服务用来验证令牌是否来自可信的 AWS 账户。外部服务可以通过使用 AWS 的验证密钥来验证 JWT 的签名,该验证密钥位于颁发者 URL 的 /.well-known/jwks.json 端点托管的公共 JSON Web 密钥集(JWKS)端点。

现在,我们看看外部服务如何处理这个身份令牌。

以下是外部服务可用于验证 AWS 令牌的 Python 示例片段:


import json
import jwt
import requests
from jwt import PyJWKClient

# Trusted issuers list - obtained from EnableOutboundFederation API response
TRUSTED_ISSUERS = [
    "https://EXAMPLE.tokens.sts.global.api.aws",
    # Add your trusted AWS account issuer URLs here
    # Obtained from EnableOutboundFederation API response
]

def verify_aws_jwt(token, expected_audience=None):
    """Verify an AWS IAM outbound identity federation JWT"""
    try:
        # Get issuer from token
        unverified_payload = jwt.decode(token, options={"verify_signature": False})
        issuer = unverified_payload.get('iss')

 	# Verify issuer is trusted
        if not TRUSTED_ISSUERS or issuer not in TRUSTED_ISSUERS:
            raise ValueError(f"Untrusted issuer: {issuer}")

        # Fetch JWKS from AWS using PyJWKClient
        jwks_client = PyJWKClient(f"{issuer}/.well-known/jwks.json")
        signing_key = jwks_client.get_signing_key_from_jwt(token)

        # Verify token signature and claims
        decoded_token = jwt.decode(
            token,
            signing_key.key,
            algorithms=["ES384", "RS256"],
            audience=expected_audience,
            issuer=issuer
        )
        return decoded_token
    except Exception as e:
        print(f"Token verification failed: {e}")
        return None

使用 IAM 策略控制对令牌生成的访问权限
IAM 主体(例如角色或用户)必须在其 IAM 策略中拥有 sts:GetWebIdentityToken 权限,才能请求令牌进行外部服务身份验证。AWS 账户管理员可以在所有相关的 AWS 策略类型中配置此权限,例如身份策略、服务控制策略(SCP)、资源控制策略(RCP)和虚拟私有云端点(VPCE)策略,进而控制其账户中的哪些 IAM 主体可以生成令牌。

此外,管理员可以使用新的条件键来指定签名算法(sts:SigningAlgorithm)、允许的令牌受众(sts:IdentityTokenAudience)和最长令牌生命周期(sts:DurationSeconds)。要了解有关条件键的更多信息,请访问 IAM 和 STS 条件键文档页面。

需要了解的其他事项
以下是有关此次发布的关键详细信息:

访问 AWS IAM 控制台并在您的 AWS 账户中启用该特征,开始使用 AWS IAM 出站身份联合验证。有关更多信息,请访问将 AWS 身份联合到外部服务文档页面。

祝您构建顺利!
Donnie

AWS 架构师中心: 云端创新的引领者

探索 AWS 架构师中心,获取经实战验证的最佳实践与架构指南,助您高效构建安全、可靠的云上应用