- ブラウザURL入力
- DNS名前→IP
- ルータ経路選択
- ロードバランサ振り分け
- Nginx受け口
- アプリ処理今ここ
- DB保存
同じ「Top 10」でも数えたものが違います。今回は CI/CD の一覧を、何を根拠にまとめたかから読み、その4番目の項目を手元の模型で動かします。
まず結論
CI/CD Top 10 の CICD-SEC-4 Poisoned Pipeline Execution(PPE)は、ソース管理には触れられるがビルド環境には触れられない者が、パイプラインに自分の手順を実行させる危険です。
設定ファイルを保護されたブランチから読んでも、その設定が呼ぶ Makefile やスクリプトが変更側のままなら、変更側の手順が同じ環境で走ります。原文はこれを Indirect PPE と呼びます。
模型のランナーで、設定ファイル ci.sh は保護された想定の main から読み、作業ディレクトリは変更側ブランチから取り出します。変更側は Makefile を書き換えています。実行されたのはどれでしょうか。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
何を数えた一覧か、そして PPE の主語
番号を読む前に、この一覧が何を根拠にまとめられたかを見ます。index はこう書きます。
The list was compiled on the basis of extensive research and analysis based on the following sources:
続く3項目は、数百の CI/CD 環境の構成の分析(hundreds of CI/CD environments)、専門家との議論、事故や弱点の公表文書です。数えた件数や割合は index 全文のどこにもありません。 並び順の基準も書かれていないので、重要度順とも頻度順とも言えません。著者は同じ1社に所属する2名(CTO と研究部門の責任者)です。
版も独特です。index と各項目の本文は版年を名乗りません。プロジェクトの Roadmap タブにだけ Initial release: v1.0, September 2022 とあります。各項目は CWE 番号も載せていません(確認範囲: CICD-SEC-1〜10 の全文)。
4番目の項目は原文で CICD-SEC-4(ファイル名は 04)です。定義の主語に注目します。
Poisoned Pipeline Execution (PPE) risks refer to the ability of an attacker with access to source control systems - and without access to the build environment, to manipulate the build process ...
ビルド環境に触れられない者が、ビルド環境で手順を走らせる。 走った手順は within the context of the pipeline’s identity、パイプラインの身元で動きます。書き換える対象も、定義の段階で設定ファイルに限っていません(the CI configuration files, or other files which the CI pipeline job relies on)。
同じコースで扱う依存パッケージの入れ替わりとは別の話で、こちらは「パイプラインがどのブランチの指示を実行するか」です。
- ・設定を書き換えるのが D-PPE、設定が呼ぶファイルに入れるのが I-PPE
- ・設定だけ守っても、右の箱が変更側なら変更側の手順が走る
模型のランナーで、設定を変更側から読む
手元で動かしたのは実在の CI 製品ではなく、20行ほどの自作の模型です。指定した ref の ci.sh を読み、別に指定した ref のソースを取り出した作業ディレクトリで、ダミー変数 DUMMY_SECRET を与えて実行するだけです。実行するコマンドは全部 echo です。
環境は Docker Engine 28.4.0(linux/arm64)上の alpine:3.20(3.20.10)、git 2.45.4、GNU Make 4.4.1、2026-09-28 実行。リポジトリには main(保護されている想定。模型では push を拒む仕組みは置いていません)と、ci.sh と Makefile を書き換えた change-branch があります。
### (c) control: config from main, source from main== runner: config read from ref 'main' / source checked out from ref 'main'-- config (ci.sh) as read:| echo "[ci.sh@main] step 1: build via make"| make build| echo "[ci.sh@main] step 2: done"-- Makefile present in the working directory (from 'main'):| build:| @echo "[Makefile@main] building the app"-- executing config:[ci.sh@main] step 1: build via make[Makefile@main] building the app[ci.sh@main] step 2: done== runner: done### (a) config read from the change branch, source from the change branch== runner: config read from ref 'change-branch' / source checked out from ref 'change-branch'-- config (ci.sh) as read:| echo "[ci.sh@change-branch] this ci.sh was rewritten on the change branch"| echo "[ci.sh@change-branch] DUMMY_SECRET seen by this step = ${DUMMY_SECRET:-<unset>}"-- Makefile present in the working directory (from 'change-branch'):| build:| @echo "[Makefile@change-branch] this Makefile was rewritten on the change branch"| @echo "[Makefile@change-branch] DUMMY_SECRET seen by make = $${DUMMY_SECRET:-<unset>}"-- executing config:[ci.sh@change-branch] this ci.sh was rewritten on the change branch[ci.sh@change-branch] DUMMY_SECRET seen by this step = dummy-value-not-a-real-secret== runner: done
(c) は設定も Makefile も main で、[Makefile@main] building the app が出ます。(a) は設定を変更側から読んだので、main の ci.sh は一切関与せず、変更側の ci.sh がそのまま走り、ダミー変数の値を表示しました。 原文の D-PPE の構造です。2回走らせ、差は時刻とコミットハッシュだけでした。
原文の PPE の定義文で、主語になっているのはどんな者ですか。
設定を守ると、残るもの
原文は PPE を3つに分けます(There are three types of PPE:)。
- D-PPE: 設定ファイルそのものを、保護されていないブランチへの push や PR で書き換える。
- I-PPE: D-PPE ができない場合でも、設定が参照するファイルに入れる。
- Public PPE(略して 3PE): 公開リポジトリで、匿名の人の未レビューのコードを CI が走らせる状況。新しい注入先ではなく、D か I を誰ができるかの区分です。
I-PPE の前提は3つ並びます。
- If the pipeline is configured to pull the CI configuration file from a separate, protected branch in the same repository.
これに別リポジトリに置く場合、CI システムの中で定義する場合が続きます(L31-33)。保護ブランチは3つのうちの1つです。 そのうえで原文は、参照先の例として make、スクリプト、テスト、linter やスキャナの設定の4つを挙げます(L39-42)。
ここで Recommendations の L195 を並べます。
Alternatively, the CI configuration file can be managed in a remote branch, separate from the branch containing the code being built in the pipeline. The remote branch should be configured as protected.
L31 が I-PPE の前提に挙げた構成と、L195 が推奨する構成は同じです。 原文は「保護ブランチでは I-PPE を防げない」とは書いていません。ただ2箇所を並べると、L195 が効くのは文頭のとおり設定ファイルの改ざんに対してで、参照先は別に残る、と読めます。
設定は main から、Makefile は変更側から
同じ模型で、設定だけを main から読みます。
### (b) config read from protected main, source from the change branch (Makefile comes from the change branch)== runner: config read from ref 'main' / source checked out from ref 'change-branch'-- config (ci.sh) as read:| echo "[ci.sh@main] step 1: build via make"| make build| echo "[ci.sh@main] step 2: done"-- Makefile present in the working directory (from 'change-branch'):| build:| @echo "[Makefile@change-branch] this Makefile was rewritten on the change branch"| @echo "[Makefile@change-branch] DUMMY_SECRET seen by make = $${DUMMY_SECRET:-<unset>}"-- executing config:[ci.sh@main] step 1: build via make[Makefile@change-branch] this Makefile was rewritten on the change branch[Makefile@change-branch] DUMMY_SECRET seen by make = dummy-value-not-a-real-secret[ci.sh@main] step 2: done== runner: done
読んだ設定は main の3行です。ところが make build は作業ディレクトリの Makefile、つまり変更側の Makefile を読み、その手順が同じ変数のある環境で走りました。
(a) と (b) の差は「秘密が見えたか」ではありません。どちらでもダミー変数は見えました。差は、どのファイルの手順が走ったかです。(a) は ci.sh、(b) は Makefile でした。
見えるものも秘密に限りません。原文の Impact は、秘密のほかにノード上のファイルやクラウドの資格、成果物の差し替え、ネットワーク上の他のホストを挙げます。
Recommendations は、設定ファイルを保護された別ブランチで管理する案を挙げています。I-PPE の節と並べたときの関係として正しいのはどれですか。
D-PPE の構造 — 設定を変更側から読む(模型の (a))
変更側の ci.sh がそのまま実行され、main の ci.sh は関与しませんでした。設定の読み元を保護ブランチに移せば、この経路は塞がります。
I-PPE の構造 — 設定は main、参照先は変更側(模型の (b))
設定は main の3行でしたが、その中の make が変更側の Makefile を読みました。設定の読み元を守っても、この経路は残ります。
2つのログで、変更側の手順が走った入口はそれぞれどのファイルでしたか。
「config (ci.sh) as read」と「Makefile present in the working directory」の from を見比べてください。
この章のまとめ
- CI/CD Top 10 は数百の環境の分析・専門家との議論・公表文書からまとめた一覧。件数も並び順の基準も書かれていない。本文に版年は無く、Roadmap に v1.0(2022年)。
- PPE の主語はソース管理に触れられるがビルド環境には触れられない者。手順はパイプラインの身元で走る。
- 3分類は D-PPE / I-PPE / 3PE。3PE は誰ができるかの区分。
- I-PPE の前提は3つで、保護ブランチはその1つ。参照先の例は make・スクリプト・テスト・linter などの設定。
- 模型では、設定を main から読んでも変更側の Makefile の手順が走った。対比の軸は「どのファイルの手順が走ったか」。
- 原文は「保護ブランチでは防げない」と明言していない。L31 と L195 を並べると、推奨が効く範囲が読める。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
CI/CD Top 10 の index は、一覧を何に基づいてまとめたと書いていますか。
用語集(3語)
- PPE(パイプライン実行ポイズニング)
- ソース管理に触れられる者が、CI 設定や設定が頼るファイルを書き換え、パイプラインの身元で自分の手順を実行させる危険。CI/CD Top 10 の CICD-SEC-4。
- D-PPE / I-PPE / 3PE
- 設定ファイルそのものを書き換える Direct、設定が参照するファイルに入れる Indirect、それを公開リポジトリで匿名の人ができる Public PPE(略して 3PE)。
- ランナー
- パイプラインの手順を実際に実行する側。本回で動かしたのは、指定した ref の ci.sh を読んで実行するだけの自作の模型。
出典(一次ソース)
- OWASP Top 10 CI/CD Security Risks — CICD-SEC-4: Poisoned Pipeline Execution (PPE)(www-project-top-10-ci-cd-security-risks、commit 74b2c790、取得 2026-09-28) ↗
- OWASP Top 10 CI/CD Security Risks — index(一覧の成り立ち。commit 74b2c790、取得 2026-09-28) ↗
- OWASP Top 10 CI/CD Security Risks — Roadmap タブ(v1.0 の記載。commit 74b2c790、取得 2026-09-28) ↗