联合身份凭据中的可变主体

可变主体是 OpenID Connect(OIDC)subject(sub)声明值,外部签发方基于其允许更改或重复使用的名称构建该值。 Microsoft Entra 联合身份凭据通过匹配外部颁发者令牌中的声明(尤其是 sub 声明)来建立信任。 当该主题派生自可变值时,信任关系可能会变得不明确,因为同一标识符以后可能属于不同的工作负荷。

本文介绍哪些使主题可变、可变主题引入的安全风险以及如何将信任定位到不可变主题,以便联合标识凭据仅信任你预期的工作负荷。

使主题可变的内容

当主体是从颁发者允许更改或重复使用的值派生出来时,该主体就是可变的。 常见示例包括:

  • 重命名、传输或删除并使用相同的名称重新创建的项目或存储库。
  • 重命名后重复使用的组、命名空间或组织句柄。
  • 基于用户名而不是稳定的内部 ID 的用户标识符。

相比之下,无法更改或回收不可变标识符。 它将永久地锚定到原始资源或工作负载。 如果颁发者提供不可变标识符,则应将信任锚定到这些值上。

可变主题的安全风险

将信任与可变主体进行匹配,会使联合身份凭据面临两种相关风险。

主题回收

对象重复使用是主要风险。 它可以按以下方式展开:

  1. 管理员创建一个信任基于名称的主体的联合身份凭据。
  2. 原始资源已删除、重命名或传输。
  3. 该标识符将再次可用。
  4. 另一方获取该标识符。
  5. 该方的令牌现在生成与现有联合标识凭据匹配的主题。
  6. 该方获得了原本仅供原始工作负载使用的访问权限。

悬空的联合身份凭据

悬空的联合身份凭据是指这样一种凭据:在其所信任的工作负载已不再存在后,该凭据仍然处于已配置状态。 悬空凭据尤其容易受到主体回收的影响:受信任名称现在可能会重新变为可复用,因此,来自其他工作负载的令牌也可能满足匹配条件。

租户管理员决定谁信任,Microsoft Entra提供精确表达信任的机制。 若要保持信任态势强劲,请遵循以下做法:

  • 首选不可变的主题。 如果颁发者提供不可变主体,请配置联合标识凭据或灵活联合标识凭据,使其与该不可变主体匹配,而不是与名称匹配。
  • 删除失效的凭据。 删除其工作负载已不存在的联合标识凭据。
  • 定期审阅。 定期审核联合标识凭据,以确认它们是否仍与预期工作负荷相对应,并且匹配的值保持不可变。
  • 将权限范围限定为最小。 仅授予工作负荷所需的权限,使任何不匹配都具有有限的影响。

将信任锚定到不可变主体

抵制主题回收的最强方法是信任不可变的主题。 某些颁发者允许工作负载提供一个基于稳定的内部 ID 而非名称构建的 sub 声明。 当颁发者支持此选项时,请将联合标识凭据配置为匹配不可变的主题。

可以通过两种方式匹配不可变的主题:

  • 标准联合标识凭据与 sub 值完全匹配。
  • 灵活的联合身份凭据通过使用声明匹配表达式来匹配sub声明,该表达式支持在多个分支或标记等场景中使用通配符。

GitLab:使用不可变主题

GitLab 是一个示例颁发者,它以前生成了可变主题,现在提供不可变的选项。 默认情况下,GitLab 从基于名称且可变的项目路径生成 sub 声明:

{
  "iss": "https://gitlab.com",
  "sub": "project_path:acme-group/billing-service:ref_type:branch:ref:main"
}

由于组或项目路径可能被重命名、转移或重新占用,因此,以 project_path 开头的主体会面临主体被重复利用的风险。

GitLab 通过两种方式降低此风险:

  • 平台保护。 当命名空间路径以前属于已删除或重命名的其他项目时,GitLab 会阻止 CI ID 令牌颁发。
  • 不可变的主题。 GitLab 允许项目显示一个标题,该标题以不可变的 project_id 开头,而不是 project_path。 若要启用此功能,请通过 GitLab Projects API 将 ci_id_token_sub_claim_components 设置为诸如 ["project_id", "ref_type", "ref"] 这样的值。 然后,主题以项目 ID 开头:
{
  "iss": "https://gitlab.com",
  "sub": "project_id:57382910:ref_type:branch:ref:main"
}

GitLab 主题要么以 project_path 开头,要么以 project_id 开头,绝不会同时以两者开头。 由于 project_id 只会被分配一次且不会被再次使用,因此,以其开头的主题即使在该路径之后被重命名或回收的情况下,仍会保持绑定到原始项目。

项目提供不可变主体后,请将 Microsoft Entra 信任锚定到该主体。 灵活的联合标识凭据可以匹配不可变的主题,并使用通配符覆盖每个分支和标记:

{
  "name": "gitlab-billing-service-immutable",
  "issuer": "https://gitlab.com",
  "claimsMatchingExpression": {
    "value": "claims['sub'] matches 'project_id:57382910:*'",
    "languageVersion": 1
  },
  "audiences": ["api://AzureADTokenExchange"]
}

GitLab:将所需的不可变声明添加到灵活的联合标识凭据

对于 GitLab,灵活的联合标识凭据必须与声明和以下一个或多个附加声明匹配 sub

  • project_id 标识运行作业的项目。
  • namespace_id 标识项目的命名空间。
  • user_id 标识运行作业的用户。

无论sub以还是project_path从头project_id开始,都需要这些附加声明。 包括表示预期信任边界的声明。

以下凭据与不可变项目主体匹配,并单独验证项目和命名空间:

{
  "name": "gitlab-billing-service-immutable",
  "issuer": "https://gitlab.com",
  "claimsMatchingExpression": {
    "value": "claims['sub'] matches 'project_id:57382910:*' and claims['project_id'] eq '57382910' and claims['namespace_id'] eq '<namespace-id>'",
    "languageVersion": 1
  },
  "audiences": ["api://AzureADTokenExchange"]
}

若要将信任限制为特定用户运行的作业,还匹配 user_id

{
  "name": "gitlab-billing-service-user",
  "issuer": "https://gitlab.com",
  "claimsMatchingExpression": {
    "value": "claims['sub'] matches 'project_id:57382910:*' and claims['project_id'] eq '57382910' and claims['namespace_id'] eq '<namespace-id>' and claims['user_id'] eq '<user-id>'",
    "languageVersion": 1
  },
  "audiences": ["api://AzureADTokenExchange"]
}

将占位符值替换为 GitLab ID 令牌中的 ID。 GitLab 包括 project_id每个 namespace_idID 令牌和 user_id 每个 ID 令牌中。

使用Azure CLI创建灵活的联合标识凭据

若要使用Azure CLI创建灵活的联合标识凭据,请将凭据正文保存到文件,例如credential.json,并将其发布到Microsoft Graph:az rest

az rest --method POST \
  --uri "https://microsoftgraph.chinacloudapi.cn/beta/applications/<app-object-id>/federatedIdentityCredentials" \
  --headers "Content-Type=application/json" \
  --body "@credential.json"

<app-object-id> 替换为你的应用注册的对象 ID。 Microsoft Graph在请求成功时返回创建的凭据,包括claimsMatchingExpression值。

若要配置 GitLab 主题和Microsoft Entra凭据,请参阅以下资源: