社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Git

“我在 GitHub Actions 里运行的代码,到底是谁的?”

CSDN • 2 周前 • 28 次点击  

【CSDN 编者按】在自动化 CI/CD 任务时,GitHub Actions 为开发者提供了极大的便利。它允许我们轻松集成、测试和部署代码,只需简单配置 .github/workflows/ 即可调用社区或官方的 Actions 组件。但你有没有想过——你在 GitHub Actions 里运行的代码,到底是谁的?它们真的安全吗?

原文链接:https://alexwlchan.net/2025/github-actions-audit/

作者 | alexwlchan      翻译|郑丽媛
出品 | CSDN(ID:CSDNnews)
一周前,有人在 tj-actions/changed-files 这个 GitHub Action 中植入了恶意代码。如果你在工作流(workflow)中使用了这个受影响的 Action,它就会把你的敏感信息泄露到构建日志中。而对于公共仓库来说,这些构建日志是公开的,任何人都能到你的机密数据——这个漏洞相当可怕!
(CSDN 付费下载自视觉中国)

可变引用 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 时代,它们依然是处理数据的一种强大而灵活的方式,可以快速拼凑出一次性的数据处理脚本。

推荐阅读:

10天做出AI面试“外挂”,狂揽多个大厂Offer!这名21岁学生已被哥大正式开除……

00后程序员当道!下班3小时“爆肝” OpenManus背后的故事

Claude深度“开盒”,看大模型的“大脑”到底如何运作?
图片

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/180629
 
28 次点击