Skip to content

再署名とクラッシュ防止

データ移行後にアプリがクラッシュする理由

macOS のコード署名メカニズム(codesign)は、ファイルパス構造を含むアプリケーションパッケージの完全性を検証します。AppPorts がアプリのデータディレクトリを外部ストレージに移行し、シンボリックリンクに置き換えると、署名シールが壊れ、以下の問題が発生します:

  • Gatekeeper ブロック: codesign --verify --deep --strict が署名失敗を検出し、システムが「破損」または「身元不明の開発者からのアプリ」というダイアログを表示し、アプリの起動をブロック
  • Keychain アクセスの中断: Keychain アクセスグループに依存するアプリは、署名アイデンティティの変更により保存された認証情報を読み取れなくなる
  • Entitlements 失敗: 一部のアプリ Entitlements は署名アイデンティティに紐づいており、署名変更後に Entitlements の不整合が発生

高リスクアプリタイプ

アプリタイプリスクレベル理由
Sparkle 自動更新アプリアップデーターがアプリを削除または置換し、シンボリックリンクを破損する可能性がある
Electron 自動更新アプリelectron-updater も外部ストレージ上のアプリに干渉する可能性がある
Keychain 依存アプリAd-hoc 署名により署名アイデンティティが変更され、Keychain アクセスグループが失敗する
Mac App Store アプリSIP 保護;再署名不可
ネイティブ自動更新アプリ(Chrome、Edge)自動更新により外部コピーが置換され、ローカルエントリが無効化される可能性がある
iOS アプリ(Mac 版)Stub Portal または全体シンボリックリンクを使用;署名の問題が少ない

高リスクデータディレクトリタイプ

データタイプリスクレベル理由
~/Library/Application Support/アプリがファイルロック、SQLite WAL ログ、または拡張属性を使用している可能性があり、シンボリックリンクをまたぐと異常動作する場合がある
~/Library/Group Containers/同じ Team 下の複数のアプリで共有されており、シンボリックリンクが他のアプリに干渉する可能性がある
~/Library/Preferences/低~中cfprefsd が plist ファイルをキャッシュしており、シンボリックリンクにより古いデータを読み取る可能性がある
~/Library/Caches/キャッシュは再構築可能;ほとんどのアプリはキャッシュの不在を適切に処理する

再署名メカニズム

Ad-hoc 署名

AppPorts は移行後のアプリ署名を修正するためにAd-hoc 署名(証明書なしローカル署名)を使用します。実行コマンド:

bash
codesign --force --deep --sign - <app path>

ここで - は Ad-hoc 署名(開発者証明書なし)を示します。

署名フロー

mermaid
flowchart TD
    A[再署名開始] --> B[元の署名アイデンティティをバックアップ]
    B --> C{アプリはロックされていますか?}
    C -->|はい| D[uchg フラグを一時的に解除]
    C -->|いいえ| E{アプリは書き込み可能ですか?}
    D --> E
    E =>|書き込み不可 & root 所有| F[管理者権限で所有権変更を試行]
    E =>|書き込み可能| G[拡張属性をクリーンアップ]
    F --> G
    F -->|失敗 & MAS アプリ| H[署名をスキップ - SIP 保護]
    G --> I[バンドルルートディレクトリの雑多なファイルをクリーンアップ]
    I --> J{Contents はシンボリックリンクですか?}
    J =>|はい| K[一時的に実ディレクトリコピーに置換]
    J =>|いいえ| L[deep 署名を実行]
    K --> L
    L =>|失敗| M[shallow 署名にフォールバック]
    L =>|成功| N{Contents が一時置換されましたか?}
    M --> N
    N =>|はい| O[シンボリックリンクを復元]
    N =>|いいえ| P[uchg フラグを再ロック]
    O --> P
    P => Q[署名完了]

重要なステップ

  1. 元の署名アイデンティティのバックアップ: 署名前に、アプリの現在の署名アイデンティティを読み取り(codesign -dvvAuthority= 行を解析)、~/Library/Application Support/AppPorts/signature-backups/<BundleID>.plist に保存

  2. 拡張属性のクリーンアップ: xattr -cr を実行してリソースフォーク、Finder 情報などを削除し、署名中の「detritus not allowed」エラーを回避

  3. バンドルルートディレクトリのクリーンアップ: .DS_Store__MACOSX.git.svn などの雑多なファイルを削除

  4. シンボリックリンク Contents の処理: Contents/ がシンボリックリンクの場合(Deep Contents Wrapper 戦略)、一時的に実ディレクトリコピーに置換し、署名後にシンボリックリンクを復元

  5. deep 署名 → shallow 署名のフォールバック: --deep 署名(すべてのネストされたコンポーネントをカバー)を優先;権限やリソースフォークの問題で失敗した場合、--deep なしの shallow 署名にフォールバック

  6. リトライメカニズム: codesign が「internal error」を生成するか SIGKILL で終了した場合、最大2回リトライ

署名バックアップと復元

バックアップ

バックアップファイルは ~/Library/Application Support/AppPorts/signature-backups/ ディレクトリに BundleID.plist という名前で保存されます:

フィールド説明
bundleIdentifierアプリの Bundle ID
signingIdentity元の署名アイデンティティ(例:Developer ID Application: ... または ad-hoc
originalPath元のアプリパス
backupDateバックアップのタイムスタンプ

バックアップは以下のタイミングでトリガーされます:

  • データディレクトリ移行前(自動再署名が有効な場合)
  • すべての署名操作前(冪等;既存のバックアップは上書きされません)

復元

署名を復元する際、AppPorts はバックアップされた署名アイデンティティに基づいて異なる戦略を実行します:

バックアップされた署名アイデンティティ復元動作
ad-hoc または空codesign --remove-signature を実行して署名を削除;バックアップを削除
有効な開発者証明書アイデンティティKeychain に証明書が存在するか確認。存在する場合、元のアイデンティティで再署名
有効な開発者証明書アイデンティティだがこのマシンに証明書がないAd-hoc 署名にフォールバック;元の署名は完全には復元できない

復元失敗のシナリオ

以下のシナリオでは、署名の復元が失敗または不完全になります:

シナリオ結果
バックアップ plist ファイルが存在しないnoBackupFound エラーをスロー;復元不可
元の開発者証明書がローカル Keychain にないAd-hoc 署名にフォールバック。アプリは起動可能だが、Keychain アクセスグループと一部の Entitlements が失敗する可能性がある
Mac App Store アプリ(SIP 保護)サイレントにスキップ。SIP がシステムアプリ署名へのあらゆる変更を防止
アプリディレクトリが書き込み不可 & root 所有管理者権限で所有権変更を試行。ユーザーが認証プロンプトをキャンセルすると失敗
Contents シンボリックリンクターゲットが消失一時置換ステップで copyItem が失敗;署名実行不可
ユーザーが管理者認証をキャンセルcodesignFailed("User cancelled authorization") をスロー
deep 署名と shallow 署名の両方が失敗エラーが上位に伝播;署名操作失敗

⚠️ 開発者証明書の喪失について

最も一般的な実際の復元失敗シナリオは:元のアプリがサードパーティ開発者(例:Developer ID Application: Google LLC)によって署名されているが、現在のマシンの Keychain に対応する秘密鍵がない場合です。この場合、復元操作は Ad-hoc 署名のみを生成でき、元の署名アイデンティティは完全には復元できません。Keychain アクセスグループやエンタープライズ設定プロファイルに特定の署名アイデンティティに依存するアプリでは、機能異常が発生する可能性があります。

最近更新