9月21日、Rust公式ブログが「GitHub Actions leaking secrets when Miri output is cached」と題した記事を公開した。この記事では、cargo miriの実行時に全環境変数がtarget/ディレクトリに書き出される挙動が、GitHub Actionsのキャッシュ機構と組み合わさることでシークレット情報の漏洩につながりうる問題について詳しく解説されている。
この問題を報告したのは、Rustエコシステムで広く使われるcargo-semver-checksの作者としても知られるOpenAIのPredrag Gruevski氏だ。Supply chain攻撃への関心が高まる昨今、CIパイプラインに潜むシークレット漏洩リスクは見落とされがちな盲点であり、今回の発覚はタイムリーな警鐘となっている。
何が起きているのか
MiriはRustの未定義動作(undefined behavior)を検出するインタープリタであり、cargo miriコマンドで実行する。Miriはビルド環境の再現性を保つため、実行時のすべての環境変数をtarget/ディレクトリに書き出す実装になっている(該当コード)。
通常、target/はCIのビルド時間を短縮するためにキャッシュされることが多い。GitHub Actionsではキャッシュのスコープ仕様上、mainブランチのCIランがキャッシュへの書き込み権限を持ち、PRからはそのキャッシュを読み取れる構成が一般的だ。つまり、シークレットを含むtarget/がキャッシュに保存されていると、PRを通じて第三者がその内容にアクセスできてしまう。
攻撃シナリオが現実的な理由
GitHubのポリシー上、リポジトリへの初回PRはメンテナの承認が必要だが、一度PRがマージされた貢献者は、以降のPRで自動的にCIが走る。つまり、過去に1回でもコントリビュートした人物が、キャッシュからシークレットを読み出すCIを実行し、その後に別のコミットをプッシュして証拠を上書きする、という手順が成立する。
さらにGitHubのUIは上書きされたコミットを表示しないことがあり、CIのログや上書きコミットは数ヶ月後に削除される。発覚が難しい点が、この問題を深刻にしている。
影響を受けるかどうかの判断
以下の条件をすべて満たす場合、対象となる。
- CIで
cargo miriを実行している - そのステップ(またはそれ以前のステップ)がシークレットを環境変数として受け取っている
- actions/cacheやswatinem/rust-cacheなどで
target/ディレクトリをキャッシュしている - そのキャッシュがPRからアクセス可能な状態になっている
Rust公式チームはエコシステムスキャン(OpenAIが提供したCodexを活用)を実施し、脆弱な状態にあるリポジトリを1件、確認を推奨するリポジトリを7件特定してそれぞれのメンテナに連絡済みだという。ただし、スキャンは完全ではないとして、自分のリポジトリを各自で確認するよう促している。
取るべき対処
即時の回避策は以下のいずれかだ。
- Miriを実行するジョブのキャッシュを無効化する
- シークレットのスコープを、Miriを呼ばないステップに限定する
- 暫定的にMiriを無効化する
対処後はキャッシュの削除を行い、漏洩した可能性のあるシークレットのローテーションも検討すること。
根本的な修正として、Rust公式チームは短期対応のPRをマージ済みだ。CARGO_*系の変数(ただしCARGO_*_TOKENは除外)とOUT_DIRのみを保持する形に変更されており、この修正は2026年9月22日のnightlyに含まれている。
Miriを使っていなくても注意が必要
記事は最後に重要な指摘をしている。Miriを使っていないプロジェクトでも、パブリックなキャッシュに書き込めるジョブがシークレットにアクセスできる状態は避けるべきだ、という点だ。多くのツールはシークレットを特別扱いせず、環境変数全体をファイルシステムに書き出す可能性がある。ビルドスクリプトが環境変数をコンパイル成果物に含めてしまうケースも想定される。
Cargo/Miri/Rust自体は「環境変数がtarget/に書き出されないことを保証しない」という立場であり、今回の修正はその保証を与えるものではなく、あくまでMiriとして不要な変数を書き出さないようにしたものだ。CIのシークレット管理をあらためて見直す良い機会といえる。
詳細はGitHub Actions leaking secrets when Miri output is cachedを参照していただきたい。