skip to content

Security Model

Modern macOS stacks several defences: SIP protects the system volume, Gatekeeper and notarization gate what may run, TCC gates access to your data, and entitlements bound each sandboxed app. Understanding the layers answers most questions about why a binary is blocked.

on this pageshow

questions

6

On macOS, an app downloaded through a web browser is blocked on first launch, but the identical file fetched with curl opens immediately. What mechanism explains the difference?

level: juniorimportance: must knowfreq 66%

answer

  1. it depends on who wrote the file
  2. an extended attribute, not a scan
  3. curl sets no metadata
  4. first-launch assessment only
  5. xattr -l shows the tag

basics

~20 s

The browser tags its download with the com.apple.quarantine extended attribute; curl does not. Gatekeeper only evaluates quarantined files, so the tagged copy is checked for a valid signature and notarization on first launch while the untagged copy simply runs.

solid answer

~40 s

Quarantine is an opt-in flag, not a property of the file's contents. Applications that download content — browsers, mail clients, chat apps — attach the `com.apple.quarantine` extended attribute to what they write. On first launch, Gatekeeper sees that attribute and performs an assessment: is the code signed, is the signature intact, and has the developer had the app notarized by Apple? If any of that fails the user gets the "cannot be opened because the developer cannot be verified" or "is damaged" dialog. `curl` is a plain HTTP client with no quarantine support, so the file it writes carries no attribute and Gatekeeper never assesses it. You can inspect the attribute with `xattr -l` and clear it with `xattr -d com.apple.quarantine`, and check what Gatekeeper thinks with `spctl --assess --verbose`.

code

bash · 12 lines
bash
# Same bytes, different provenance metadata
curl -sLO https://example.com/Example.zip
xattr -l Example.zip            # prints nothing

# A browser-downloaded copy carries the flag:
#   com.apple.quarantine: 0083;65f0a1b2;Safari;

# Ask Gatekeeper what it thinks of an app bundle
spctl --assess --verbose=4 /Applications/Example.app

# Clear the flag (only for code whose origin you trust)
xattr -d com.apple.quarantine /Applications/Example.app

go deeper

for a junior

Recall that browser downloads are tagged as quarantined and get checked on first open, that curl-fetched files are not tagged, and that the tag lives in an extended attribute you can list with xattr.

for a middle

Explain the assessment itself: signature integrity, Developer ID identity, and a notarization ticket that is either stapled into the bundle or looked up online — and that clearing the attribute skips the whole thing.

for a senior

Separate the three layers cleanly (signing, notarization, App Store review), diagnose "is damaged" as a broken seal after a post-signing edit, and recognise App Translocation as the cause of an app that cannot find its neighbouring files.

for a principal

Own distribution strategy: which builds get Developer ID plus notarization, how the notary step lands in CI without leaking credentials, and where the organisation accepts unnotarized internal tooling versus requiring the full chain.

## Quarantine is metadata, not a scan The first thing to understand is that macOS is not inspecting the bytes of the file to decide it is "from the internet". It is reading a label that the downloading application chose to attach. That label is an extended attribute named `com.apple.quarantine`, and applications opt into setting it — historically via the `LSFileQuarantineEnabled` key in their Info.plist, and automatically for sandboxed apps. ```sh $ xattr -l ~/Downloads/Example.app com.apple.quarantine: 0083;65f0a1b2;Safari;... $ xattr -l /tmp/example-from-curl.app # nothing — no attribute at all ``` That is the whole difference. Safari, Chrome, Mail and Messages set it. `curl`, `wget`, `scp`, `git clone` and a tarball extracted with `tar` do not. It is a provenance hint, deliberately cheap, and it is why "download it with curl instead" is a well-known workaround — and equally why it is a bad security habit. ## What Gatekeeper does when it sees the flag Gatekeeper is the policy engine that runs on **first launch of a quarantined executable**. Its assessment asks: 1. **Is the code signed at all, and is the signature intact?** A signature covers every byte of the bundle. If someone edited a file inside the app after signing, the seal is broken. That is what the confusing "the application is damaged and can't be opened" dialog usually means — not disk corruption, but a broken or missing signature. 2. **Who signed it?** A Developer ID certificate issued by Apple to an identified developer, an App Store signature, or nothing recognisable. 3. **Is it notarized?** Notarization is a separate step in which the developer uploads the signed build to Apple's notary service, Apple runs an automated malware scan, and returns a ticket. The developer normally *staples* that ticket into the bundle with `xcrun stapler staple`; if it is not stapled, macOS looks it up online. Only if all of that passes does the app launch quietly. Otherwise the user gets a blocking dialog. Once the user approves an app, the decision is remembered and the quarantine flag is cleared, which is why the prompt appears exactly once. ## Signing, notarization and the App Store are three different things Candidates routinely merge these: - **Code signing** proves integrity and identity. It says "this is the bundle developer X produced, unmodified". Anyone with a Developer ID certificate can sign, and signing happens on the developer's machine. - **Notarization** is Apple's automated check of an already-signed build. It is not review, no human looks at it, and it does not judge quality — it scans for malware and for signing and hardening mistakes. It requires the Hardened Runtime. - **App Store review** is a human process with its own rules, and distributes through a different channel entirely. An app distributed outside the App Store on modern macOS needs the first two. ## App Translocation, the surprising side effect When a quarantined app is launched from a location like `~/Downloads`, macOS may run it from a randomised, read-only path rather than where it sits — a feature usually called App Translocation or Gatekeeper path randomisation, added in macOS 10.12. It exists to stop an attacker shipping a legitimate app in a disk image next to a malicious library it will load relatively. The visible symptom is an app that cannot find files it expects beside itself, or that appears not to save its settings, until the user drags it to `/Applications` — which clears quarantine and stops the translocation. ## Clearing quarantine, and why not to make it a habit ```sh $ spctl --assess --verbose=4 /Applications/Example.app /Applications/Example.app: rejected source=Unnotarized Developer ID $ xattr -d com.apple.quarantine /Applications/Example.app ``` Removing the attribute makes the file look local and Gatekeeper stops assessing it. That is a reasonable move for a binary you built yourself or one whose provenance you verified another way. Doing it reflexively for anything downloaded discards the one check that stands between the user and a trojaned installer. The user-facing approval path has tightened over releases: the old "Control-click, Open" shortcut was the standard way to run an unnotarized app, and in macOS 15 Sequoia that route was removed in favour of an explicit approval in System Settings under Privacy & Security. Expect the exact clicks to keep moving; the underlying quarantine-plus-assessment model has been stable for years.

  • A user reports an app "is damaged and can't be opened". What is your first hypothesis?
    Not disk corruption — a signature problem. The bundle is quarantined and its code signature is missing, broken, or was invalidated by an edit after signing, such as a script that touched a file inside the .app. Verify with `codesign --verify --deep --strict` and `spctl --assess --verbose`, then fix the build so the app is signed and notarized rather than telling users to strip the attribute.
  • What exactly does notarization add that a Developer ID signature does not?
    The signature proves who built it and that it is unmodified; it says nothing about what the code does. Notarization submits that signed build to Apple's automated malware and hardening scan and returns a ticket, so macOS can distinguish "signed by someone" from "signed and screened by Apple". It also gives Apple a revocation lever if the developer's build later turns out malicious.
  • Why is stapling the notarization ticket worth doing if macOS can check online?
    Stapling embeds the ticket in the bundle or disk image with `xcrun stapler staple`, so first launch succeeds with no network round trip. Without it, a machine that is offline or behind a restrictive proxy may fail the lookup and block the app. Stapling makes the result deterministic and removes an Apple service from your users' launch path.

saying these in an interview costs you the question

  • Believing macOS scans the file's contents for malware
  • Saying notarization is the same as App Store review
  • Thinking the check runs on every launch, not the first
  • Assuming curl downloads are somehow trusted or verified
  • Treating "is damaged" as disk corruption rather than a bad signature

context

open as a page

On macOS, why can a process running as root still be denied when it writes to /System or /usr/bin, and what enforces that?

level: middleimportance: must knowfreq 68%

basics

~20 s

System Integrity Protection (SIP) is a kernel policy that marks paths such as /System, /bin, /sbin and /usr (excluding /usr/local) as restricted. The kernel denies writes there regardless of uid, so root has no special power.

open as a page

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

level: middleimportance: should knowfreq 46%

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.

open as a page

On an Apple Silicon Mac, a freshly compiled arm64 binary dies immediately with "Killed: 9" while the same source runs fine on an Intel Mac. What is happening?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Apple Silicon requires every arm64 executable to carry a valid code signature. The kernel refuses to execute unsigned or signature-broken arm64 code and kills the process with SIGKILL at exec, before any of its code runs. An ad-hoc signature satisfies the requirement.

open as a page

A script run with sudo on macOS gets "Operation not permitted" reading files under a user's ~/Desktop even though the mode bits allow it. What is blocking it, and how is it resolved?

level: seniorimportance: should knowfreq 52%

basics

~20 s

TCC — Transparency, Consent and Control — is blocking it. TCC gates access to protected user data by the identity of the responsible application, not by uid, so running as root does not help. The fix is granting Full Disk Access to the app that launched the script.

open as a page

On Apple hardware, what does it mean that a private key was generated "in the Secure Enclave", and what does that let you do that a key in a file cannot?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

The key is created inside a separate coprocessor and never leaves it in usable form. Software can ask the enclave to sign or perform key agreement, but cannot read, copy or back up the key material, and the enclave enforces a per-use policy such as requiring a biometric match.

open as a page