公証(notarization)に提出するmacOSアプリは、Hardened Runtimeを有効にして署名する必要がある。無効のまま提出すると、公証の結果がInvalidになる。

XcodeのSigning & CapabilitiesでHardened Runtimeを追加すると、ビルド設定のENABLE_HARDENED_RUNTIMEがYESになる。署名後のアプリにcodesign -dvを実行すると、有効になっているかを確認できる。実行例はXcode 27.0、macOS 27.0.1のもの。

$ codesign -dv --verbose=4 [アプリのパス]
...
CodeDirectory v=20500 size=776 flags=0x10000(runtime) hashes=13+7 location=embedded
...
Runtime Version=27.0.0

Hardened Runtimeとは

Hardened Runtimeは、実行中のアプリを攻撃から守るためのmacOSの仕組みである。アプリのコードの改ざんや、プロセスへのコードの注入などを、既定で禁止する。

Xcodeでは、禁止している動作の一部を、例外として個別に許可できる。例外には、JITコンパイルされたコードの実行、署名されていない実行可能メモリの利用、DYLD環境変数の利用、デバッガのアタッチなどがある。アプリが必要とする例外だけを許可し、不要な例外は無効のままにする。

Hardened Runtimeを追加する

Xcodeでプロジェクトを開き、次の手順で追加する。

  1. プロジェクトを選択し、アプリのターゲットを選ぶ
  2. 「Signing & Capabilities」タブを開く

Hardened Runtimeを追加する前のSigning & Capabilities

追加前の画面には、Signingのセクションだけが表示される。

  1. 「+ Capability」を押し、検索欄にHardenedと入力する
  2. 候補の「Hardened Runtime」をダブルクリックする

Capabilityの検索ポップアップ

ポップアップの説明には、公証を受けるアプリにこのcapabilityが必要だと書かれている。

追加すると、「Hardened Runtime」のセクションが表示される。

Hardened Runtimeを追加した後のSigning & Capabilities

「Runtime Exceptions」と「Resource Access」には、許可できる動作のチェックボックスが並ぶ。すべて未選択のまま使う。

ビルド設定はENABLE_HARDENED_RUNTIMEになる

capabilityの追加で、project.pbxprojのビルド設定にENABLE_HARDENED_RUNTIME = YESが書き込まれる。あわせて、すべての例外がNOとして明示的に書き込まれる。

ENABLE_HARDENED_RUNTIME = YES;
RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO;
RUNTIME_EXCEPTION_ALLOW_JIT = NO;
RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO;
RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO;
RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO;
RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = NO;
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
ENABLE_RESOURCE_ACCESS_CAMERA = NO;
...

Debug用とRelease用の両方の設定に、同じ内容が追加される。project.pbxprojにENABLE_HARDENED_RUNTIME = YESだけを追加しても、Hardened Runtimeは有効になる。

署名後のアプリで有効になっているか確認する

Hardened Runtimeが有効かどうかは、署名されたアプリのcodesign -dvで確認できる。署名のフラグにruntimeが含まれていれば、有効である。

追加前のReleaseビルドでは、フラグが0x0(none)である。

$ xcodebuild -project [プロジェクト名].xcodeproj -scheme [スキーム名] \
    -configuration Release -derivedDataPath build build
$ codesign -dv --verbose=4 build/Build/Products/Release/[アプリ名].app 2>&1 | grep -E 'flags|Runtime'
CodeDirectory v=20400 size=768 flags=0x0(none) hashes=13+7 location=embedded

追加後は、フラグが0x10000(runtime)になり、Runtime Versionも表示される。

$ codesign -dv --verbose=4 build/Build/Products/Release/[アプリ名].app 2>&1 | grep -E 'flags|Runtime'
CodeDirectory v=20500 size=776 flags=0x10000(runtime) hashes=13+7 location=embedded
Runtime Version=27.0.0

codesignの出力の読み方は、署名の検証の記事で詳しく解説する。

参考: 【codesign】署名の内容と整合性を検証する

有効にせずに公証を提出するとInvalidになる

Hardened Runtimeを有効にせず署名したアプリを提出すると、公証の結果がInvalidになる。公証のログには、次のエラーが記録される。

The executable does not have the hardened runtime enabled.

参考: 【notarytool】公証に失敗した理由をログから調べる

App Sandboxとの違い

Hardened RuntimeとApp Sandboxは、別の仕組みである。Hardened Runtimeは、アプリのコードや実行時の動作を守る。App Sandboxは、アプリがアクセスできるファイルやネットワークなどのリソースを制限する。

Mac App Store外で配布するアプリに、App Sandboxは不要である。App SandboxをオフにしてHardened Runtimeだけを有効にしたアプリでも、公証はAcceptedになる。