在当今的软件开发流程中,GitHub Actions 作为一款强大的自动化工具,被广大开发者广泛应用。它能帮助我们自动完成诸如代码构建、测试、部署等一系列繁琐的任务,极大地提高了开发效率。然而,看似便捷的背后,却隐藏着不容忽视的安全隐患。你有没有想过,在使用 GitHub Actions 时,你运行的究竟是谁的代码?
恶意代码入侵事件敲响警钟
就在不久前,有人在 tj-actions/changed-files 这款 GitHub Action 中植入了恶意代码。这可不是一件小事!一旦你使用了被篡改的这个 Action,你的机密信息就可能会泄露到构建日志中。而对于公共仓库来说,这些构建日志是公开可见的,这意味着任何人都有可能看到你的机密信息,简直细思极恐!
为什么会出现这样的情况呢?这背后涉及到一个关于引用方式的问题。在 GitHub Actions 工作流中,大家常常会使用标签来引用 Action,就像这样:
jobs:
changed_files:
...
steps:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v2
...
乍一看,这似乎是指向已经发布的 “v2 版本” Action 的固定引用,但实际上,这里的v2
标签是可变的。如果有人在tj-actions/changed-files
仓库中修改了v2
标签,让它指向了不同的提交,那么下次运行这个 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。让我们来详细拆解一下它的工作原理:
find . -path '*/.github/workflows/*' -type f -name '*.yml' -print0
:这个命令用于查找所有的 GitHub Actions 工作流文件。它会在当前目录及其子目录下,寻找路径中包含.github/workflows/
且文件名以.yml
结尾的文件。找到后,它会以空字节(\0
)作为分隔符输出这些文件名。这是因为相比于默认的换行符,空字节更安全,能避免文件名中包含换行符时出现的问题。如果你的工作流文件扩展名有时是.yaml
,可以把-name '*.yml'
替换为$ -name '*.yml' -o -name '*.yaml' $
。另外,我还添加了一些-path
规则,比如-not -path './cpython/*'
,用来排除那些我克隆的开源项目仓库,因为我不太关心它们使用的 GitHub Actions。xargs -0 grep --no-filename "uses:"
:xargs
命令会根据前面find
命令输出的文件名,逐个进行处理。-0
标志告诉它以空字节作为分隔符。然后,grep
命令会在每个文件中查找包含"uses:"
的行,这是在工作流文件中使用 Action 的标识。--no-filename
选项表示只输出匹配的行,而不输出文件名。由于我的文件格式和缩进不太一致,所以输出的内容有点杂乱。sed 's/\- uses:/uses:/g'
:在工作流文件中,uses:
前面有时会有一个连字符,有时没有,这取决于它在 YAML 字典中的位置。这个sed
命令会把"- uses:"
替换为"uses:"
,对数据进行初步整理。我知道sed
是一个非常强大的文本处理工具,但我常用的也就像这种替换文本的简单命令:sed 's/old/new/g'
。tr '"' ' '
:Action 的名称有时会被引号括起来,有时不会。这个命令的作用就是去除输出中的双引号。写这篇文章的时候我突然想到,其实也可以用sed
来完成这个替换操作。不过我选择tr
是因为我用它的时间更久,而且对于单个字符的替换,它的语法更简单:tr '<oldchar>' '<newchar>'
。awk '{print $2}'
:awk
命令会把字符串按空格进行分割,然后输出第二个部分,也就是 Action 的名称。虽然awk
有很多强大的模式匹配功能,但我目前也就掌握了打印字符串中第 n 个单词的用法。sed 's/\r//g'
:我的一些工作流文件中存在回车符(\r
),它们会出现在awk
的输出中。这个命令就是用来去除这些回车符,让数据在最后一步处理时更加统一。sort | uniq --count | sort --numeric-sort
:首先,sort
命令会对前面的输出进行排序,让相同的行相邻;然后,uniq --count
会对相邻的相同行进行分组并统计数量;最后,再用sort --numeric-sort
按数量进行排序,把使用次数最多的 Action 排在最下面。我把这个脚本设置成了一个名为tally
的 Shell 别名。
运行这个脚本后,我得到了类似这样的输出:
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@v1
31 actions/setup-python@v5
58 actions/checkout@v4
看着这份清单,我开始思考对每个 Action 及其作者的信任程度。像actions
或ruby
这样的大组织提供的 Action,虽然不能说绝对完美,但它们通常会有比较完善的安全措施,能有效防范恶意修改。而对于那些来自个人开发者或小组织的 Action,我就会更加谨慎,尤其是在我不认识作者的情况下。这并不是说个人开发者的安全意识一定不好,只是在互联网上,个人开发者的安全设置差异比大组织要大得多。另外,我还会考虑是否真的需要使用别人的 Action,还是可以自己编写脚本来替代。如果我只是用到某个 Action 的一小部分功能,我通常更倾向于自己写脚本。虽然前期会多花点功夫,但这样我能清楚地知道代码在做什么,也能减少上游变更带来的影响和风险。
总体来说,我对自己项目中使用的这些 Action 还算比较放心。大部分 Action 都来自大组织,剩下的一些是针对我自己的 Rust 命令行工具的,这些工具属于非关键的小项目,就算 GitHub 仓库被入侵,影响也相对较小。
安全使用 GitHub Actions 的建议
通过这次事件和自查,我们可以总结出一些安全使用 GitHub Actions 的建议。首先,尽量使用不可变的引用,也就是指定具体的 Git 提交 ID 来引用 Action,这样能最大程度地避免因标签被篡改而导致的安全问题。虽然使用标签更方便,但在安全问题上,还是谨慎为好。
其次,要对使用的 Action 进行严格审查。在引入一个新的 Action 之前,仔细查看它的仓库、作者信息,了解其更新频率和社区反馈。如果是来自不太知名的作者或小组织,更要格外小心。可以查看代码的历史记录、提交信息,看看是否有异常情况。
另外,定期检查项目中使用的 Actions 也是个好习惯。就像我用脚本统计自己使用的 Actions 一样,你也可以定期运行类似的脚本,看看是否有新的风险出现。如果发现某个 Action 存在安全隐患,及时进行替换或更新。
最后,对于一些简单的功能,如果自己有能力编写脚本实现,尽量不要依赖外部的 Action。这样不仅能提高安全性,还能让你更加了解项目的具体实现,在遇到问题时也能更快速地进行调试和修复。
GitHub Actions 为我们的开发工作带来了极大的便利,但我们不能忽视其中的安全问题。通过了解这些潜在风险,并采取相应的防范措施,我们就能在享受其便利的同时,保障项目的安全。希望大家都能重视起来,让自己的代码在 GitHub Actions 的运行中更加安全可靠。你在使用 GitHub Actions 时遇到过哪些安全问题呢?欢迎在评论区分享你的经验和看法。
关于Allthinker 敖行客:
公司专注于通过先进的理念与技术,为开发者打造开放、自由、高效且安全的研发空间,期待与你一起创造一个更美好的研发新世界。
关于AT Work:
AT Work是敖行客打造的下一代研发智能体,基于自主研发的"思链"认知引擎构建,实现云原生研发场景的全面智能化革新。作为业内首个搭载多模态AI中台的云端研发平台,通过深度学习模型重构需求分析、代码生成、质量管控、知识管理四大核心模块,深度融合云IDE、敏捷看板、共享云盘、云文档、云端知识库等数字工具链,形成"需求-设计-开发-测试-交付"的智能闭环。
科技脉搏,每日跳动。
与敖行客 Allthinker一起,创造属于开发者的多彩世界。
- 智慧链接 思想协作 -