如何通过默认设置来最小化权限范围,从而增强GitHub Actions的安全性
当某个工作流拥有的权限超出了实际需求时,一个看似简单的构建任务就可能引发仓库数据被修改、令牌被滥用,甚至带来比团队预期更严重的后果。 这个问题很容易被忽视,因为该工作流仍然能够正常运行,所以这些多余的权限往往会在直到出现审查问题、事故或发布失败时才会被发现。 这一点非常重要,因为GitHub Actions在代码管理、机密信息处理、版本发布以及自动化部署等环节中扮演着核心角色。如果能在工作流和具体任务的层面严格限制权限范围,那么一旦某个步骤、操作或依赖项遭到攻击,攻击者能造成的破坏也会大大减少。 在本教程中,你将学习如何确定一个工作流实际所需的权限范围,将这些权限降到最低限度,同时确保在权限受
当某个工作流拥有的权限超出了实际需求时,一个看似简单的构建任务就可能引发仓库数据被修改、令牌被滥用,甚至带来比团队预期更严重的后果。
这个问题很容易被忽视,因为该工作流仍然能够正常运行,所以这些多余的权限往往会在直到出现审查问题、事故或发布失败时才会被发现。
这一点非常重要,因为GitHub Actions在代码管理、机密信息处理、版本发布以及自动化部署等环节中扮演着核心角色。如果能在工作流和具体任务的层面严格限制权限范围,那么一旦某个步骤、操作或依赖项遭到攻击,攻击者能造成的破坏也会大大减少。
在本教程中,你将学习如何确定一个工作流实际所需的权限范围,将这些权限降到最低限度,同时确保在权限受到限制的情况下该工作流仍能正常运行。
你可以从现有的某个工作流开始入手,明确它的权限设置,然后移除那些工作流根本不会用到的权限。
我们将涵盖以下内容:
先决条件
你应该已经拥有一个GitHub仓库,其中至少包含一个工作流文件,并且具备编辑仓库设置及工作流YAML文件的权限。
此外,你还需要对GitHub Actions的任务、权限设置以及拉取请求相关工作有一定的了解。
关键要点
首先使用最小权限集来配置工作流,确保任务仍能正常运行。
只给真正需要写入权限的任务赋予这些权限。
在移除了多余的权限之后,要确认工作流仍然能够成功执行。
当工作流涉及到AWS或其他云服务提供商时,应使用OIDC进行身份验证,而非长期有效的凭证。
最终成果
你将把现有的一个GitHub Actions工作流改造成一个权限更加严格的版本,确保每个任务只拥有它真正需要的权限。
通常来说,这意味着会创建一个仅具有读取权限的测试任务、一个具有写入权限的发布任务,以及一些使用短期有效的OIDC凭证进行云部署的任务,而不会使用存储在本地的长期机密信息。
为什么默认方法会失败
一种常见的做法是让工作流继承广泛的仓库访问权限,直到整个流程开始运行后才考虑安全问题。这种做法看似方便,但实际上会让所有任务看起来都比实际更值得信任,从而增加安全隐患。
更安全的做法是将每项任务视为一个独立的边界。构建任务通常只需要读取权限,而发布任务可能只需要有限的写入权限。我们的目标并不是为了让每个工作流程都变得过于严格,而是要确保每一项权限都是明确且易于审核的。
例如,测试工作流程通常只需要读取代码并上传测试结果文件,而不应该自动具备推送标签、发布版本或向包管理仓库写入数据的功能。
如何安全地实施这些措施
步骤1:明确工作流程的实际功能
在编辑YAML配置文件之前,先列出该工作流程实际执行的操作。例如,测试任务可能只需要克隆代码并上传测试结果文件,而发布任务则可能需要创建新的版本或推送包。
这种区分非常重要,因为GitHub Actions的权限设置可以在工作流程层面和具体任务层面有所不同。
如果不知道从哪里开始入手,可以仔细分析该工作流程,并问自己这样一个问题:这项任务需要读取数据、写入数据,还是两者都需要?
以下是一种适用于大多数工作流程的简单权限审核方法:
| 工作流程操作 | 通常需要的权限 |
|---|---|
| 克隆仓库并运行测试 | contents: read |
| 上传构建或测试结果文件 | 通常不需要对仓库令牌拥有额外的写入权限 |
| 创建新版本 | contents: write |
| 发布包 | packages: write |
| 通过OIDC获取云服务凭证 | id-token: write |
可以以这个表格为参考,如果你的工作流程更为简单,就可以进一步减少所需的权限范围。
步骤2:设置最低限度的默认权限范围
首先为整个工作流程配置基本的权限,只授予大多数任务所必需的权限。
如果某项任务仅仅用于检查代码或运行测试,那么contents: read通常就足够了。
name: ci
on:
pull_request:
push:
branches:
- main
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
通过这样的配置,该工作流程只能读取仓库内容,而不会默认获得额外的写入权限。
步骤3:仅在需要时才授予写入权限
如果某项任务需要创建新版本或发布包,那么就为这项任务单独配置所需的权限,而不要影响整个工作流程的权限设置。
只给真正需要写入权限的任务分配这些权限,这样就能让其他环节的权限审核变得更加简单。
release:
runs-on: ubuntu-latest
permissions:
contents: write
needs: test
steps:
- uses: actions/checkout@v4
- run: ./scripts/release.sh
这种方法能够确保你的构建路径保持简洁,同时仍能让那些确实需要写入权限的任务顺利完成它们的工作。
步骤4:当工作流程离开GitHub时,使用OIDC进行云访问
如果工作流程需要访问云端资源,应配置OIDC而不是将长期有效的凭证存储在秘密文件中。这样一来,任务在运行时会请求临时令牌,从而避免在代码仓库中保存静态的云访问密钥。
permissions:
contents: read
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 配置云访问凭证
run: echo "在这里配置基于OIDC的凭证"
步骤5:确认不必要的访问权限已被移除
在删除了多余的权限之后,运行工作流程,并确认以下两点:首先,原本应该执行的任务仍然能够成功完成;其次,那些试图使用被移除的权限的任务应当会明显失败。
一个有效的验证方式是查看工作流程的日志,确认这些日志确实反映了你设置的权限范围,同时确保任何被阻止的写入操作都会失败,而不会悄悄地成功执行。
如果只有在你再次扩大权限范围时,工作流程才能正常运行,那么你就应该考虑将相关任务拆分,或者将需要写入操作的步骤移放到另一个较小的发布工作流程中。
你也可以通过故意制造一些故障情况来验证这些变更是否有效。例如,在一个只有contents: read权限的分支上执行发布操作,这样的操作应该会失败,而不会默默地完成发布任务。
如何验证这些措施的有效性
可以使用一个小型测试分支,并提交一个能够触发工作流程的变更请求。
然后确认以下内容:
仅具有读取权限的任务仍然可以正常完成。
只有当拥有明确的写入权限时,发布任务才能成功执行。
任何被阻止的写操作都会立即失败,而不会获得具有更广泛权限的临时令牌。
如果你使用了安全扫描工具或代码检查工具,请确保这些工具仍然包含在工作流程中,这样权限变更才不会影响正常的发布流程。
不仅要查看工作流程最终的运行结果,还要仔细检查日志信息。日志应该清楚地显示任务仅请求了它实际需要的权限,而那些被拒绝的写操作也确实因为没有相应的权限而失败。
这样的做法能为未来对工作流程进行的任何修改提供清晰的审计痕迹和便捷的验证方式。
最有力的证明是:当使用最小限度的必要权限时,工作流程仍然可以正常运行;只有当你故意移除某些必要的权限时,工作流程才会失败。
如果你想进一步确认这些变更的有效性,可以比较修改前后YAML配置文件的内容。新的配置文件应该显示出在工作流程层面仅设置了最基本的权限,而在任务层面也只有少数例外情况。
当这种方法出现问题时
这种做法并非万能的。有些代码仓库中的工作流程需要执行多种不同的操作,对于这类情况,可能需要在调整权限设置之前先对这些工作流程进行拆分处理。
起初,这种方式可能会让人感觉运行速度变慢了,因为所有的权限设置都变得明文可见了。这就是这种做法所需要付出的代价:目前需要进行更多的配置工作,但之后安全边界会变得更加清晰明了。
最后需要强调的是,“最小权限原则”并不能替代代码审查或安全扫描机制;它仅仅能限制那些被入侵的工作流程所能造成的危害而已。
当第三方操作所需的权限超出了预期范围时,这种机制也会遇到局限性。在这种情况下,下一步应该仔细审查这些操作本身,而不仅仅是调用这些操作的 workflow。
结论
通过本教程,你学会了如何将 GitHub Actions 的权限设置限制在最低必要范围内,如何在不同的任务之间分配读写权限,并且如何确认在调整了权限设置之后,相关工作流程依然能够正常运行。
作为下一步行动,你可以将这种做法应用到那些目前仍然需要超出合理范围权限的发布流程、包分发机制或云部署工作中去。
参考资料
相关文章
为什么软件自动化需要在“意图”与“执行”之间添加一层中间环节
软件自动化擅长将指令转化为具体的行动。 一个部署流程可以完成发布操作,Terraform可以配置基础设施,运行手册可以重启服务,而代理程序则可以分析遥测数据并选择相应的补救措施。 但这些系统都有一个共同的弱点:它们通常更擅长执行指令,而非理解这些指令背后的真正意图。 当自动化过程较为简单时,这种差距很容易被忽视。如果一个脚本只执行一条命令,那么这个脚本本身或许就足以说明应该发生什么。 然而,随着系统变得越来越分布式、越来越受策略驱动,也越来越自主,执行逻辑开始承担一些原本并不属于它的职责。 如今,一个部署工作流程可能会包含以下内容: 需要部署什么 它可以在哪里运行 必须遵守哪些约束条件 需要检
阅读全文
Netflix重新设计了这一系统,使得每月能够执行4.2亿次工作流程操作,同时让相关工作流程的规模扩大了10倍。
Netflix对其Conductor工作流编排引擎进行了升级优化,以便能够处理更大的工作负载。此次升级使该系统所能支持的工作流规模从约2,500个任务增加到了30,000个任务,同时也将工作流评估的延迟时间减少了大约40%。Conductor 4.0将工作流元数据与任务数据分离出来,将评估过程改为异步处理,并引入了动态工作者分配机制及并发控制功能。 作者:Leela Kumili
阅读全文
如何使用 GitHub Actions、Ingress 以及自动 TLS 功能,在 GKE 上搭建临时性的 PR 预览环境
每一个发布软件的团队最终都会搭建一个测试环境。而每一个搭建了测试环境的团队,最终都会遇到同样的问题。 测试环境是共享的。一个环境,却有大量的拉取请求。有人会部署一个尚未完成的功能来进行测试,其他人会在其基础上继续进行开发,还有第三个人会执行数据库迁移操作。到了周四下午,就没人能弄清楚到底哪个分支的哪个版本正在运行中,“在测试环境中可以正常使用”这句话已经完全失去了任何参考意义。 常见的解决办法包括:创建一个Slack频道,让大家把测试环境当作会议室来使用;制定轮换部署计划;或者再搭建第二个、甚至第三个测试环境,但每个测试环境的配置都会与生产环境越来越偏离。 其实有更好的解决方案。像Vercel
阅读全文
如何阻止人工智能代理生成尼日利亚的邮政编码
2026年10月1日,尼日利亚的邮政编码从六位数字改为由11个字符组成的代码,这种编码能够唯一标识某栋建筑,例如 EK-01-A03-FK-01 。 在此之前训练的语言模型仍然会自信地使用旧格式来处理邮政编码。这会影响两种类型的程序:一种是负责生成验证代码的编程程序,另一种是在运行时从客户信息中读取邮政编码或填写配送表格的程序。 本文将介绍如何解决这个问题。对于那些生成验证代码的程序,你需要检测它们目前生成的代码是否存在错误,并为它们提供相应的规则来纠正这些错误;而对于在运行时处理邮政编码的程序,则需要为它们设定明确的边界:当模型提出一个邮政编码时,由特定的程序来判断该编码是否符合规范、是否已
阅读全文