アップデートパイプラインはグリーンで、バンドルはエッジで利用可能、デバイスは更新を確認しています。すると、生成された JavaScript に不審な変更があることに誰かが気づきます。テスト完了後、侵害されたビルドランナー、盗まれた開発者資格情報、改ざんされた成果物が、リリースに悪意あるペイロードを混入させた可能性があります。 署名検証がなければ、クライアントはチームがビルドしたバンドルと、攻撃者が転送中または配信レイヤーで改ざんしたバンドルを区別する確実な方法がありません。
署名付きアップデートはこの判断を変えます。デバイスは、現在実行中の code を置き換える前に、信頼できる公開鍵を使ってバンドルを検証します。署名が一致しない場合、アップデートは非アクティブのままです。一見シンプルに見えますが、本番環境での失敗は暗号技術そのものではなく、その周辺で起こることが多いです。チームは鍵のローテーションを追えなくなり、誤った成果物に署名したり、未検証のキャッシュファイルを適用したり、テレメトリの収集が少なすぎてどのデバイスがアップデートを拒否したかを説明できないこともあります。
以下のセクションでは、信頼モデルと検証フローから CI/CD の制御、インシデント対応、署名だけでは解決できない限界まで、こうした運用上のエッジケースに焦点を当てます。
目次
- 署名検証が壊滅的なアップデートを防ぐ理由
- 暗号署名が信頼を生み出す仕組み
- モバイル・Web システムでの実践的な活用
- 完全な検証フローの構築
- 多くのチームが見落としがちな鍵管理の課題
- CI/CDとモニタリングへの検証の統合
- セキュリティのベストプラクティスとよくある落とし穴
署名検証が壊滅的なアップデートを防ぐ理由
人気のCapacitorプラグインのCI/CDパイプラインが、リリースプロセスの終盤に侵害されます。攻撃者はlive updateバンドルを改ざんし、アプリケーションデータを読み取るcodeを追加して、パイプラインの通常の配信経路でアーティファクトを公開します。デバイスには見慣れないダウンロードドメインは表示されません。インストール待ちの有効なアップデートとして表示されるだけです。
クライアント側の暗号学的検証がない場合、アプリはアップデートポリシーが許可し次第、改ざんされたバンドルをダウンロードして実行する可能性があります。想定される影響範囲には、データ流出、認証情報の窃取、決済フローの改ざん、ビジネスロジックのサイレントな操作が含まれます。調査も困難です。エンジニアは、どのアーティファクトが配信され、どのチャネルが受け取り、どのデバイスがダウンロードし、どのデバイスが適用したか、またリリースが取り下げられる前に悪意のあるcodeが実行されたかどうかを特定する必要があります。

署名検証を有効にしているチームでは、障害モードが異なります。アプリは同じ汚染されたバンドルをダウンロードし、期待されるダイジェストを計算して、添付された署名を信頼済みキーと照合します。暗号学的検証に失敗すると、アップデーターはバンドルの有効化を拒否し、新しいcodeが実行される前にセキュリティチームにイベントが届きます。
本番環境のルール: デバイスが更新の同一性と正確なバイト列の両方を検証するまで、その更新を敵対的なものとして扱うこと。
この保護が機能するのは、検証器が抽出やアクティベーションの前にデバイス上で動作し、信頼済みキーがアップデート自体によって置き換えられない場合だけです。つまり、証明書とキーの扱いは管理上の細部ではなく、アップデート設計の一部になります。Capacitorを使用しているチームは、この境界を自分たちの 証明書管理プロセス。これには、誰が署名できるか、キーの保管場所、クライアントが承認された後継キーをどう知るかが含まれます。
現代の研究では、署名検証は数十年にわたり、測定可能な工学分野として扱われてきました。オフラインとオンラインの両方の署名検証に関する最初の研究は1977年に発表され、その後の研究はHMMやFFTを含む手法へと広がりました。広く引用される比較では、人間の専門家は、誤受入れが約 0.5%、誤拒絶が7%一方、一般の人は 誤受入れ6.5%、誤拒絶26%に達しましたとしており、 署名検証研究の歴史的概要はこちら。これらの数値はソフトウェアアップデートではなく手書き署名に関するものですが、検証の品質は検証器、その参照データ、判定ポリシーに依存するという有用な点を裏付けています。
暗号署名が信頼を生み出す仕組み
リリースbundleを封印された封筒だと考えてください。ビルドシステムは正確なバイト列のダイジェストを計算し、次に 秘密鍵 を使用して、そのダイジェストに対してデジタル署名を作成します。アプリは、対応する 公開鍵を保持しているか、安全に受信しており、これが封筒に押された既知の紋章のような役割を果たします。攻撃者がバンドルのほんの一部でも改ざんした場合、アプリが計算するダイジェストは変わり、署名は検証できなくなります。

このフローは4つの明確な要素で構成されます:
- ハッシュ化 バンドルを固定長のダイジェストに変換します。この完全性確認のステップでは、SHA-256やSHA-512がよく使われます。
- 鍵生成 非対称鍵ペアを作成します。秘密鍵で署名し、公開鍵で検証します。
- 署名 ダイジェストをリリースメタデータ(バージョン、チャンネル、プラットフォーム、対象ユーザーなど)に紐付けます。
- 検証 ダウンロードしたバイト列からダイジェストを再計算し、その署名が信頼できる秘密鍵で生成されたものであるかを確認します。
検証側は、署名をアップデーターが適用するペイロードと同じものに紐付ける必要があります。アプリが後から、マニフェストのハッシュがバンドルと一致することを確認せずにバンドルをダウンロードするなら、マニフェストに対する署名だけでは不十分です。同様に、ハッシュ自体が認証されていなければ、バンドルのハッシュを検証しても誰がそれを承認したかは分かりません。
モバイル配信に向けたアルゴリズムの選択
RSAは馴染みがあり広くサポートされていますが、一般的により大きな鍵と慎重なパディングの選択が必要です。新しいモバイルアップデートプロトコルでは、鍵と署名がコンパクトで、モバイルプロセッサ上での検証処理が効率的なことから、Ed25519がよく選ばれます。互換性の要件でRSAが必要な場合は、RSA-PSSも適切です。選択にあたっては、他の環境のベンチマークを真似るのではなく、プラットフォームの暗号ライブラリ、対応ハードウェア、相互運用性の要件、移行計画に従うべきです。
参考になる独立系の入門ガイドとして、こちらの記事があります: ブロックチェーンのための暗号署名を理解する。トランザクションの文脈はOTA配信とは異なりますが、秘密鍵による承認と公開鍵による検証の説明はそのまま当てはまります。
信頼チェーンとピン留めされた鍵
証明書チェーンは、ルートから中間認証局を経てリーフ証明書へと信頼を委譲します。このモデルは広範なPKI運用を簡素化できますが、アプリ更新クライアントにはより狭い要件があることが多いです。つまり、この更新を承認したパブリッシャーキーだけを信頼することです。アプリバイナリに公開鍵、または承認済みキーの小さなセットを直接埋め込むことは、ピンニングの一種です。外部の認証局への依存を減らせますが、バイナリが代替キーをすでに信頼している必要があるため、ローテーションの問題が生じます。
署名済みエンベロープを完全な状態に保ちます。リリースメタデータでは、アーティファクト、そのダイジェスト、対象チャネル、キー識別子を特定する必要があります。これをCapacitorに組み込むチームは、的を絞った Capacitorアプリ向けのトークン署名チェックリスト を使って、リリース前にキーのスコープ、保管、検証境界を確認できます。
モバイル・Web システムでの実践的な活用
署名検証はモバイル製品の複数の層に現れ、各層は異なる問いに答えます。App Storeの署名は、OSがインストール可能なパッケージを承認されたパブリッシャー由来かどうか判断するのに役立ちます。署名済みOTAバンドルは、JavaScriptとアセットのペイロードが、アップデータが信頼するリリース元から来たかどうかを答えます。JWT署名は、APIがトークンが想定されたIDサービスから発行されたことを検証するのに役立ちます。
これらのレイヤーを混同すると、ギャップが生じます。IPAの署名が有効でも、後から配信されるWeb bundleが自動的に認証されるわけではありません。JWTが有効でも、アップデートパッケージが安全であることを証明するわけではありません。TLS接続はトランスポートを保護しますが、CDN、プロキシ、キャッシュ、ビルドシステムが改ざんの発生源になった場合、アーティファクト署名の代わりにはなりません。
| コンテキスト | 署名メカニズム | 防止できる失敗モード | よくあるギャップ |
|---|---|---|---|
| App Storeパッケージ | プラットフォームの code 署名とプラットフォーム審査による制御 | 再署名された、または未承認のインストール可能パッケージ | チームはストアの署名がインストール後のWebアセットまでカバーしていると想定 |
| Web bundle および Service Worker | 署名付きエクスチェンジまたは SRI 形式の整合性参照 | 汚染されたCDNレスポンスまたは改ざんされたアセット | エントリファイルのみがチェックされ、インポートされたアセットは検証されません |
| API トークン | 信頼できる公開鍵(多くの場合、JWKSエンドポイント経由で取得)でJWT署名を検証 | 偽造または改ざんされたトークン | サーバーは署名を検証するが、発行者、オーディエンス、有効期限、トークンの目的を無視する |
| オーバーザエア更新 | デバイス上で検証される、分離型または埋め込み型のバンドル署名 | 中間者攻撃または改ざんされた更新の注入 | クライアントが決定を強制する前に、コンテンツをダウンロード、キャッシュ、または展開する |
Webアセットの場合、サブリソース完全性(SRI)はブラウザが参照リソースとして受け入れるものを制限できますが、動的インポート、サービスワーカーのキャッシュ、または攻撃者が選択したファイルを指す更新マニフェストの問題を自動的に解決するものではありません。実装では、完全なアーティファクトセットを定義し、実行されるバイトを検証する必要があります。
JWTデプロイメントは別の形で失敗します。エンジニアは正しい公開鍵を公開しても、誤った発行者やオーディエンスを持つトークンを受理したり、トークンヘッダーで指定されたアルゴリズムの選択を信頼したりすることがあります。暗号署名が有効であっても、認可の決定が誤っている可能性があります。
頻繁なリリースと顧客向けモバイル体験に依存するプロダクトでは、運用コンテキストも重要です。評価中のチームは 2026年に向けたリテールアプリのエンゲージメント戦略 アップデートの完全性は、迅速な実験の前提条件として扱うべきです。高速なロールアウトが有用なのは、リリースチャネル、アーティファクト、受信者のすべてが同じ認可判断に紐付いている場合だけです。
完全な検証フローの構築
本番環境のアップデーターでは、検証をインストール付近で走るコールバックではなく、ゲートとして扱うべきです。安全なシーケンスは決定論的です:
- 認証済みトランスポート経由でマニフェストと署名を取得する。
- マニフェストの構造、バージョンポリシー、チャネル、有効期限、およびアーティファクトの識別情報を検証する。
- マニフェストで指定された正確なbundleをダウンロードする。
- bundleのダイジェストをローカルで計算する。
- 信頼できるEd25519またはRSA-PSSの公開鍵を使用して署名を検証する。
- 検証済みのアーティファクトを隔離された場所に保存する。
- アトミックに適用し、ロールバックパスを保持する。

マニフェストは、判断に影響するすべての値をバインドする必要があります。最低限、bundle digest、version、channel、platform、key identifier が対象です。検証後にダウンローダーが URL、ファイル名、channel を差し替えられないようにしてください。検証器は不変のバイト列と不変のメタデータを受け取り、単一の accept または reject 結果を返す必要があります。
簡略化された TypeScript の型は次のようになります。
type UpdateManifest = {
version: string
channel: string
platform: string
sha256: string
signature: string
keyId: string
}
async function verifyBundle(
bundle: Uint8Array,
manifest: UpdateManifest,
trustedKeys: Map<string, Uint8Array>
): Promise<boolean> {
const publicKey = trustedKeys.get(manifest.keyId)
if (!publicKey) return false
const digest = await sha256(bundle)
if (!constantTimeEqual(digest, hexToBytes(manifest.sha256))) {
return false
}
try {
return await ed25519Verify(
base64ToBytes(manifest.signature),
digest,
publicKey
)
} catch {
return false
}
}
この例は意図的に厳格にしています。不正な形式の base64、不明な key identifier、digest の不一致、署名の失敗はいずれも reject される必要があります。ダウンロード中のタイムアウトは、前回の不完全なファイルを適用する理由にはなりません。不完全なアーティファクトは削除し、最後に正常だったバージョンを保持して、上限を設けたポリシーで再試行してください。
競合状態とロールバックミスの防止
一時パスにダウンロードします。ファイルをクローズしてフラッシュし、内容全体を検証してから、バージョン管理された検証済みストアにリネームします。アクティベーションステップでは、その検証済みパスのみを参照すべきです。Capacitor や Electron 環境では、非同期のダウンロード完了イベントが検証 Promise とは独立してアクティベーションをトリガーしないようにしてください。更新ステートマシンは 1 つだけにして、次のような遷移を管理すべきです。 downloading, verified, pending, active, rejectedや rolled_back.
機密性の高いバイト列の比較には、攻撃者が繰り返し検証動作を観測できる場合特に、一定時間比較が適切です。運用上より重要なのは、以前の試行でバージョンが利用可能とマークされたからといって、バンドルを受け入れるショートカットを公開しないことです。利用可能性と真正性は別々の状態です。
この Capacitor アップデートの整合性チェック ハッシュ検証とアクティベーションロジックを明確に分離するための有用な実装リファレンスです。切り詰められたファイル、不正な署名、不明なキー、古いマニフェスト、重複バージョン、アクティベーション中のプロセス終了など、失敗分岐を意図的にテストしてください。
自動署名検証に関する研究は、他の分野で閾値と参照データがなぜ重要であるかを示しています。1994年のオンライン研究では 22の機能をテストし、最良の 10を選択し、 99.5% 本物の署名を正しく分類し、 86% の偽造をユークリッド距離法で拒否したと報告しました。これは 公開された統計手法の研究に基づいています。ソフトウェア更新の検証は生体認証ではなく決定論的ですが、教訓は依然として関連性があります:入力と決定境界を正確に定義してください。
多くのチームが見落としがちな鍵管理の課題
最初の署名鍵は簡単です。2番目の鍵こそが、アーキテクチャの真価を問われます。
チームは鍵ペアを生成し、公開鍵をアプリに配置して、1日で最初のバンドルに署名できます。しかし数ヶ月後、エンジニアが退職しラップトップへのアクセス権を保持したまま去ったり、CIシークレットがビルドログに出力されたり、署名ジョブを別のランナーに移行する必要が生じたりします。その時点で「鍵をローテーションするだけ」は、チェックインしておらず代替鍵を認識する手段のないデバイスを放棄することを意味する可能性があります。

最初のリリース前に、設計で考慮すべき3つの問題があります:
- 行き止まりのないローテーション: 新しい鍵を必須にする前に、その鍵への信頼を配布します。署名された鍵移行レコードにより、既存の信頼済み鍵が次の鍵を承認でき、定義された移行期間中はアプリが旧鍵を受け入れ続けることができます。
- 前提のない失効処理: アプリにピン留めされた鍵は、OCSPやCRL形式の失効を自動的に提供しません。クライアントには、署名された拒否リスト、最低限の許容鍵バージョン、またはネットワークが利用不可でも安全に機能するサーバー制御ポリシーが必要です。
- 署名アクセスの制限: CI ランナーは、再利用可能な秘密鍵を平文の環境変数として受け取るのではなく、Vault や HSM に署名操作をリクエストすべきです。ログではコマンド出力を秘匿化し、信頼できないブランチからのプルリクエストが本番の署名用認証情報に到達しないようにする必要があります。
運用の実態: 鍵のローテーションはアップデートの問題です。アップデートの仕組みが信頼の変更を安全に配信できなければ、鍵が侵害されてもクリーンに復旧できません。
鍵の階層化は被害範囲(blast radius)を抑えます。ルート権限がリリース用の鍵を承認し、開発、ステージング、本番の各チャネルは個別の鍵で署名します。閾値署名(Threshold signing)を使えば、機密性の高い本番リリースに複数の承認者を要求できるため、1つの認証情報が盗まれても有効なアップデートが作成されるのを防げます。これらの制御はプロセスとレイテンシを増やすため、アップデートの影響度とチャネルの機密性に応じて適用すべきです。
モバイルアップデートでは、Trust on first use(初回使用時の信頼)は特に脆弱です。最初の鍵がバンドルと同じチャネル経由で届く場合、そのチャネルを制御する攻撃者が両方を置き換えられます。最初のトラストアンカーは、アプリのバイナリ、プラットフォームで保護された設定、または別の独立して認証された経路で届ける必要があります。
Capacitor チーム向けに、Capgo のドキュメント化されたアプローチには、公開鍵ピンニングと鍵ローテーションのサポートが含まれています。その セキュアな OTA アップデートのための鍵管理ガイダンスは、 鍵の生成を一度限りのセットアップとして扱うのではなく、そのライフサイクルを計画するための実用的なリファレンスを提供します。
CI/CDとモニタリングへの検証の統合
署名はリリース処理の一部であるべきです。パイプラインが先にバンドルを公開し、後から別の手動ステップで署名を付けるべきではありません。イミュータブルな成果物をビルドし、ダイジェストを計算し、正確なバイト列に署名し、クリーンな検証ステップで署名を検証し、バンドルとメタデータを1つのリリース単位として公開します。
実用的なパイプラインは次の成果物を生成します:
- イミュータブルなバンドル: クライアントがダウンロードするファイルであり、CDNが再パッケージするディレクトリではありません。
- マニフェスト: バージョン、チャネル、プラットフォーム、ダイジェスト、キーID、ロールアウトポリシー。
- 分離署名: 正規化されたマニフェストデータ、または正確に定義されたダイジェスト表現に対する署名。
- 検証結果: その環境で期待される公開鍵に対して署名が検証されることを示す、機械可読なチェック。
GitHub Actions と GitLab CI は、構文が異なるものの、同じパターンを強制できます。署名ジョブは、プライベート署名サービスが利用できない場合、署名が不正な形式の場合、または新しくダウンロードしたテストコピーが検証に通らない場合に失敗すべきです。デプロイジョブは、単にビルド成功に依存するのではなく、その結果に依存すべきです。
パスに署名した後、後続のジョブでその内容が変更されないようにしてください。署名後はアーティファクトをロックし、公開前にダイジェストを比較し、公開するマニフェストがリリース記録から再現可能であることを確認しましょう。これにより、CIジョブが署名した圧縮アーカイブと、配信レイヤーが提供する再圧縮・再生成されたファイルが食い違うという、驚くほどよくある統合ギャップを検知できます。
オブザーバビリティはデバイス上で行うべきです
署名ジョブの成功は、パイプラインが有効な署名を作成したことを証明するに過ぎません。デバイスが意図したバイト列を受信したことや、アプリが意図したキーを使用したことまでは証明できません。検証結果は、アプリのバージョン、アップデートバージョン、チャネル、プラットフォーム、キー識別子、結果カテゴリ、およびプライバシーに配慮したリリース相関IDとともに記録してください。bundle、token、プライベートメタデータ、ユーザーコンテンツをログに出力しないでください。
有用なダッシュボードでは、以下を切り分けて表示します:
- 署名失敗とダウンロード失敗、
- ダイジェストの不一致とマニフェストの形式エラー、
- 不明なキーとポリシーによる拒否、
- アプリのバージョン、リージョン、チャネル、リリース経過期間ごとの失敗。
ダイジェストの不一致が急増した場合、キャッシュの破損、配信パスの改ざん、またはアーティファクトの公開エラーの可能性があります。不明なキーのイベントは、キーローテーションの未完了や不正なリリース試行を示しているかもしれません。検証テレメトリ単体で攻撃者を特定することはできませんが、インシデント対応チームに対して、調査すべきタイムラインと対象範囲を提供します。
この Capacitor OTAアップデートのためのCI/CDセキュリティガイド これにより、チームは署名、検証、デプロイのゲートを1つのワークフローにまとめられます。重要な設計判断はオーナーシップです。セキュリティチームは、検証が実行されたかどうかをエンジニアリングに確認する必要があってはなりません。リリース記録とクライアントテレメトリが、その問いに直接答えるべきです。
セキュリティのベストプラクティスとよくある落とし穴
code を実行できるすべてのアップデートパスで、署名検証は必須要件であるべきです。アップデーターは展開、インストール、アクティベーションの前に検証しなければならず、署名、ダイジェスト、鍵、ポリシーのチェックが利用できない場合は fail closed でなければなりません。
すぐに使えるレビューチェックリストとして活用してください:
- 秘密鍵の保護: 署名用マテリアルは HSM またはマネージド Vault に保管してください。ソース管理へのコミット、モバイルバイナリへの埋め込み、CI ログへの出力は絶対に避けてください。
- 信頼できる公開鍵のピン留め: 最初のトラストアンカーは、アップデートペイロードの外側に保存してください。複数の鍵をサポートする場合は、それぞれの用途と移行ルールを定義してください。
- リリース全体の紐付け: 正確な bundle、channel、platform、および適用ポリシーを識別する正規化メタデータに署名してください。
- すべての検証エラーを拒否する: タイムアウト、不正な形式の署名、未知の鍵、マニフェスト欠落は、以前の未検証の結果で続行してよい理由にはなりません。
- 侵害復旧をテスト: staging環境でキーローテーション、失効キーの処理、ロールバック、中断されたダウンロード、古いデバイスを検証します。
- 検証器の変更をレビュー: 暗号ライブラリ、正規化、パース、フォールバック動作、デバッグ設定については、セキュリティに特化したcodeレビューを必須にします。
- デバッグ動作を分離: デフォルトフラグや環境設定のミスで、開発用バイパスが本番ビルドにパッケージされることがないようにしなければなりません。
署名は、鮮度、受信者へのバインディング、リプレイ耐性、ペイロードがプロバイダーの現在の状態と互換性があることを証明するものではありません。Webhookセキュリティのガイダンスではこの区別が明確に示されており、最近の脆弱性報告では、不正な形式やnullの署名、正規性の問題が、限定的な検証チェックを依然として弱体化させ得ることが示されています。周辺の認可ルールを設計する際は、Webhookセキュリティのギャップ分析を参照してください。
閾値の選択は、生体認証システムにおける関連する教訓を生みます。ある研究では 62個のパラメトリック特徴量 で 102人からの1,232件の署名 書き手依存の閾値が報告されており、その値は 誤拒否率 2.8% と 誤受容率 1.6%、一方で別の手法では 誤拒否率 2.68% と 誤受容率 1.99%です。これは、 閾値選択に関する研究。アプリケーションは異なりますが、動作原理は同じです。検証者の決定ポリシーは、暗号プリミティブと同じくらい重要になります。
署名付きの Live Update bundle、デバイス上での検証、ロールアウト制御、ロールバック保護、そして配信のオブザーバビリティを単一のシステムで実現したい Capacitor や Electron チームにとって、Capgo は有力な実装オプションとなります。詳細はこちら: Capgo そのアップデートワークフローが自社のキー管理、CI/CD、モニタリング要件に適合するかを評価するため。