【CSDN 编者按】在自动化 CI/CD 任务时,GitHub Actions 为开发者提供了极大的便利。它允许我们轻松集成、测试和部署代码,只需简单配置 .github/workflows/ 即可调用社区或官方的 Actions 组件。但你有没有想过——你在 GitHub Actions 里运行的代码,到底是谁的?它们真的安全吗?摘要:在自动化 CI/CD 任务时,GitHub Actions 为开发者提供了极大的便利。它允许我们轻松集成、测试和部署代码,只需简单配置
原文链接:
https://alexwlchan.net/2025/github-actions-audit/作者 | alexwlchan 翻译|郑丽媛
出品 | CSDN(ID:CSDNnews)
一周前,有人在 tj-actions/changed-files 这个 GitHub Action 中植入了恶意代码。如果你在工作流(workflow)中使用了这个受影响的 Action,它就会把你的敏感信息泄露到构建日志中。而对于公共仓库来说,这些构建日志是公开的,任何人都能看到你的机密数据——这个漏洞相当可怕!
可变引用 vs. 不可变引用
之所以会发生这样的攻击,是因为在 GitHub Actions 的工作流中,通常会通过标签(tag)来引用某个 Action,例如:
jobs: changed_files: ... steps: - name: Get changed files id: changed-files uses: tj-actions/changed-files@v2 ...乍一看,这似乎是一个对 "v2" 版本的不可变引用,但实际上,v2 是一个可变的 Git 标签。如果有人在 tj-actions/changed-files 仓库中更改了 v2 标签,使其指向不同的 commit,那么下次运行时,这个 Action 执行的代码就会发生变化。
相比之下,如果你直接指定 Git 提交 ID(如 a5b3abf),那么这就是一个不可变引用,每次都会运行相同的代码。
总体而言,使用标签和提交 ID 之间存在一个权衡:
指定确切的提交 ID 能确保代码不会意外更改,可提高安全性。
使用标签则更方便阅读,也更容易与不同版本进行比较。
我的工作流中,是否存在可变引用?
我对这次的攻击并不担心,因为我没有使用 tj-actions,但我很好奇自己究竟使用了哪些 GitHub Actions。因此,我在本地克隆的所有仓库中运行了一段简短的 shell 脚本来检查:
find . -path '*/.github/workflows/*' -type f -name '*.yml' -print0 \ | xargs -0 grep --no-filename "uses:" \ | sed 's/\- uses:/uses:/g' \ | tr '"' ' ' \ | awk '{print $2}' \ | sed 's/\r//g' \ | sort \ | uniq --count \ | sort --numeric-sort这个脚本会统计我使用的所有 GitHub Actions,并输出了如下结果:
1 hashicorp/setup-terraform@v3 2 dtolnay/Rust-toolchain@v1 2 taiki-e/create-gh-release-action@v1 2 taiki-e/upload-rust-binary-action@v1 4 actions/setup-python@v4 6 actions/cache@v4 9 ruby/setup-ruby@v131 actions/setup-python@v558 actions/checkout@v4于是,我仔细检查了这份列表,并思考是否应该信任每个 Action 及其作者。
这些 Action 来自像 actions 或 ruby 这样的大型组织吗?虽然它们并非完美无缺,但至少通常会有完善的安全措施来防范恶意更改。
这些 Action 来自个人开发者或小型组织吗?如果是的话,我会更加谨慎,尤其是当我并不了解作者的时候。我并不是说个人开发者的安全性就一定不高,只是相比大型组织,互联网上开发者们的安全性实在有些参差不齐。
我是否真的需要使用别人的 Action? 或者,我能否自己编写一个脚本来代替?通常情况下,我更倾向于自己写脚本,特别是当我只需要 Action 提供的部分功能时。虽然这样做前期会多花一些功夫,但我可以完全掌控代码,不必担心上游发生变更而带来的潜在风险。
总的来说,我对自己使用的 Actions 还算放心。大部分来自大型组织,其余的则是针对我的 Rust 命令行工具的一些特定 Actions,且这些工具都不是很关键,所以即使其 GitHub 仓库被攻破,影响也不会太大。
这个脚本是如何工作的?
这是一个典型的 Unix 管道(pipeline)应用,它串联了多个内置的文本处理工具来完成任务。下面,我们来逐步拆解它的工作方式。
find . -path '*/.github/workflows/*'-type f -name '*.yml' -print0这条 find 命令会查找所有 GitHub Actions 的工作流文件,即 .github/workflows/ 等文件夹中名称以 .yml 结尾的文件,并打印出一个文件名列表,如:
./alexwlchan.net/.github/workflows/build_site.yml
./books.alexwlchan.net/.github/workflows/build_site.yml
./concurrently/.github/workflows/main.yml
在打印时,文件名之间会有一个空字节 (\0),以便在下一步中可以分割文件名。默认情况下,它会使用换行符,但用空字节会更安全一些,以避免文件名中包含换行符时导致解析错误。
如果你有时也把 .YAML 作为扩展名,可以用 \( -name '*.yml' -o -name '*.yaml' \) 替换 -name '*.yml'。
我有很多本地 repos,它们都是开源项目的克隆,并不是我的代码,所以我不太关心它们使用的是什么 GitHub Actions。我添加了额外的 -path 规则,如 -not -path './cpython/*' 将它们排除在外。
xargs -0 grep --no-filename "uses:"接下来,我们用 xargs 逐个处理文件名。-0 选项告诉 xargs 以空字节(\0)分割文件名,然后运行 grep 查找包含 "uses:" 的行——这是在工作流文件中调用 Action 的方式。
-no-filename 选项意味着 grep 只输出匹配的行,而不是文件名。由于我的文件格式和缩进并不一致,因此输出相当混乱:
- uses: actions/checkout@v4
uses: "actions/cache@v4"
uses: ruby/setup-ruby@v1
sed 's/\- uses:/uses:/g'\有时 uses: 前面有一个 -,有时没有——这取决于 uses: 在 YAML 结构中的位置。这个 sed 命令会把"- uses:"替换为"uses:",以便整理数据。
uses: actions/checkout@v4
uses: "actions/cache@v4"
uses: ruby/setup-ruby@v1
我知道 sed 是一个功能强大的文本处理工具,但我只会用一些基本的命令,比如替换文本的模式:sed 's/old/new/g'。
tr '"' ' '有些 Action 名称会带双引号,有些没有,而这个命令则会移除输出中的双引号。
uses: actions/checkout@v4
uses: actions/cache@v4
uses: ruby/setup-ruby@v1
现在回过头来看,我发现也可以用 sed 来完成这个替换。我之所以使用 tr,是因为我对它更熟悉,而且它的语法更简单,适用于单字符替换:tr '' ''。
awk '{print $2}'这条命令会按空格拆分字符串,并打印第二个字段,也就是 Action 的名字:
actions/checkout@v4
actions/cache@v4
ruby/setup-ruby@v1
awk 是另一个强大的文本处理工具,但我我对它的了解不是很深——只知道如何打印字符串中的第 N 个单词。其实它还支持很多模式匹配功能,不过我没有尝试过。
sed 's/\r//g'因为我的一些工作流文件包含回车符(\r),这些字符会被 awk 处理后保留在输出中。这条命令会去掉回车符,使数据在最终输出时更加一致。
sort | uniq --count | sort --numeric-sort这部分命令会进行排序和统计,使相同的行相邻,然后统计并计算每行的数量,最后重新排序,将出现频率最高的行列放在最下面。
我将其作为一个 shell 别名,名为 tally。
6 actions/cache@v4
9 ruby/setup-ruby@v1
59 actions/checkout@v4
这种循序渐进的方法就是我构建 Unix 文本管道的方法: 我可以一次写一个步骤,逐步完善和调整输出,直到得到我想要的结果。尽管有许多方法可以实现这一目标,但对于这种一次性的脚本来说,只要最终能得到正确的结果就够了。
如果你使用 GitHub Actions,不妨用这个脚本检查一下你自己的 Action 依赖项,看看你正在使用哪些 Actions。除此之外,我强烈推荐你掌握 Unix 文本处理工具和管道——即使在 AI 时代,它们依然是处理数据的一种强大而灵活的方式,可以快速拼凑出一次性的数据处理脚本。
来源:CSDN一点号