再現可能ビルドとビルド来歴の検証
同じソースから誰がビルドしても同じバイナリになる状態を作れば、配布物がソースと一致するかを第三者が確かめられます。非決定性の除去と複数ビルダ照合で、ビルド改ざんを根から断てます。
- 1.再現可能ビルド(reproducible build)は、同一ソース・同一手順から bit 単位で同一のバイナリを得る性質。これが成り立てば『配布バイナリが本当にこのソースから出たか』を、ソースを再ビルドして比較するだけで第三者が独立検証できる。
- 2.再現性を壊すのは非決定性。タイムスタンプ・ファイル順・絶対パス・並列ビルドの順序・乱数・埋め込まれたビルド環境情報などが原因で、SOURCE_DATE_EPOCH 固定・パス正規化・ソート・ロックされた依存で一つずつ潰す。
- 3.複数の独立ビルダが同じソースから同じハッシュを再現すれば、単一ビルダの改ざんを多数決で検出できる。署名・透明性ログ・来歴(provenance)と束ね、検証側がハッシュ照合を強制して初めて防御になる。
「ソースとバイナリの一致」を証明可能にする
利用者が手にするのはソースではなくバイナリです。ところが公開ソースをいくら監査しても、配布バイナリが本当にそのソースから作られた保証はありません。汚染された CI が綺麗なソースから汚れた成果物を作っても、外からは見分けがつかない――これが サプライチェーン攻撃 のビルド層の急所です。再現可能ビルド(reproducible build) は、この断絶を埋めます。定義は単純で、「同一のソースと同一のビルド手順から、誰がいつどこでビルドしても bit 単位で同一のバイナリが得られる」性質を指します。これが成り立てば、検証者は配布バイナリのハッシュと、自分で再ビルドした成果物のハッシュを突き合わせるだけで、「このバイナリは確かにこのソースから出た」を独立に確認できます。信頼の対象が「ビルドした人・環境」から「公開ソースそのもの」へ移る、というのが核心です。
なぜ普通のビルドは再現しないのか:非決定性
同じソースでも、素朴にビルドすると成果物のハッシュは毎回ずれます。原因はビルド過程に紛れ込む非決定性(non-determinism) です。決定的(deterministic)とは「入力が同じなら出力が常に同じ」こと。ここに環境やタイミング由来の入力が混じると壊れます。代表的な発生源を押さえます。
| 非決定性の源 | なぜ混入するか | 除去の原理 |
|---|---|---|
| タイムスタンプ | ビルド日時をバイナリ・アーカイブに埋め込む | SOURCE_DATE_EPOCH で基準時刻を固定 |
| ファイル順序 | ディレクトリ走査順が OS/FS 依存 | アーカイブ・リンク前にソートして正規化 |
| 絶対パス | ビルドディレクトリ名がデバッグ情報に残る | パスを固定値へ書き換え(パスマップ) |
| 並列ビルドの順序 | ジョブ完了順でリンク順や ID が変わる | 順序に依存しない結合・ID 採番 |
| 乱数・ロケール・TZ | ハッシュ順序・文字列整形・時刻表示が揺れる | シード・LANG・TZ を固定 |
要点は、これらがバイナリの意味を変えないのにハッシュだけを変える点です。たとえば書庫(tar 等)はファイルの mtime と格納順を保持するため、コンパイル結果が同一でも書庫が変わり、ハッシュがずれます。だから「同じ意味の出力を、同じ byte 列へ正規化(canonicalize) する」ことが再現性の作業の実体になります。
SOURCE_DATE_EPOCH は「ビルドに埋め込む時刻はこの Unix 時刻に固定せよ」という事実上の標準環境変数です。多くのコンパイラ・アーカイバ・ドキュメント生成器がこれを参照し、現在時刻の代わりに使います。値には通常、最新コミットのコミット時刻(git では git log -1 --pretty=%ct)を採ります。こうすると「いつビルドしたか」ではなく「どのソース状態か」だけが成果物に反映され、時刻起因のずれが一掃されます。
再現性を確立する手順の原理
実務では「まず差分を観測し、源を一つずつ潰す」反復になります。鍵となる道具が diffoscope で、2 つの成果物を再帰的に展開(書庫の中の書庫、ELF のセクション、メタデータまで)して意味のある差分を可視化します。生のバイナリ diff では「どこが違うか」しか出ませんが、diffoscope は「mtime が違う」「ファイル順が違う」「埋め込みパスが違う」と原因の言葉で示すため、対策に直結します。
再現性確立のループ(概念):
1. 同一ソース・同一ツールチェーンで 2 回(別環境で)ビルド
2. 2 つの成果物のハッシュを比較 → 一致なら完了
3. 不一致なら diffoscope で差分の正体を特定
4. 源に応じて正規化(時刻固定/ソート/パスマップ/シード固定)
5. 1 に戻る
依存の固定も前提です。ツールチェーン(コンパイラ・リンカ・ライブラリ)のバージョンが違えば出力は変わるため、ロックファイルやコンテナイメージのダイジェスト固定でビルド環境ごと再現可能にします。ここを詰めるほど「環境を持ち寄っても一致する」状態に近づきます。なお完全な bit 一致が難しい領域(一部の JIT 由来出力など)では、意味的に同等な部分を除いた正規化比較で妥協することもありますが、検証の強度は落ちます。
再現可能ビルドが保証するのは「バイナリがソースに一致する」ことだけで、「そのソースが安全」かは別問題です。ソース自体にバックドアがあれば、再現性はそのバックドアを忠実に再現します。再現可能ビルドはソース監査を無意味化しない――むしろ「監査したソースが配布物に確実に反映される」ことを保証し、ソース監査の価値をバイナリまで届かせるための土台です。両者は補完関係にあります。
複数ビルダによる相互検証
再現可能であること自体は「検証可能になる」だけで、まだ攻撃を止めません。止めるのは複数の独立ビルダによる照合です。原理はシンプルで、互いに独立した N 個のビルダが同じソースから各自ビルドし、全員が同じハッシュを出せば、その成果物は信頼できる。逆に1 つだけ異なるハッシュを出したら、そのビルダが改ざんされたか環境が汚染された疑いが立ちます。攻撃者が成果物を差し替えるには、独立したビルダの過半数を同時に侵害しなければならず、難度が跳ね上がります。
相互検証(概念):
builders = [A, B, C] # 互いに独立・隔離された環境
hashes = { ビルダ -> 再ビルド成果物のハッシュ } # 集合 {A,B,C} の各出力
if すべてのハッシュが一致:
成果物を信頼してよい(N者が独立に同じ結論)
else:
不一致のビルダを隔離し、ソース・環境・ツールチェーンを精査
ここで効くのが、ビルダの独立性です。同じ CI テンプレート・同じベースイメージを共有していると、その共通部分を一突きで汚染でき、N 者の独立という前提が崩れます。独立性が高いほど(別組織・別 OS・別ネットワーク)、相互検証は強くなります。これは サプライチェーンの完全性 で扱う SLSA の「ビルド隔離」の発想と地続きで、隔離されたビルダがそれぞれ署名と来歴を出すことで、照合に耐える証拠になります。
来歴・署名・透明性ログと束ねる
再現可能ビルドは単独で動く機構ではなく、来歴(provenance)・署名・ログと鎖にして初めて運用になります。役割分担を整理します。
| 仕組み | 何を保証するか | 再現可能ビルドとの関係 |
|---|---|---|
| 再現可能ビルド | バイナリがソースに一致する | 第三者が再ビルドで独立検証できる土台 |
| 署名 | 配布物がすり替えられていない | 誰のビルド結果かを暗号で固定する |
| 来歴(provenance) | どのソース・ビルダ・手順で作ったか | 再ビルドに必要な手順を機械可読に残す |
| 透明性ログ | 署名・ハッシュを消せない記録に残す | 複数ビルダの結論を後から監査可能にする |
来歴は、再現に必要な「どのコミットを、どのツールチェーンで、どの手順で」を 署名付き宣言 として残します。検証側はこの手順どおりに再ビルドし、ハッシュを照合できます。複数ビルダが各自の結果を 追記専用の透明性ログ(Merkle 木 構造)に記録すれば、「あるビルダだけ違うハッシュを後から黙って書き換える」ことも一貫性証明で検出できます。こうして「ソース→手順→各ビルダの再現結果→消せない記録」が一本の検証可能な鎖になります。
再現可能にし、来歴を付け、署名する――これらは受け取り側がハッシュを照合して拒否する運用があって初めて防御になります。再現可能だが誰も再ビルドして比べない、来歴はあるが照合しない、では攻撃者に「正しく見える書類」を渡すだけです。配布の取り込み(admission)時に「独立ビルダ N 者のハッシュが一致し、来歴のソースが許可リスト内でなければ拒否」というポリシーを強制して、初めて『ソースとバイナリの一致』が実運用の盾になります。
- 再現可能ビルド = 同一ソース・同一手順から bit 単位で同一バイナリ。配布物がソースに一致するかを第三者が再ビルドで独立検証できる。
- 壊す元凶は非決定性(タイムスタンプ・ファイル順・絶対パス・並列順序・乱数/ロケール/TZ)。SOURCE_DATE_EPOCH・ソート・パスマップ・依存固定で正規化して潰す。
- diffoscope で差分の「原因」を特定し、源を一つずつ除去する反復で確立する。
- 防御の核は複数独立ビルダの相互検証。全員同一ハッシュなら信頼、1 者だけ不一致なら汚染を疑う。独立性が高いほど強い。
- 単独では効かず、署名・来歴・透明性ログと束ね、検証側がハッシュ照合を強制して初めて改ざんを止める。再現可能 ≠ ソースが安全。
再現可能ビルドを「ビルドが安定する話」で止めず、なぜ非決定性がハッシュだけを変えるのか・正規化が何を等価にするのか・複数ビルダの一致が攻撃者に何を強いるか・来歴と署名がどこを埋めるかまで分解できると、ビルド改ざんがどの層で止まり、なぜソース監査だけでもビルダ信頼だけでも素通りされるかが一本の線でつながります。
セキュリティ Article
再現可能ビルドとビルド来歴の検証を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
再現可能ビルド
比較で見る軸
難易度: advanced / カテゴリ: セキュリティ / タグ数: 5
導入後に効く点
再現性を壊すのは非決定性。タイムスタンプ・ファイル順・絶対パス・並列ビルドの順序・乱数・埋め込まれたビルド環境情報などが原因で、SOURCE_DATE_EPOCH 固定・パス正規化・ソート・ロックされた依存で一つずつ潰す。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- セキュリティ
- タグ数
- 5
判断チェックリスト
- 自社の用途が「再現可能ビルド / サプライチェーン」に近いか確認する。
- 強みである「再現可能ビルド(reproducible build)は、同一ソース・同一手順から bit 単位で同一のバイナリを得る性質。これが成り立てば『配布バイナリが本当にこのソースから出たか』を、ソースを再ビルドして比較するだけで第三者が独立検証できる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。