Zenn
🔒

サードパーティアクションはSHAで指定すれば安心?残念ながら、いいえ

2025/03/16に公開
2
TL;DR
  • SHAで指定されたアクション自体がタグで別のアクションを呼び出していたら、その呼び出し先は呼び出した時点のタグの向き先になるため
  • タグ指定をしていないcompositeアクションはSHA指定で安全
  • (ランタイムでevalしていない前提の上で)nodeやdockerタイプのアクションはSHA指定で安全

2025/3/15に、GitHub Actionの有名なサードパーティアクションが侵害される事件がありました

tj-actions/changed-filesが侵害されて、機密情報をログに出力する悪意のあるコードが混入してしまったというものです。

実行手順としてはtj-actions/changed-filesのすべてのタグ指定が侵害されたコミット(0e58ed8671d6b60d0890c21b07f8835ace038e67)を向くように書き換えられたため、uses: tj-actions/changed-files@v45のような指定をしているworkflowは、侵害されたコードを実行してしまうことになっていました。

現在は悪意のあるコードが削除されたため安全に利用できます。

タイムライン


さて、この事件を受けて「GitHub Actionsのサードパーティアクションを使う際にはSHAで指定するべきだよね」という意見をいろいろなところで見かけます。SHAで指定すれば安心なのでしょうか?

actionのタグはgitのタグ。gitのタグは書き換えられる

GitHub Actionsでサードパーティアクションを呼び出すときに何が行われているのか、おさらいしておきましょう。

私たちが普段なにげなく使っているGitHub Actionsのworkflowでuses: actions/checkout@v4のように書くと、これはactions/checkoutリポジトリのタグv4を指定していることになります。

(正確にはブランチなどのrefsも同じように指定できますが、一般的にはタグを指定することが多いので、ここではタグに絞って説明します)

これは一見セマンティックバージョニングに基づいているように見えますが、実はそうではありません。GitHubのドキュメントではセマンティックバージョニングを使用することを推奨していますが、gitのタグとして許容されるものなら何でも使えます。しかし、セマンティックバージョニングのように使えたほうが便利であることをみな知っているので、サードパーティアクションの作者たちが気を利かせてやってくれているだけなのです。

具体的な例を考えてみましょう。

私たちのworkflowに以下のような記述があるとします。

- name: Checkout
  uses: actions/checkout@v4

これを実行したとき、runnerはactions/checkoutリポジトリのv4タグを指すコードをダウンロードしてきます。今v4は……


actions/checkout tags

v4.2.2である11bd719を指しているようです。

さて、時間が経ち、actions/checkoutをより良くするために、書き込み権を持っている人が変更を加え、新しいバージョン v4.2.3 (423aaaa) をリリースしたとします。さらに、このときv4タグがv4.2.3のコミットハッシュである423aaaaを指すように書き換えてくれました

ふたたび、私たちのworkflowを実行すると、今度はactions/checkoutのv4.2.3がダウンロードされます。今v4が指しているのは423aaaaだからです。
こうして、私たちは既存のworkflowに何も変更を加えることなく、新しいバージョンを使うことができました。みんながハッピーです。めでたしめでたし。

このように、タグを書き換えられるということは適切に使えば大変便利なことです。

(つかの間の)安心を手に入れよう

ええ、あなたのおっしゃりたいことはわかります。タグが書き換えられることによって、私たちのworkflowがいつの間にか悪意のあるコードを実行してしまったらどうしよう、ということですね。

ご安心ください。GitHubは私たちの気持ちをわかっています。タグではなくSHAで指定することができますし、そうするように推奨しています。

We strongly recommend that you include the version of the action you are using by specifying a Git ref, SHA, or Docker tag.

https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses

つまり、さきほどのworkflowは以下のように書き換えることができます:

- name: Checkout
  uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

これで、actions/checkoutのv4.2.2のコードがダウンロードされることが保証されます。v4.2.3がリリースされようと、v5がリリースされようと、このworkflowではv4.2.2のコードがダウンロードされることになります。

これで安心ですね!

なお、手動でこのように書き換えるのがめんどくさい場合はRenovatehelpers:pinGitHubActionDigestsToSemverpinactで自動でできるそうです。

安心ではないことを確かめよう

ここまでがおさらいでした。残念なことに、この方法でも実行されるアクションが一意にできないことがあります。

それは、参照先のアクションが他のアクションをタグで呼び出している場合です。

私たちのworkflowがいくらSHAで指定していても、そのアクションが他のアクションをタグで呼び出している場合、タグで呼び出されたアクションは、呼び出された時点のタグの指定で呼び出されます。

一応試してみましょう。リポジトリを3つ用意しました:

侵害される前の各ファイルは以下のようになっています:

test-good-action\action.yml
name: "Good Action"
description: "This is a good action. (This is a Demonstration for the GitHub Actions Dependency Problem)"
runs:
  using: "composite"
  steps:
    - run: echo "Hello, World!"
      shell: bash
test-superturbo-action\action.yml
name: "SuperTurbo"
description: "This action accelerates the workflow. (This is a Demonstration for the GitHub Actions Dependency Problem)"
runs:
  using: "composite"
  steps:
    - uses: eai04191/test-good-action@v1

    - run: echo "Speed up!"
      shell: bash
test-action-user.github\workflows\run.yml
name: Run Workflow

on: workflow_dispatch

jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - name: Use Superturbo
        uses: eai04191/test-superturbo-action@5fc4223282d36d4fe373fd8bf34df99f1dcecd67

      - run: echo "Done"
        shell: bash

eai04191/test-good-actionはv1タグで900d111badad0910c520ef3f4a5927b8f9217b61を指しています。

実行結果は以下のようになりました。いいですね。

https://github.com/eai04191/test-action-user/actions/runs/13875298166/job/38826836626

Set up Job 抜粋
Prepare all required actions
Getting action download info
Download action repository 'eai04191/test-superturbo-action@5fc4223282d36d4fe373fd8bf34df99f1dcecd67' (SHA:5fc4223282d36d4fe373fd8bf34df99f1dcecd67)
Getting action download info
Download action repository 'eai04191/test-good-action@v1' (SHA:900d111badad0910c520ef3f4a5927b8f9217b61)
Complete job name: run

Set up Jobで5fc4223282d36d4fe373fd8bf34df99f1dcecd67900d111badad0910c520ef3f4a5927b8f9217b61がダウンロードされています。

Use Superturbo 抜粋
▶ Run eai04191/test-superturbo-action@5fc4223282d36d4fe373fd8bf34df99f1dcecd67
▶ Run eai04191/test-good-action@v1
▶ Run echo "Hello, World!"
Hello, World!
▶ Run echo "Speed up!"
Speed up!

さて、侵害が発生しました。eai04191/test-good-actionリポジトリが侵害され、悪意のあるコードを含むコミットがpushされ、v1タグはbadbad0fae80a5c446118e22f35d25aef6d33fe1を指すように書き換えられました。

test-good-action\action.yml
 runs:
   using: "composite"
   steps:
-    - run: echo "Hello, World!"
+    - run: echo "Hello, Evil World!"
       shell: bash

そして私たちのworkflowを再度実行してみましょう。がんばってSHAで指定したので何も変わってないといいのですが!

https://github.com/eai04191/test-action-user/actions/runs/13876060873/job/38828506686

Set up Job 抜粋
Prepare all required actions
Getting action download info
Download action repository 'eai04191/test-superturbo-action@5fc4223282d36d4fe373fd8bf34df99f1dcecd67' (SHA:5fc4223282d36d4fe373fd8bf34df99f1dcecd67)
Getting action download info
Download action repository 'eai04191/test-good-action@v1' (SHA:badbad0fae80a5c446118e22f35d25aef6d33fe1)
Complete job name: run
Use Superturbo 抜粋
▶ Run eai04191/test-superturbo-action@5fc4223282d36d4fe373fd8bf34df99f1dcecd67
▶ Run eai04191/test-good-action@v1
▶ Run echo "Hello, Evil World!"
Hello, Evil World!
▶ Run echo "Speed up!"
Speed up!

あーあ、eai04191/test-good-action@v1badbad0fae80a5c446118e22f35d25aef6d33fe1を指すように書き換えられてしまったので、悪意のあるコードが実行されてしまいました。

どうすればよかったのか

残念ながら、この問題に対する手っ取り早い特効薬はありません。

そもそもサードパーティアクションを使わない

  • shellやNode.jsやPythonのスクリプトファイルを呼び出すだけでは足りないか?
  • 共通化するだけならorganizationに置いて
    • uses: my-org/my-org-actions/myaction@v1
    • で十分でないか?

どうしても使う場合は

  • 心中する覚悟がある公開元のみ使う
    • actions/ など
  • 最低限、今のバージョンで何をしているのかは読んでおく
    • (compositeの場合) 他のアクションを呼び出していないか
    • (node, dockerの場合) 外部から取得したものをevalしていないか
  • 呼び出しがなければSHAで指定する
  • 呼び出しをしていて、どうしても使いたい場合はforkしてSHAで呼び出したり、処理を変更する

といったくらいでしょうか。いずれも利便性とのトレードオフになります。どうしてもサードパーティアクションを使いたい場合は、リスクを十分に理解する必要があります。

おまけ: Immutable Actions の紹介

https://github.com/github/roadmap/issues/592

https://github.com/actions/publish-immutable-action

GitHubが2022年から開発中の機能にImmutable Actionsがあります。アクションをコンテナとして提供することで、@v1.0.0のような指定を、変更されうるタグやブランチによる参照ではなく、一意のコンテナとして参照することができる機能です。
ただし、この記事で取り上げたような依存するアクションを持っている場合に動的に解決されるのかはまだ不明です。

広告

NCDCでは、安全なサプライチェーンをチームで模索しながらプロダクトを開発したいエンジニアを募集しています。

GitHubで編集を提案
2
NCDCエンジニアブログ

Discussion

ログインするとコメントできます