tj-actions/changed-files 2025年 書き換えられたタグとCIの秘密
GitHub Actions のタグは、書き込みの権限があれば別のコミットへ付け替えられる。タグで呼んでいた CI は、付け替えた先のコードを、そのジョブの秘密ごと動かした。1つの部品の CI から漏れたトークンが次の部品のタグを書き換え、2万3千を超えるリポジトリが使う部品に届いた。
要点
- 悪いコミットは、CI の実行機のプロセスのメモリから秘密を読み、2重に Base64 にしてジョブのログへ出した。公開のリポジトリでは、ログを誰でも読めた。
- 付け替えに使われたのは、tj-actions の bot のアカウントの個人用のアクセストークンで、CI の秘密として置かれていた。
- その前に reviewdog/action-setup の v1 のタグが3月11日に約2時間書き換えられていた。tj-actions の CI は、それを中で呼ぶ部品を使っていた。
- タグではなくコミットのハッシュで固定していれば、changed-files の付け替えは届かない。ただし、固定した部品が中で別の部品をタグで呼んでいれば、そこから入られる。
- 根本の守りは、全部の外部のアクションを推移的な依存まで中身で固定し、CI に長く使える広いトークンを置かないこと。
何が起きたか
| 日付 | 出来事 |
|---|---|
| 2024年11月28日、12月6日 | Unit 42 の調べ。SpotBugs の保守者が、ある CI の定義に個人用のアクセストークンを足し、その CI は pull_request_target の契機で動いていた。12月6日に、攻撃者がそのリポジトリへ変更の依頼を送り、トークンを取った[8] |
| 2025年3月11日 10時52分 UTC | Unit 42 の調べ。取ったトークンで SpotBugs の別のリポジトリに捨てのアカウントを招き、悪い CI を走らせて、reviewdog の保守者でもある人のトークンを取った[8] |
| 2025年3月11日 18時42分から20時31分 UTC | reviewdog/action-setup の v1 のタグが書き換えられる。秘密をログへ出すコードが足されていた。中でこれを v1 で呼ぶ reviewdog の他のアクションも、版や固定の仕方にかかわらず影響を受けた[5][6] |
| 2025年3月14日 | changed-files の v1 から v45.0.7 のタグが、悪いコミット 0e58ed8 を指すよう書き換えられる[1]。Unit 42 は16時57分 UTC とし、コミットは更新の自動の bot を装っていたと書く[8] |
| 2025年3月14日 20時52分 UTC | 利用者がリポジトリに、複数のタグが侵害されていると報告する。外への不審な通信を見つけた StepSecurity の記事を引いていた[3][2] |
| 2025年3月15日 | CVE-2025-30066 が NVD と GitHub の勧告の一覧に載る[1][2]。保守者が、悪いコミットを全部のタグと枝から除き、検証されていないコミットを指すタグを押せない保護を入れ、GitHub が組織の公開を戻したと書く[4]。攻撃は tj-actions の bot のアカウントに結び付いたトークンからで、GitHub もどう漏れたか分からない、とも書く[4] |
| 2025年3月16日 | 保守者が、そのトークンは CI の秘密として置かれていて無効にしたこと、今後 tj-actions の組織では個人用のアクセストークンを使わないことを書く[4] |
| 2025年3月18日 | CISA が告知を出し、CVE-2025-30066 を既知の悪用された脆弱性の一覧に載せる[7][1]。reviewdog の保守者が、Wiz Research の知らせで action-setup の件を知ったと公表する[6] |
| 2025年3月19日 | CISA が、changed-files の侵害は reviewdog の侵害で可能になった恐れがあると告知に足す[7]。CVE-2025-30154 が公表される[5] |
| 2025年3月23日 | reviewdog の保守者が、トークンを漏らした貢献者を特定したこと、そのトークンは別のリポジトリへの攻撃から漏れたこと、書き換えは v1 のタグだけだったことを書く[6] |
| 2025年4月2日 | Unit 42 が、最初の漏れが SpotBugs の CI だったとする調べを足す[8] |
SpotBugs の CI からの漏れと、tj-actions の bot のトークンが reviewdog を通じて漏れたという道筋は、セキュリティの会社の Unit 42 の調べによる[8]。tj-actions の保守者は、トークンがどう漏れたかを GitHub も特定できないと書いた[4]。reviewdog の保守者は、トークンが別のリポジトリへの攻撃から漏れたことまでを書き、その調べとして Unit 42 と Wiz を挙げた[6]。Unit 42 は、この連鎖の狙いの1つが Coinbase のリポジトリだったとし、Coinbase は損害は無かったと答えたと書く[8]。
通った道筋
| 段 | 公表されたこと |
|---|---|
| reviewdog の書き込み権限 | reviewdog の組織は、action の各リポジトリに貢献した人を自動で招き、書き込みの権限を持つ班に入れていた。班は当時118人だった。保守者は、この自動の招きか、既存の貢献者のアカウントの侵害で書き込みの権限を得たとみた[6]。後に、ある貢献者のトークンで、ふつうの git push でタグが上書きされていたと分かった。タグへの git push は、GitHub の公開の活動の記録に出ないとも書いた[6] |
| 中で呼ぶ部品 | reviewdog の他のアクションは中で action-setup を v1 で呼んでいたので、利用者がそのアクションを何で固定していても影響を受けた[5]。Unit 42 は、tj-actions の CI が、action-setup を中で呼ぶ tj-actions の別の部品を使っていたとする[8] |
| CI のトークン | 付け替えは tj-actions の bot に結び付いた個人用のアクセストークンからで、そのトークンは CI の秘密として置かれていた[4] |
| 利用者の CI | 悪いコードは、実行機の作業のプロセスのメモリから秘密を読み、Base64 にしてログへ出した[2]。CISA は、ログの秘密は2重の Base64 で隠れている場合があると注意した[7] |
- ログへ出したのは秘密そのものではなく、符号にした値である。符号にした値は元の秘密の文字列と一致しないので、ログの伏せ字を当てにできない。CISA が2重の Base64 に注意を促したのはこのためである[7]。
- 外へ送るのでなくログへ出したため、外への通信を絞っても漏れは止まらない。ただし、悪いコードは公開の gist から処理を取ってきており、その外への通信で見つかった[2]。
タグと、ハッシュでの固定
次は架空の組織 example-org の部品を使う、架空の CI の定義である。
悪い例。タグで呼び、広い権限で動かす
permissions: write-all
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: example-org/changed-files@v45 # タグは付け替えられる
- run: ./deploy.sh
env:
CLOUD_KEY: ${{ secrets.CLOUD_KEY }}
直した例。コミットのハッシュで固定し、権限と秘密を絞る
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: example-org/changed-files@4f2a9c1e7b3d5a6c8e0f1a2b3c4d5e6f7a8b9c0d # v46.0.1
deploy:
needs: lint
environment: production # 承認の要る環境にだけ本番の秘密を置く
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
env:
CLOUD_KEY: ${{ secrets.CLOUD_KEY }}
- GitHub の文書は、外部のアクションをコミットのハッシュで固定するよう求め、作者を信頼していても、リポジトリに入られればタグは動かされたり消されたりすると書く。ハッシュを選ぶときは、fork ではなくそのアクションのリポジトリのものかを確かめるよう求める[9]。
- 部品を使うジョブと、本番の秘密を使うジョブを分ける。この件の悪いコードはジョブのプロセスのメモリを読んだ[2]。同じジョブに秘密が無ければ、読む物が無い。
2026年11月時点の GitHub の文書は、推移的な依存まで含めて全部のアクションのコミットを記録し、実行時に出どころのリポジトリを確かめるロックファイルを、最も強い守りとし、ハッシュでの固定に代わるものとしている[9]。GitHub は2025年8月15日から、ハッシュで固定していないアクションを使うワークフローを失敗させる決まりを、企業、組織、リポジトリの単位で掛けられるようにした[10]。
どこで止められたか
| 止める所 | この件で | 限り |
|---|---|---|
| 書き込みを自動で配らず、タグを守る | reviewdog は貢献者を自動で招いて書き込みを渡していた。事故の後に自動の招きを止め、ほとんどの貢献者から書き込みを外した[6]。tj-actions は、検証されていないコミットを指すタグを押せない保護と、署名つきのコミットの必須を入れた[4] | 保守者の正規のトークンが盗まれれば、保護の範囲の中で付け替えられる |
| 長く使える個人のトークンを置かない | 付け替えたのは CI の秘密に置いた bot のトークンだった[4]。SpotBugs から reviewdog までも、個人のトークンを渡り歩いた[8] | CI が書き込む以上、何らかの資格情報は置く。寿命と範囲を絞るほど、盗まれた後に使える時間と場所が減る |
| 推移的な依存まで中身で固定する | changed-files をハッシュで呼んでいれば、付け替えは届かない[9] | 中で別の部品をタグで呼ぶ部品からは入られる[5]。固定した版に最初から悪いコードがあれば防げない |
| 部品を動かすジョブに秘密を渡さない | 悪いコードは実行機のプロセスのメモリから秘密を読んだ[2] | 同じジョブの中では、どの手順からもメモリは読める。ジョブを分けるしかない |
| 外への通信を見て、漏れたら全部回す | 外の gist への想定外の通信が見つかるきっかけだった[2]。CISA は、期間中に動いた CI の秘密を、漏れたものとして全部すぐに回すよう求めた[7] | 漏れはログへの出力で、通信を止めても漏れは止まらない。見つけた時には読まれている前提で回す |
守り方
- 外部のアクションは全部、コミットのハッシュで固定する。使える所では推移的な依存まで記録するロックファイルを使い、使えない所ではハッシュの固定を組織の決まりで強制する[9][10]。固定のハッシュは、fork でなく本来のリポジトリのものかを確かめる[9]。
- 自分の作る部品が中で使う部品も、ハッシュで固定する。reviewdog は事故の後に、全部のリポジトリで中のアクションをハッシュで指定し直した[6]。
- CI に長く使える個人用のアクセストークンを置かない。GITHUB_TOKEN は要る権限だけを付け、要るジョブにだけ広げる[9]。tj-actions は今後、組織で個人用のトークンを使わないとした[4]。
- 外の部品を動かすジョブと、本番の秘密を使うジョブを分け、本番の秘密は承認の要る環境に閉じる。
- fork からの変更の依頼で、秘密を持って動く pull_request_target の契機は、要らなければ使わない。使うなら信頼しないコードを取り出して動かさない[9]。
型としての整理は A03 ソフトウェアサプライチェーンの不備 の型3、ビルドと配布の仕組みを乗っ取られる型に当たる。悪い版が範囲指定で届いた先例は event-stream 2018年、配布物にだけ仕込まれた先例は xz utils 2024年 にある。
AIが書く CI の定義
- AIのコーディングの道具にワークフローを書かせると、アクションを版のタグで呼ぶ定義を出しがちである。差分で
uses:の行を見て、ハッシュでなければ直させる。固定の考え方は AIが持ち込む部品の危険 の固定する節にある。 - AIは、動かないと言われた CI を直すために
permissions: write-allを足したり、個人用のトークンを秘密に置く案を出したりする。権限を広げる差分は、人がその権限が要る理由を確かめてから入れる。
参照
- CVE-2025-30066 NIST NVD
- tj-actions changed-files through 45.0.7 allows remote attackers to discover secrets by reading actions logs GHSA-mrrh-fwg8-r2c3 GitHub Advisory Database、2025年3月15日
- Multiple tags in this action are compromised tj-actions/changed-files の issue 2463、2025年3月14日
- Pretty sure this repo got hacked tj-actions/changed-files の issue 2464、保守者の2025年3月15日と16日の書き込み
- CVE-2025-30154 NIST NVD、2025年3月19日公表
- Security Advisory Supply Chain Attack on reviewdog GitHub Actions during a specific time period reviewdog の保守者、2025年3月18日、3月23日の追記
- Supply Chain Compromise of Third-Party tj-actions/changed-files CVE-2025-30066 and reviewdog/action-setup@v1 CVE-2025-30154 CISA、2025年3月18日、3月26日改訂
- GitHub Actions Supply Chain Attack: A Targeted Attack on Coinbase Expanded to the Widespread tj-actions/changed-files Incident Palo Alto Networks Unit 42、2025年3月20日、4月2日更新。調査会社の調べ
- Secure use reference GitHub Docs、2026年11月に参照
- GitHub Actions policy now supports blocking and SHA pinning actions GitHub Changelog、2025年8月15日