---
title: 「miseが勝手に検証してくれる」の裏側
tags: 
author: [Kattyan](https://image.docswell.com/user/kattyan)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/PJR9D2VL79.jpg?width=480
description: 未踏ITの有志合宿で話した内容です
published: September 12, 26
canonical: https://image.docswell.com/s/kattyan/ZN78LR-how-to-verfy-packages-mise
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/PJR9D2VL79.jpg)

公開
「miseが勝手に検証してくれる」の裏側
GitHub Artifact AttestationsとSigstore
Kattyan


# Page. 2

![Page Image](https://bcdn.docswell.com/page/PEXQ1DW6JX.jpg)

公開
きっかけ: 自作ツールのリリースに4行足した
sou1118/typdiff1 のrelease workflow2
permissions:
contents: read
id-token: write
# 追加
attestations: write
# 追加
# ...ビルド後...
- uses: actions/attest@v4.2.2
with:
subject-path: typdiff-*.tar.gz
するとmise installが勝手にこうなる
[2/3] checksum typdiff-aarch64-...
[2/3] verify GitHub artifact
attestations
[2/3] ✓ GitHub artifact
attestations verified
• こちらはmise側に何も設定していない
• この✓は一体何を検証したのか?
1https://github.com/sou1118/typdiff/pull/21
2 actions/attest: https://github.com/actions/attest
2


# Page. 3

![Page Image](https://bcdn.docswell.com/page/3EK92XVGED.jpg)

公開
そもそも何を解決したいのか
• リリースバイナリを落とすとき、
普通は「誰がどこで何からビルドしたか」が
分からない
• checksumは「配布物が途中で壊れていない」ことしか言わない
• しかもchecksumも同じリリースページから降ってくる(同じ鍵で守られた金庫と鍵)
• 欲しいのは来歴(provenance)3 — 次の主張を後から暗号学的に検証できる形で
残したい
成果物
typdiff-…-apple-darwin.tar.gz
sha256: 1fb5f0db…fab2f
「repo Xのworkflow Yがtag Zから作った」
←
→
出所 + 発行者の署名
ソース/ビルド
repo X / workflow Y / tag Z
3SLSA Provenance v1: https://slsa.dev/spec/v1.0/provenance
3


# Page. 4

![Page Image](https://bcdn.docswell.com/page/L73W45M575.jpg)

公開
全体像: 署名する側と検証する側
GitHub公式の解説4
署名(GitHub Actions側)
Runner → OIDC ID Token → Fulcio (CA) → 10分だけ有効な証明書
→ in-toto/SLSA文書に署名 → Rekor (透明性ログ) → Attestations APIに保存
検証(mise側)
成果物のSHA-256 → Attestations APIからbundle取得 → TUFで信頼の起点を取得
→ 証明書チェーン/ログを検証 → subjectのdigestと照合
4https://docs.github.com/en/actions/concepts/security/artifact-attestations
4


# Page. 5

![Page Image](https://bcdn.docswell.com/page/87DKQD6YJG.jpg)

公開
Sigstore: 鍵を持たない署名(keyless signing)
• 従来の署名の一番の問題は鍵の管理 (保管・ローテーション・失効・漏洩)5
• Sigstoreの発想: 長期鍵を持たない。ビルドのたびに作って捨てる
←
② IDトークンを要求(id-token: write)
→
Actionsの
OIDCプロバイダ
→
Fulcio (CA)6
→
Rekor7
(追記専用の透明性ログ)
ID Token (repo, workflow, ref …)
ワークフロー(Runner)
③ ID Token + 公開鍵を提示
① 鍵ペアをその場で生成
←
有効期間10分のX.509証明書(claim入り)
⑤ 秘密鍵は捨てる
④ 署名 + 証明書を記録
• 鍵を使い捨てにするという解決法
5Sigstoreの全体像: https://docs.sigstore.dev/about/overview/
6https://docs.sigstore.dev/certificate_authority/overview/
7https://docs.sigstore.dev/logging/overview/
5


# Page. 6

![Page Image](https://bcdn.docswell.com/page/VJPKLXY2E8.jpg)

公開
実際に発行された証明書(typdiff v0.1.3)
Issuer: O=sigstore.dev, CN=sigstore-intermediate
Validity
Not Before: Aug 20 16:44:03 2026 GMT
Not After : Aug 20 16:54:03 2026 GMT
← 10分
Subject:
← 空 (人間の識別子がない)
X509v3 Subject Alternative Name: critical
URI:https://github.com/sou1118/typdiff/.github/workflows/release.yml@refs/tags/v0.1.3
1.3.6.1.4.1.57264.1.8 (Issuer)
https://token.actions.githubusercontent.com
1.3.6.1.4.1.57264.1.11 (Runner Env)
github-hosted
1.3.6.1.4.1.57264.1.12 (Source Repo URI) https://github.com/sou1118/typdiff
1.3.6.1.4.1.57264.1.21 (Run Invocation) .../actions/runs/32393569126/attempts/1
CT Precertificate SCTs: ...
• 主体は人間ではなく「ワークフローのref」89
• OIDCのclaimがそのままX.509拡張になっている
8OIDの一覧: https://github.com/sigstore/fulcio/blob/main/docs/oid-info.md
9再現: gh api /repos/{owner}/{repo}/attestations/sha256:&lt;digest&gt; → 証明書をopenssl x509 -inform der -text
6


# Page. 7

![Page Image](https://bcdn.docswell.com/page/2EVVQLGXEQ.jpg)

公開
署名されている中身: in-toto + SLSA Provenance
{ &quot;_type&quot;: &quot;https://in-toto.io/Statement/v1&quot;,
&quot;subject&quot;: [{ &quot;name&quot;: &quot;typdiff-aarch64-apple-darwin.tar.gz&quot;,
&quot;digest&quot;: { &quot;sha256&quot;: &quot;1fb5f0db...fab2f&quot; } }],
&quot;predicateType&quot;: &quot;https://slsa.dev/provenance/v1&quot;,
&quot;predicate&quot;: { &quot;buildDefinition&quot;: {
&quot;buildType&quot;: &quot;https://actions.github.io/buildtypes/workflow/v1&quot;,
&quot;externalParameters&quot;: { &quot;workflow&quot;: { &quot;ref&quot;: &quot;refs/tags/v0.1.3&quot;,
&quot;repository&quot;: &quot;https://github.com/sou1118/typdiff&quot;,
&quot;path&quot;: &quot;.github/workflows/release.yml&quot; } },
&quot;resolvedDependencies&quot;: [{ &quot;uri&quot;: &quot;git+https://github.com/sou1118/typdiff@refs/tags/v0.1.3&quot;,
&quot;digest&quot;: { &quot;gitCommit&quot;: &quot;8f7dedf1...&quot; } }] },
&quot;runDetails&quot;: { &quot;builder&quot;: { &quot;id&quot;: &quot;.../release.yml@refs/tags/v0.1.3&quot; } } } }
• subject (何を) × predicate (どう作ったか)という汎用の形10
• actions/attestはpredicateを渡さないとSLSA build provenanceを自動生成する
11
10in-toto Statement v1: https://github.com/in-toto/attestation/blob/main/spec/v1/statement.md
11Attestation Modes: https://github.com/actions/attest
7


# Page. 8

![Page Image](https://bcdn.docswell.com/page/57GLWX8REL.jpg)

公開
コンテナ: Sigstore BundleとDSSE
• GitHubが返すのはapplication/vnd.dev.sigstore.bundle.v0.3+json12
• dsseEnvelope: payload (さっきのJSON) / payloadType / signatures
• verificationMaterial: certificate / tlogEntries / timestampVerificationData
• DSSE (Dead Simple Signing Envelope)13
• payloadそのものではなくpayloadTypeを含めてエンコードした列に署名する
•「JSONとして同じだが別の型として解釈させる」類の混同攻撃を防ぐ
• tlogEntriesにはRekorのlogIndex / integratedTimeに加えて inclusion proof
(ハッシュツリーの経路)とSET (ログの署名)が入る
• → ログサーバに問い合わせずに「確かにログに載っている」ことを検証できる
• 証明書は10分で失効するが、
この時刻が「署名時点では有効だった」ことを示す
12Bundleの定義: https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto
13https://github.com/secure-systems-lab/dsse/blob/master/protocol.md
8


# Page. 9

![Page Image](https://bcdn.docswell.com/page/4EQY389YJP.jpg)

公開
検証側(mise)は何をしているか
• miseはmise-sigstoreという自前クレートで検証している14
mise
① 成果物のSHA-256を計算
④ 証明書チェーン・SCT・
inclusion proof / SETを検証
⑤ DSSE署名を検証し
subject[].digestと ① を照合
←
② GET /repos/{owner}/{repo}/attestations/sha256:{digest}15
→
GitHub
Attestations API
→
Sigstoreの
TUF16リポジトリ
Sigstore bundle
③ trusted rootを取得・更新
←
Fulcio / Rekorの公開鍵
• ⑤ が抜けると「どこかの正しい成果物の署名」で任意のファイルが通ってしまう
• デフォルト有効(github_attestations)17。mise.lockにもprovenanceとして
記録される
14https://github.com/jdx/mise crates/mise-sigstore, src/github/sigstore.rs
15https://docs.github.com/en/rest/repos/attestations
16https://theupdateframework.io/
17https://mise.jdx.dev/dev-tools/backends/github.html
9


# Page. 10

![Page Image](https://bcdn.docswell.com/page/KJ4W12XZ71.jpg)

公開
ハマりどころ: Sigstoreインスタンスが2つある
bundleの署名証明書のissuerで分岐する1819
↓
public repo / O=sigstore.dev
private / internal repo / O=GitHub, Inc.
Sigstore public good instance
GitHub内製インスタンス
・公開の透明性ログあり
・inclusion proof / SCTが付く
・標準ポリシーで完全に検証
・公開の透明性ログがない
・inclusion proofもSCTも無い
・trusted rootを差し替え + skip_sct / skip_tlog
• 素直に「Sigstoreの標準ポリシー」で検証すると右が必ず落ちる
•「同じ仕組みに見えて信頼の根っこが別物」というのが実装を読むと分かる
18miseのis_github_internal_certificate / verify_bundle (crates/mise-sigstore/src/lib.rs)
19https://github.com/actions/attestのREADME、およびhttps://docs.github.com/en/actions/concepts/security/artifact-attestations
10


# Page. 11

![Page Image](https://bcdn.docswell.com/page/LE1YGMND7G.jpg)

公開
これは何を保証しないのか
• GitHub公式ドキュメント20いわく
artifact attestations are not a guarantee that an artifact is secure
• 保証するのは「このソースとこのビルド手順から出てきた」ことだけ
• attestationは誰でも自分のリポジトリで作れる
•「署名がある」≠「信頼できる」。どのrepo / workflowを信じるかは人間が決める
• 厳密にやるならgh attestation verify --signer-workflow ...のようにidentityを固定する
• ビルドスクリプト自体が汚染されていれば、
検証は普通に通る
• ここから先はSLSAのlevel (ビルド環境の隔離、改ざん耐性)の話になる
• public repoでは出所が公開の透明性ログに載る (隠したいものは載せない)
20https://docs.github.com/en/actions/concepts/security/artifact-attestations
11


# Page. 12

![Page Image](https://bcdn.docswell.com/page/GEWGK348J2.jpg)

公開
まとめ
• actions/attestは4行で入るが、中では OIDC → Fulcioの10分証明書 → in-toto/
SLSA文書にDSSE署名 → Rekorが動いている
• 鍵を長期保管しないための設計(keyless)が全体の形を決めている
• 検証側はTUFで信頼の起点を取り直し、証明書・ログ・そして subject digestの
一致を確認している
• public / privateでインスタンスが分かれており、検証ポリシーの分岐が必要
• 同じ仕組みはnpm provenance21 / PyPI (PEP 740)22 / Homebrewのbottle検
証23などにも広がっている
•「署名されている」ことより「何が署名されているか」を読むのが大事
21https://docs.npmjs.com/generating-provenance-statements
22https://peps.python.org/pep-0740/
23 HOMEBREW_VERIFY_ATTESTATIONS: https://docs.brew.sh/Manpage
12


