macOS App Sandboxing and Entitlements Explained
How the App Sandbox confines what an application can access by default, and how entitlements grant it specific, narrow exceptions.
Every application distributed through the Mac App Store, and a growing share of applications distributed outside it, runs inside the App Sandbox — a kernel-enforced confinement that restricts a process to a minimal default set of resources, regardless of what the user running it is otherwise permitted to do. It’s the same “constrain even a fully-privileged actor” philosophy as System Integrity Protection, applied to individual applications instead of the OS as a whole.
Default-deny: the sandbox’s starting point
Without any explicit entitlements, a sandboxed app can’t read files outside its own container, can’t access the network, can’t see other processes, and can’t use most system services — a deliberately restrictive starting point that assumes an app should be able to justify every capability it needs, rather than inheriting the full permissions of the user running it.
codesign -d --entitlements :- /Applications/MyApp.app
The container: an app’s private sandboxed home
A sandboxed app’s own files live in a dedicated container directory, not scattered across the user’s home folder the way an unsandboxed app might behave:
~/Library/Containers/com.example.myapp/Data/
Even the app’s own preferences and documents are redirected into this container transparently — NSUserDefaults and standard file dialogs work normally from the app’s point of view, but the actual files land inside the container rather than directly in ~/Library/Preferences or ~/Documents.
Entitlements: explicit, narrow exceptions
An entitlement is a specific capability an app declares it needs, embedded in its code signature, and granted (or not) at install/launch time. Entitlements are deliberately granular — network client access and network server access are separate entitlements, as are read-only versus read-write file access to specific locations:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>
An app requesting camera access, microphone access, or Bluetooth needs the corresponding entitlement declared explicitly — and, at runtime, the user is separately prompted by TCC (Transparency, Consent, and Control) to actually grant it, meaning the entitlement is necessary but not sufficient on its own.
Security-scoped bookmarks: persistent access outside the container
A sandboxed app that needs continued access to a file the user explicitly selected (via an Open dialog) beyond the current launch — without re-prompting every time — uses a security-scoped bookmark, a serialized, tamper-resistant reference the system will honor for exactly that file, without granting broader access to its containing folder:
let bookmark = try fileURL.bookmarkData(
options: .withSecurityScope,
includingResourceValuesForKeys: nil,
relativeTo: nil
)
var isStale = false
let restoredURL = try URL(
resolvingBookmarkData: bookmark,
options: .withSecurityScope,
relativeTo: nil,
bookmarkDataIsStale: &isStale
)
restoredURL.startAccessingSecurityScopedResource()
This is the mechanism behind “this app remembers the folder I gave it access to last time” without the app having been granted broad filesystem access to achieve that — the bookmark is scoped to exactly the file or folder the user picked.
Diagnosing a sandbox violation
When a sandboxed app is denied something it expected to be able to do, the unified logging system records the specific denial, identifying exactly which entitlement (or lack of one) caused it:
log stream --predicate 'subsystem == "com.apple.sandbox"'
Sandboxing versus SIP versus code signing
These three mechanisms are frequently confused because they’re all part of the same broader security model, but each answers a different question: code signing asks “is this binary what it claims to be, unmodified”; SIP asks “is this operation trying to modify a part of the OS that nothing should modify”; the App Sandbox asks “does this specific app have permission to do this specific thing, right now.” A perfectly validly signed, non-SIP-related app can still be denied file access by the sandbox if it lacks the right entitlement — the three checks are independent layers, not redundant versions of the same check.
Why sandboxing pays off even for non-malicious apps
The App Sandbox’s value isn’t limited to defending against deliberately malicious software — it also limits the blast radius of an ordinary security vulnerability in an otherwise legitimate app. A sandboxed PDF reader with a memory-corruption bug in its parsing code is still confined to its container and its declared entitlements even if that bug is successfully exploited, which is precisely the scenario sandboxing is designed to contain: not “will this app try to do something bad,” but “if this app is compromised by someone else’s exploit, how much damage can it actually do.”
The technical foundation: Seatbelt and the TrustedBSD MAC framework
The App Sandbox is built on top of Seatbelt, Apple’s own kernel-level mandatory access control implementation, itself layered on the TrustedBSD MAC (Mandatory Access Control) framework — a pluggable kernel security-policy architecture originally developed for FreeBSD and adopted into Darwin. This lineage is exactly why the App Sandbox’s underlying philosophy — a policy engine that can deny an operation regardless of standard Unix permission checks having already succeeded — mirrors SELinux’s role on Linux so closely despite the two systems having entirely separate implementations: both are mandatory access control layers sitting on top of, not replacing, the traditional discretionary Unix permission model, evaluating a specific policy (a sandbox profile, an SELinux policy) independently of whatever the underlying file permissions already allow.
Temporary exceptions for narrowly-scoped legacy needs
Not every legitimate access pattern fits neatly into the standard entitlement categories — for cases needing broader, less common access that doesn’t map to an existing granular entitlement, Apple provides temporary exceptions, explicitly discouraged for new development and subject to extra App Review scrutiny when used for App Store submissions:
<key>com.apple.security.temporary-exception.files.absolute-path.read-write</key>
<array>
<string>/private/var/somelegacydata/</string>
</array>
These exist specifically as a pressure-relief valve for apps with genuinely unusual, hard-to-avoid access patterns during the sandbox’s early adoption years, not as a general-purpose escape hatch — Apple’s own guidance actively steers developers toward narrower, purpose-built entitlements wherever one exists, reserving temporary exceptions for the residual cases nothing else covers.
Sandbox profiles: what an entitlements plist actually compiles down to
Behind the scenes, an app’s declared entitlements get compiled into a sandbox profile — a Scheme-like declarative policy document the kernel’s Seatbelt implementation actually evaluates at runtime for every relevant system call the sandboxed process attempts. Apple’s own system services use hand-written sandbox profiles directly (visible, for those exploring the mechanism further, under /usr/share/sandbox/ on many macOS versions), while third-party App Sandbox entitlements are translated automatically into the equivalent profile syntax by the system — meaning the human-readable entitlements plist a developer actually writes is a higher-level, more restricted interface onto the same underlying profile mechanism Apple’s own most privileged system daemons are also governed by, just with a much narrower set of capabilities exposed to third-party developers than Apple grants its own first-party code.
Related:
- XPC Services: How macOS Processes Talk to Each Other Securely
- Understanding launchd: macOS’s Init System and Service Manager
Sources: