WWDC Quick Look 💓 By SwiftGGTeam
Protect your Mac app with environment constraints

Protect your Mac app with environment constraints

Watch original video

Highlight

macOS Sonoma introduces Environment Constraints, allowing developers to embed launch constraints and library loading constraints in binaries to precisely control which processes can launch their own auxiliary tools, XPC services, and which code can be loaded into the process address space.

Core Content

Why do we need environmental constraints?

Feature-rich Mac apps typically contain multiple components: frameworks, accessibility tools, XPC services, launch agents, and more. These components run in potentially hostile environments and face multiple security threats:

  • Malicious processes may passposix_spawnControl input and resource access to child processes
  • An attacker may modify disk files and inject unexpected data into the victim process
  • Auxiliary tools may leak keychain or iCloud data when called illegally

00:48

Existing security mechanisms (App Sandbox, Hardened Runtime, Gatekeeper) mainly focus on the running process itself, rather than the execution environment of the process. Environmental constraints fill this gap.

Three types of environmental constraints

Launch Constraints are embedded in specific binary files and define three types of attributes:

  1. Self Constraints: Attribute requirements of the process itself
  2. Parent Process Constraints: Which parent processes can start it
  3. Responsible Process Constraints: Which processes can be responsible for it

04:54

Launchd Plist Constraints PassedSpawnConstraintThe key is registered in the plist that starts the agent/daemon process, ensuring that only processes that meet the constraints can be started by the plist.

Library Load Constraints control which code can be loaded into the process address space, replacing the original “all or nothing” library verification scheme.

Actual scene

Assume your applicationMyDemo.appContains the following components:

  • a launch agentDemoMenuBar.agent- an auxiliary tooldemohelper- A framework and its XPC services
  • A library from a third-party team

10:21

Without constraints, an attacker could run directly from the terminaldemohelperand pass in--cloudParameters to access iCloud data. After setting the parent process constraints, onlyMyDemo.appThis auxiliary tool can be started.

Detailed Content

Constrained data structure

Environment constraints are encoded using a plist dictionary, with top-level key-value pairs implicitly logically ANDed. The following operators are supported:

  • $and / $or: Logical AND/OR -$and-array / $or-array: Limit dictionary nesting level -$in: Specifies an array of allowed values

07:55

Commonly used fact keys:

  • signing-identifier: code signing identifier -team-identifier: development team identifier -cdhash: The unique hash value of the code

Example: Library Loading Constraints

Allow loading code signed by your own team or a trusted third-party team:

<dict>
    <key>team-identifier</key>
    <dict>
        <key>$in</key>
        <array>
            <string>M2657GZ2M9</string>
            <string>P9Z4AN7VHQ</string>
        </array>
    </dict>
</dict>

15:29

Key points:

  • team-identifierSpecify allowed development teams -$inOperator accepts array of strings, allowing multiple teams
  • This constraint overridesdisable-library-validationentitlement to avoid “allow anyone” security downgrade

Example: Parent Process Constraints

Restrict accessibility tools to only be launched by the main app:

<dict>
    <key>team-identifier</key>
    <string>M2657GZ2M9</string>
    <key>signing-identifier</key>
    <string>com.demo.MyDemo</string>
</dict>

11:02

Key points:

  • Constrain team identifiers and signature identifiers at the same time to prevent other applications from the same team from using them fraudulently
  • Specify this file in Xcode’s “Launch Constraint Parent Process Plist” settings
  • The system generates a crash report when constraints are violated

Example: Process startup constraints

XPC services only allow access to specific processes:

<dict>
    <key>team-identifier</key>
    <string>M2657GZ2M9</string>
    <key>signing-identifier</key>
    <dict>
        <key>$in</key>
        <array>
            <string>com.demo.MyDemo</string>
            <string>com.demo.DemoMenuBar</string>
            <string>demohelper</string>
        </array>
    </dict>
</dict>

14:06

Key points:

  • use$inList all process signature identifiers allowed to access this XPC service
  • Configured in Xcode’s “Launch Constraint Responsible Process Plist” settings
  • Prevent XPC services from being called independently after being extracted from the bundle

Example: Launchd Plist constraints

To prevent startup agents from being tampered with:

<dict>
    <key>Label</key>
    <string>com.demo.DemoMenuBar.agent</string>
    <key>BundleProgram</key>
    <string>Contents/Library/LaunchAgents/DemoMenuBar.app/Contents/MacOS/DemoMenuBar</string>
    <key>KeepAlive</key>
    <dict>
        <key>SuccessfulExit</key>
        <true/>
    </dict>
    <key>RunAtLoad</key>
    <true/>
    <key>SpawnConstraint</key>
    <dict>
        <key>team-identifier</key>
        <string>M2657GZ2M9</string>
        <key>signing-identifier</key>
        <string>com.demo.DemoMenuBar</string>
    </dict>
</dict>

14:52

Key points:

  • SpawnConstraintKey definition plist code identities allowed to launch
  • After the user approves the background task, the attacker cannot replace the executable file pointed to by plist
  • PassSMAppServiceEffective when API registration plist

Availability

  • Library loading constraints and launchd plist constraints: target platform any macOS version, enforcement starts with macOS Sonoma
  • Launch constraints: target platform macOS 13.3+, enforcement starting from macOS 13.3

15:51

Core Takeaways

  1. Add parent process constraints for auxiliary tools

    • WHAT: Restrict your command line helpers to be launched only by the main application
    • Why it’s worth it: Prevent attackers from directly calling accessibility tools to access Keychain or iCloud data
    • How to start: Create a constraint plist file and reference it in Xcode’s “Launch Constraint Parent Process Plist”
  2. Add responsible process constraints to the XPC service

    • What to do: Limit which processes can trigger your XPC service
    • Why it’s worth doing: Prevent the XPC service from being called independently after being extracted, protecting the privileges assigned to it
    • How to start: Configure the list of allowed signature identifiers in the “Launch Constraint Responsible Process Plist”
  3. Replace disable-library-validation with library loading constraints

    • What: Precisely control which third-party libraries are allowed to be loaded instead of turning off library verification completely
    • Why it’s worth doing: Prevent arbitrary signed code injection while integrating third-party libraries
    • How to get started: Create an include$inOperator’s constraints plist, listing allowed team identifiers
  4. Add SpawnConstraint for startup agent

    • What: Embed launch constraints in launchd plist
    • Why it’s worth doing: Ensure user-approved background tasks cannot be tampered with to point to malicious code
    • How to start: Add in plistSpawnConstraintDictionary, viaSMAppServiceregister

Comments

GitHub Issues · utterances