ソフトウェア開発を効率化するうえで欠かせない CI/CD パイプラインですが、実は コマンドインジェクション や カスタムアクションを介したサプライチェーン攻撃 など、見過ごされがちなセキュリティリスクをはらんでいることをご存じでしょうか。特に GitHub Actions のように世界中で広く利用されているプラットフォームは攻撃者の標的になりやすく、万が一侵害されれば GitHub Token を通じてリポジトリ全体を乗っ取られる危険性もあります。本記事では、CI/CD に潜む脆弱性と対策のポイントをわかりやすく解説します。
本記事の内容は、DEF CON 32 で行われたセッション「Your CI/CD Pipeline Is Vulnerable, But It’s Not Your Fault」(以下「DEF CON 32 セッション」)の発表内容を参考に構成しています。
セッションの当該箇所へのリンクも記載しているので、詳細を確認しながら読み進めていただければと思います。
CI/CD は、Continuous Integration(継続的インテグレーション)と Continuous Delivery/Deployment(継続的デリバリー/デプロイ)の略で、以下のプロセスを自動化する仕組みです
手動作業を減らし、開発を素早く進められる反面、パイプライン自体が攻撃者の標的になり得ます。なぜなら、パイプラインにはクラウド環境の認証情報や秘密鍵など重要情報が集約されているからです。
CI/CD ツールとして、GitHub Actions は特に注目されています。DEF CON 32 セッションでは、以下のような理由が挙げられていました(3:00〜3:52 付近)
このように利便性が高いため、多くのリポジトリが導入し、結果として攻撃対象が増大します。
Web アプリケーション開発などで良く知られる脆弱性の1つで、ユーザ入力を適切にサニタイズせず OS コマンドに渡してしまう状況を指します。
例えば以下のようなコードを想像してください。
(DEF CON 32 セッション 6:54〜7:52 付近)
ここに "; rm -rf / のような文字列を渡されると、想定外のOSコマンドを実行してしまいます。CI/CD でも同様のことが起こりうるのです。
GitHub Actions では、ワークフロー内に run: echo ${{ github.event.issue.title }} のような記述があると、Issue タイトルに悪意のある文字列を仕込むことで任意のコマンドが実行される可能性があります。
上記の右下を見るとGitHub Actionsの実行ログにIssueのタイトルが出力されていることがわかります。つまり、GitHub Actionsの処理を実行しているOS上でechoコマンドが実行されているということです。
それでは、Issueのタイトルに下記画像の一番下の文字を登録されたら何が起きるでしょうか。
このスクリプトの内容を解読すると、、、
この内容を実際にGitHubのIssueタイトルに登録してみましょう。
下記画像は、悪意のある外部サーバのログです。
外部サーバにGitHubプロジェクト上でsecretとして設定されている機密情報が送信されていることがわかります。
これらのsecretsの値に、たとえばAWS本番環境のアクセスキー等を登録していたら、AWSの本番環境に第3者がアクセスできるようになってしまうでしょう。
ユーザ入力を直接コマンド引数に渡すのではなく、環境変数に保存してから使うなどの対策が必要です。
GitHub Actions では、ワークフローごとに GITHUB_TOKEN という特別なトークンが自動生成されます。このトークンは以下の操作が可能です(5:00〜6:00 付近)。
もし攻撃者がこのトークンを奪取すると、リポジトリのオーナーやメンテナと同等の権限で操作されてしまいます。リポジトリを支配されるだけでなく、その成果物を利用するエンドユーザにも被害が拡大し得ます。
GitHub Actions には、コミュニティやサードパーティが作成した「 カスタムアクション (Composite Action) 」を容易に導入できます。
しかし、依存関係が階層的に広がるため、どこか1つにコマンドインジェクションの脆弱性があれば、利用者側のパイプライン全体が危険にさらされます(9:00〜12:00 付近)。自分のコードは安全でも、外部から持ち込んだアクションが脆弱だと意味がありません。
Google のフラッグシップ OSS である Bazel(ビルド&テストツール)でも、カスタムアクションによりコマンドインジェクション脆弱性が見つかりました。
Bazel リポジトリ本体のワークフローに明示的な問題は見当たらずとも、別リポジトリで管理されているアクションに対するユーザ入力がサニタイズされていなかったケースです(約 14:00〜16:00 付近)。
GITHUB_TOKEN などのシークレットを奪取し、リリースやコード改ざんが可能に大規模 OSS でこうした脆弱性があると、そのプロジェクトを使っている全ユーザや企業に影響が及ぶ恐れがあります。
GitHub Actions を安全に使うためには、Secrets の管理からスクリプトインジェクション対策、さらに自己ホスト型ランナーやクロスリポジトリアクセスの管理まで、幅広い観点からのセキュリティ強化が必要です。以下では、公式ドキュメント(GitHub Docs: Security hardening for GitHub Actions) の主なポイントを抜粋してまとめています。
GITHUB_TOKEN は読取り専用 (read) をデフォルトにし、必要なステップだけ write にする。Using CODEOWNERS to monitor changes
.github/workflows ディレクトリを CODEOWNERS に指定しておき、変更時に信頼できる人の承認を必須にする。Understanding the risk of script injections
github.event.issue.title や github.event.pull_request.body など、攻撃者が自由に書き換えられる入力を直接コマンドに渡すと、任意のコマンド実行につながる恐れがある。zzz";echo "hello";# のように悪意ある文字列を含められる可能性がある。Good practices for mitigating script injection attacks
env: TITLE: ${{ github.event.pull_request.title }} のように、シェルコマンドを生成する前に変数へ代入する。"$TITLE" のように二重引用符で変数を扱い、単純にコマンドがつながるのを防ぐ。uses: actions/checkout@<commit-SHA> のように、コミットハッシュで指定すると改ざんリスクを下げられる。Using Dependabot version updates to keep actions up to date
Preventing GitHub Actions from creating or approving pull requests
Using code scanning to secure workflows
Hardening for GitHub-hosted runners
Hardening for self-hosted runners
Considering cross-repository access
GITHUB_TOKEN はあくまで “1 リポジトリごと”
GITHUB_TOKEN が自動付与されるため、ここを狙われると大きな被害に繋がる可能性があります。今後も本ブログでは、ソフトウェア開発やインフラ運用のセキュリティに役立つ情報を発信していきます。ぜひブックマークしていただけると幸いです!