skip to content

What is an entitlement on macOS, and how do the App Sandbox and the Hardened Runtime use entitlements differently?

level: middleimportance: should knowfreq 46%

answer

  1. signed claims, not editable config
  2. one grants, the other relaxes
  3. default-deny versus default-protect
  4. container redirects the home directory
  5. App Store versus notarization requirement

basics

~20 s

An entitlement is a key/value claim embedded in a program's code signature that the kernel and system services consult at runtime. The App Sandbox uses entitlements to widen a default-deny confinement; the Hardened Runtime uses them to relax protections it otherwise switches on.

solid answer

~40 s

Entitlements are signed metadata: a property list baked into the code signature at build time, so it cannot be edited afterwards without invalidating the signature. They are how macOS answers "is this specific program allowed to do this specific thing". The two systems that consume them work in opposite directions. **App Sandbox** — turned on by the `com.apple.security.app-sandbox` entitlement — confines the process to its own container and denies almost everything else; each further entitlement, such as `com.apple.security.network.client` or `com.apple.security.files.user-selected.read-write`, opens one hole. **Hardened Runtime** is the opposite posture: it applies a fixed set of protections (library validation, no arbitrary code injection, no unsigned executable memory), and entitlements like `com.apple.security.cs.allow-jit` selectively relax them for programs that genuinely need it. Notarization requires the Hardened Runtime; the sandbox is required for the App Store.

code

bash · 11 lines
bash
# Read the signed entitlements out of an installed app
codesign -d --entitlements - /Applications/Example.app

# Sign with the Hardened Runtime and an entitlements file
codesign --force --options runtime \
  --entitlements Example.entitlements \
  --sign "Developer ID Application: Example Ltd" \
  Example.app

# Verify the seal afterwards
codesign --verify --strict --verbose=2 Example.app

go deeper

for a junior

Know that macOS apps declare permissions as entitlements inside their code signature, that a sandboxed app is restricted to its own container, and that these are set at build time rather than configured later.

for a middle

Explain the opposite directions clearly: sandbox entitlements open holes in a default-deny confinement, Hardened Runtime entitlements remove protections that are otherwise on, and the signature is what makes either trustworthy.

for a senior

Show judgment about which entitlements a build actually needs, explain the user-selected-file flow so an app never needs blanket filesystem access, and treat disabling library validation as a real weakening rather than a build fix.

for a principal

Own the posture across a portfolio: what the team is allowed to claim, how entitlement changes are reviewed like security changes, and how signing identities and restricted entitlements are managed so no single build can quietly widen its own privileges.

## Signed claims, not configuration An entitlement is a key/value pair in a property list that is embedded **inside the code signature**. That placement is the whole point. A configuration file can be edited by whoever has write access; entitlements cannot, because changing them changes the signed bytes and breaks the seal, at which point Gatekeeper and the kernel stop trusting the binary at all. Entitlements are therefore statements the developer made at build time, attested by their signing identity, and the system can rely on them at runtime. You can read them out of any signed binary: ```sh codesign -d --entitlements - /Applications/Example.app ``` Some entitlements are freely usable by any developer; others are *restricted* and only take effect when the signing certificate is authorised by Apple to carry them. That two-tier design is why an app cannot simply claim a powerful capability for itself. ## App Sandbox: deny by default, widen deliberately Adding `com.apple.security.app-sandbox` opts the application into confinement. Once inside: - The app gets a **container** — a private directory under `~/Library/Containers/<bundle-id>/Data` — and its view of `$HOME` is redirected there. Code that writes to `~/Library/Preferences` lands inside the container, invisible to the rest of the system. - Network access, file access outside the container, camera, printing, and hardware access are all denied. - Each capability must be requested individually: `com.apple.security.network.client` for outgoing connections, `com.apple.security.network.server` for listening, `com.apple.security.files.user-selected.read-write` for files the user picks. That last one deserves a note, because it is the mechanism people find surprising. A sandboxed app cannot open `/Users/me/report.pdf` by path. But when the user chooses that file in an open panel, the system — through a separate, out-of-process file-picking service often called the powerbox — hands the app an extension to its sandbox for that file. The user's act of choosing *is* the authorisation, and the app never needed a broad grant. Security-scoped bookmarks let the app persist that access across launches. The sandbox is mandatory for App Store distribution and optional outside it. ## Hardened Runtime: protect by default, relax deliberately The Hardened Runtime is a signing option (`codesign -o runtime`) that turns on a set of process-level protections: - **Library validation** — the process may only load libraries signed by the same team identifier or by Apple, which stops an attacker dropping a malicious dylib beside the binary. - No unsigned executable memory, so a bug that gets bytes into a page cannot simply run them. - Restrictions on debugger attachment and code injection into the process by other software. Each of these has an escape entitlement for legitimate cases: `com.apple.security.cs.allow-jit` for a language runtime that compiles code at execution time, `com.apple.security.cs.disable-library-validation` for a host that loads third-party plug-ins signed by other vendors, `com.apple.security.cs.allow-dyld-environment-variables` for tooling that depends on dynamic-linker variables. Notarization requires the Hardened Runtime, and Apple's automated checks look at which relaxations you claimed. Turning off library validation on an application that has no plug-in model is a red flag in a review — and a real weakening, because it re-opens exactly the dylib-substitution attack the setting exists to close. ## The two are orthogonal A common muddle is to treat sandbox and hardened runtime as the same switch. They are not: | | App Sandbox | Hardened Runtime | |---|---|---| | Default posture | deny nearly everything | apply a fixed protection set | | Entitlements | grant capabilities | remove protections | | Required for | App Store | notarization | | Protects | the system from the app | the app from injected code | An app can be both sandboxed and hardened, either, or neither. And neither one replaces the privacy consent layer: the user's approval for camera, microphone or their Documents folder is a separate runtime gate that applies to sandboxed and unsandboxed apps alike. ## What this means in practice When you are asked to make a macOS build "work", resist the reflex to add entitlements until it stops complaining. Every entitlement you add is a permanent, signed statement about what your program may do, visible to anyone who inspects the binary and to Apple's notary service. Claim the narrowest set that supports the feature, and be able to justify each one — that justification is precisely what an interviewer is probing for.

  • How can a sandboxed text editor open a file anywhere on disk without a broad filesystem entitlement?
    Through the user's choice. The open panel runs outside the app's process; when the user picks a file, the system extends the app's sandbox to cover exactly that item. The app never had a path-based grant. To keep access across launches it stores a security-scoped bookmark and re-acquires the extension, which keeps the grant tied to specific files the user chose.
  • When is disabling library validation legitimate?
    When the application is genuinely a host for code signed by someone else — an audio host loading third-party plug-ins, or a runtime loading vendor modules. Outside that, it is a way to silence a load failure at the cost of re-opening dylib substitution, where an attacker drops a signed-by-nobody library next to your binary. Prefer fixing the signing of what you load.
  • Does sandboxing an app remove the need for privacy consent?
    No — they are independent layers. The sandbox is a confinement the developer declares and the kernel enforces; privacy consent is the user's runtime decision about camera, microphone, screen recording and protected folders. A sandboxed app with the camera entitlement still triggers a consent prompt, and the user can revoke it later in System Settings.

saying these in an interview costs you the question

  • Treating the sandbox and the Hardened Runtime as one switch
  • Thinking entitlements can be edited after signing
  • Saying a sandboxed app can open any path the user owns
  • Disabling library validation to make a build stop failing
  • Believing entitlements replace the user's privacy consent

context