← 返回蜂巢洞察

如何通过默认设置来最小化权限范围,从而增强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 的权限设置限制在最低必要范围内,如何在不同的任务之间分配读写权限,并且如何确认在调整了权限设置之后,相关工作流程依然能够正常运行。

作为下一步行动,你可以将这种做法应用到那些目前仍然需要超出合理范围权限的发布流程、包分发机制或云部署工作中去。

参考资料

相关文章

技术实践

如果你已经不再需要使用原有的定时任务调度工具了,应该该怎么办呢?

大多数开发者在开始自动化工作的过程中都会采取类似的方法。他们会编写一些脚本来完成某些有用的任务,比如从API中获取数据、调整一批图片的大小,或者发送报告邮件,然后将这些脚本安排在每天早上自动运行。 这样做之后,他们会在自己的crontab文件中添加相应的指令,这样就能感受到自己对自动化流程的控制力了。现在,在他们睡觉的时候,电脑会自动执行这些脚本。 有一段时间里,这样的安排确实足够用了。但后来就不再适用了。 也许某个备份脚本在凌晨3点悄悄地失败了,而你直到当天晚些时候需要使用该脚本时才发现了这个问题;又或者你编写了一个在终端环境中运行得非常顺利的脚本,但却发现在用crontab安排它执行时却会

阅读全文
技术实践

文章:与运行时环境无关的AI工作流程:确保系统稳定性并加快评估迭代速度的有效模式

AI工作流程存在两个相互矛盾的需求。要在生产环境中可靠地运行,就必须确保每个步骤都能被持久化存储并分布式执行,这样系统才能在发生故障后继续运行、完成部署并重新启动。然而,正是这些机制使得系统的运行速度变得过慢,而快速迭代测试对于评估大语言模型的输出质量来说却是必不可少的。那些能够提升系统稳定性的特性,恰恰会降低迭代的效率。 作者:Mateus Moury

阅读全文
技术实践

现代质量保证工程师的实际工作内容:远不止是发现漏洞而已

如果你问别人QA工程师是做什么的,你很可能会听到这样一个熟悉的答案: “他们负责测试软件并找出其中的缺陷。” 这种看法非常普遍,而且公平地说,找出缺陷确实是QA工作的重要组成部分。但如果你在现代软件团队中工作几周,你就会很快意识到,这其实只是QA工程师实际工作内容的很小一部分。 在过去十年里,软件开发发生了巨大的变化。各个开发团队不再需要等待数月才能发布新功能;许多机构甚至每周、每天,甚至是每天多次进行系统更新。应用程序已经变得越来越复杂,云服务、API、微服务、移动应用以及第三方集成组件都在幕后共同发挥作用。 随着软件的发展,QA的角色也在不断演变。 如今的QA工程师会在某个功能开始进入测试

阅读全文