GitHub Actions 需要 OIDC 受众约束

Lobsters Hottest 工具

摘要

这篇博客文章认为,与 GitLab CI/CD 不同,GitHub Actions 缺乏静态的 OIDC 受众约束,而随着基于 OIDC 的联合身份验证越来越普遍,这一设计缺陷带来了日益增长的安全风险。

<p><a href="https://lobste.rs/s/ipt1em/github_actions_needs_oidc_audience">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/08/10 14:59

# GitHub Actions 需要 OIDC 受众约束 来源:https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints ## ENOSUCHBLOG ## *编程、哲学、骑行。* - 首页 (https://blog.yossarian.net/) - 标签 (https://blog.yossarian.net/tags) - 系列 (https://blog.yossarian.net/series) - 收藏 (https://blog.yossarian.net/favorites) - 归档 (https://blog.yossarian.net/archive) - 主站 (https://yossarian.net/) - TILs (https://yossarian.net/til) --- ## *2026 年 8 月 10 日*标签:dear-github (https://blog.yossarian.net/tags#dear-github),oss (https://blog.yossarian.net/tags#oss),security (https://blog.yossarian.net/tags#security) --- **TL;DR**:GitHub Actions 应该允许最终用户表达*受众约束*,使攻击者更难以在使用了独立 OIDC 承载作业的服务之间进行横向移动。他们可以通过相对较小的语法调整来实现这一点,尽管后端的影响可能并不小。 与许多 CI/CD 提供商一样,GitHub Actions 通过 OpenID Connect (https://openid.net/developers/how-connect-works/)(OIDC)提供可验证的机器身份[1](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#fn:ident)。 这些身份之所以很出色,原因有很多,尤其是它们允许在 GitHub Actions 上运行的工作流与(第三方)服务进行联合,而*无需* GitHub 对每次交互都进行中介和预先批准。 这就是 Trusted Publishing (https://docs.pypi.org/trusted-publishers/) 和 Sigstore (https://sigstore.dev/) 两者运作的支柱:GitHub Actions 上的一个工作流将其机器身份(通过 OIDC 令牌)提供给外部服务,外部服务对其进行身份验证,并出于某种目的(分别是上传到 PyPI 或签署工件)进行使用。 不幸的是,GitHub 在工作流中暴露 OIDC 令牌的*机制*存在一个重大弱点,而且(在我看来)这个弱点会随着时间推移带来日益严重的安全风险。这篇文章就是关于这个弱点的。 ## CI/CD 和 OIDC [#](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#cicd-and-oidc) 所有“CI/CD 中的 OIDC”实现的核心都是某种机制,允许工作流(流水线定义等)请求或预先加载 OIDC 身份。 GitLab CI/CD 中的做法如下: ```yaml my-job: id_tokens: PYPI_ID_TOKEN: aud: pypi script: | do-something.sh --id-token "${PYPI_ID_TOKEN}" ``` GitHub Actions 中对应的是: ```yaml my-job: runs-on: ubuntu-latest permissions: id-token: write steps: run: | resp=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=pypi") oidc_token=$(jq '.value' <<< "${resp}") do-something.sh --token "${oidc_token}" ``` 两者之间的差异很小但很重要:GitLab 要求*OIDC 受众*(`aud`)**事先静态声明**,而 GitHub 则要求工作流**动态请求**一个受众在*运行时*才选择的令牌(即 HTTP 请求中的 `audience` 参数)。 ## 为什么这很重要?[#](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#why-does-this-matter) 首先,快速了解一下 OIDC。 在底层,OIDC(大部分)只是 OAuth 2.0 (https://oauth.net/2/),而 OIDC 身份令牌只是 JSON Web Token (https://datatracker.ietf.org/doc/html/rfc7519)(JWT),外加一些对它们应包含的声明(claims)的更严格[2](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#fn:stringent)约束。 OIDC ID 令牌中最重要的声明可以说是 `sub`,因为它标识了主体(“subject”)[3](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#fn:sub)。 然而,*其次*最重要的声明是 `aud`,即*受众*。受众*至关重要*,因为它**约束了谁会接受该令牌**:接受 ID 令牌的服务只有在确认受众与其自身匹配时才应该接受。 换句话说:`aud` 声明防止了一个*特意*为某个服务签发的 ID 令牌被攻击者窃取并*误用*到另一个服务上。 这旨在作为一种纵深防御:即使攻击者设法窃取了 OIDC 凭据,他们也不应该能够利用它*横向移动*到其他服务。 这是一种脆弱的防御,但通常*颇为有效*[4](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#fn:effective),**除非**你让攻击者也能控制 `aud` 声明。 不幸的是,这正是 GitHub Actions 所允许的[5](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#fn:others):`id-token: write` 赋予作业(或整个工作流)生成它想要的*任何* ID 令牌的能力,受众也*任意*。 在一个(我们的世界中)被授予 `id-token: write` 的作业*同时*也会运行大量第三方代码的世界里,这一点关系重大:这些代码中的任何漏洞(或恶意软件)都可能请求它*本不*应该有权访问的受众的*新* ID 令牌。 我们在设计 Trusted Publishing 时思考过这个问题,并得出结论:Trusted Publishing 使用的机器身份*必须*包含工作流名称,以防止攻击者通过从 `aws-deploy.yml` 中诱导一个 `aud: pypi` 的 ID 令牌来冒充 `pypi-publish.yml`。 然而,这种约束并不适用于所有可能的用例:许多集成只想将 `org/repo` 标识符作为足够的身份,这意味着在签发 ID 令牌时,所有工作流实际上是同级的。 ## GitHub 应该怎么做?[#](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#what-should-github-do) 增加约束!理想情况下是这样的: ```yaml my-job: runs-on: ubuntu-latest permissions: id-token: [pypi] ``` 这里的基本思想是*约束*作业可以为其请求 ID 令牌的受众。在上面的例子中,尝试请求 `aud: sts.amazonaws.com` 的令牌会导致错误,从而防止一个*本意*仅为发布到 PyPI 的作业被用作通往 AWS 的跳板。 当然,这可能会有一些缺点。例如,某些服务可能(不明智地)在其受众中要求动态信息,这意味着无法静态预声明一个值。我认为在实践中这种情况足够罕见,不值得因此阻止一项安全改进;而且当这种情况*确实*发生时,GitHub 可以继续允许 `id-token: write` 形式作为一种(安全性较低!的)兜底方案。 ---

相似文章

GitHub 已不适配新时代的形态

Hacker News Top

这篇博客文章认为,GitHub 的协作模式(分支、拉取请求、代码审查)已难以适应现代 AI 驱动的软件开发时代——在这个时代,LLM 和智能体以极高速度生成代码,因此需要重新思考工具和工作流程。

GitHub 对开发者年龄验证的看法

Hacker News Top

GitHub 的博文解释了旨在保护未成年人网络安全的年龄验证法律,可能会无意中给开源开发者和基础设施带来负担,并敦促政策制定者合理界定此类法律的范围。