App Sandbox in Depth

The access control mechanisms used by App Sandbox to protect user data are small in number and easy to understand. But the specific steps for you to take, as you adopt App Sandbox, are unique to your app. To determine what those steps are, you must understand the key concepts for this technology.

The Need for a Last Line of Defense

You secure your app against attack from malware by following the practices recommended in Secure Coding Guide. But despite your best efforts to build an invulnerable barrier—by avoiding buffer overflows and other memory corruptions, preventing exposure of user data, and eliminating other vulnerabilities—your app can be exploited by malicious code. An attacker needs only to find a single hole in your defenses, or in any of the frameworks and libraries that you link against, to gain control of your app’s interactions with the system.

App Sandbox is designed to confront this scenario head on by letting you describe your app’s intended interactions with the system. The system then grants your app only the access your app needs to get its job done. If malicious code gains control of a properly sandboxed app, it is left with access to only the files and resources in the app’s sandbox.

To successfully adopt App Sandbox, use a different mindset than you might be accustomed to, as suggested in Table 2-1.

Table 2-1  The App Sandbox mindset

When developing…

When adopting App Sandbox…

Add features

Minimize system resource use

Take advantage of access throughout your app

Partition functionality, then distrust each part

Use the most convenient API

Use the most secure API

View restrictions as limitations

View restrictions as safeguards

When designing for App Sandbox, you are planning for the following worst-case scenario: Despite your best efforts, malicious code breaches an unintended security hole—either in your code or in a framework you’ve linked against. Capabilities you’ve added to your app become capabilities of the hostile code. Keep this in mind as you read the rest of this document.

Entitlements and System Resource Access

An app that is not sandboxed has access to all user-accessible system resources—including the built-in camera and microphone, network sockets, printing, and most of the file system. If successfully attacked by malicious code, such an app can behave as a hostile agent with wide-ranging potential to inflict harm.

When you enable App Sandbox for your app, you remove all but a minimal set of privileges and then deliberately restore them, one-by-one, using entitlements. An entitlement is a key-value pair that identifies a specific capability, such as the capability to open an outbound network socket.

One special entitlement—Enable App Sandboxing—turns on App Sandbox. When you enable sandboxing, Xcode creates a .entitlements property list file and shows it in the project navigator.

If your app requires a capability, request it by adding the corresponding entitlement to your Xcode project using the Summary tab of the target editor. If you don’t require a capability, take care to not include the corresponding entitlement.

You request entitlements on a target-by-target basis. If your app has a single target—the main application—you request entitlements only for that target. If you design your app to use a main application along with helpers (in the form of XPC services), you request entitlements individually, and as appropriate, for each target. You learn more about this in “External Tools, XPC Services, and Privilege Separation.”

You may require finer-grained control over your app’s entitlements than is available in the Xcode target editor. For example, you might request a temporary exception entitlement because App Sandbox does not support a capability your app needs, such as the ability to send an Apple event to an app that does not yet provide any scripting access groups. To work with temporary exception entitlements, use the Xcode property list editor to edit a target’s .entitlements property list file directly.

OS X App Sandbox entitlements are described in “Enabling App Sandbox” in Entitlement Key Reference. For a walk-through of requesting an entitlement for a target in an Xcode project, see “App Sandbox Quick Start.”

Container Directories and File System Access

When you adopt App Sandbox, your application has access to the following locations:

These policies are detailed further in the sections that follow.

The App Sandbox Container Directory

The app sandbox container directory has the following characteristics:

  • It is located at a system-defined path, within the user’s home directory. In a sandboxed app, this path is returned when your app calls the NSHomeDirectory function.

  • Your app has unrestricted read/write access to the container and its subdirectories.

  • OS X path-finding APIs (above the POSIX layer) refer to locations that are specific to your app.

    Most of these path-finding APIs refer to locations relative to your app’s container. For example, the container includes an individual Library directory (specified by the NSLibraryDirectory search path constant) for use only by your app, with individual Application Support and Preferences subdirectories.

    Using your container for support files requires no code change (from the pre-sandbox version of your app) but may require one-time migration, as explained in “Migrating an App to a Sandbox.”

    Some path-finding APIs (above the POSIX layer) refer to app-specific locations outside of the user’s home directory. In a sandboxed app, for example, the NSTemporaryDirectory function provides a path to a directory that is outside of the user’s home directory but specific to your app and within your sandbox; you have unrestricted read/write access to it for the current user. The behavior of these path-finding APIs is suitably adjusted for App Sandbox and no code change is needed.

  • OS X establishes and enforces the connection between your app and its container by way of your app’s code signature.

  • The container is in a hidden location, and so users do not interact with it directly. Specifically, the container is not for user documents. It is for files that your app uses, along with databases, caches, and other app-specific data.

    For a shoebox-style app, in which you provide the only user interface to the user’s content, that content goes in the container and your app has full access to it.

Thanks to code signing, no other sandboxed app can gain access to your container, even if it attempts to masquerade as your app by using your bundle identifier. Future versions of your app, however—provided that you use the same code signature and bundle identifier—do reuse your app’s container.

For each user, a sandboxed app’s container directory is created automatically when that user first runs the app. Because a container is within a user’s home directory, each user on a system gets their own container for your app.

The Application Group Container Directory

In addition to per-app containers, in OS X v10.7.5 and in OS X v10.8.3 and later, an application can use the com.apple.security.application-groups entitlement to request access to a shared container that is common to multiple applications produced by the same development team. This container is intended for content that is not user-facing, such as shared caches or databases.

These group containers are automatically added into each app’s sandbox container as determined by the existence of these keys, and are stored in ~/Library/Group Containers/<application-group-id>, where <application-group-id> is the name of the group. The group name itself must begin with your development team ID, followed by a period.

Beginning in OS X v10.8.3, your app can obtain the path to the group containers by calling the containerURLForSecurityApplicationGroupIdentifier: method of NSFileManager.

For more details, see “Adding an Application to an Application Group” in Entitlement Key Reference.

Powerbox and File System Access Outside of Your Container

Your sandboxed app can access file system locations outside of its container in the following three ways:

  • At the specific direction of the user

  • By using entitlements for specific file-system locations (described in “Entitlements and System Resource Access”)

  • When the file system location is in certain directories that are world readable

The OS X security technology that interacts with the user to expand your sandbox is called Powerbox. Powerbox has no API. Your app uses Powerbox transparently when you use the NSOpenPanel and NSSavePanel classes. You enable Powerbox by setting an entitlement using Xcode, as described in “Enabling User-Selected File Access” in Entitlement Key Reference.

When you invoke an Open or Save dialog from your sandboxed app, the window that appears is presented not by AppKit but by Powerbox. Using Powerbox is automatic when you adopt App Sandbox—it requires no code change from the pre-sandbox version of your app. Accessory panels that you’ve implemented for opening or saving are faithfully rendered and used.

The security benefit provided by Powerbox is that it cannot be manipulated programmatically—specifically, there is no mechanism for hostile code to use Powerbox for accessing the file system. Only a user, by interacting with Open and Save dialogs via Powerbox, can use those dialogs to reach portions of the file system outside of your previously established sandbox. For example, if a user saves a new document, Powerbox expands your sandbox to give your app read/write access to the document.

When a user of your app specifies they want to use a file or a folder, the system adds the associated path to your app’s sandbox. Say, for example, a user drags the ~/Documents folder onto your app’s Dock tile (or onto your app’s Finder icon, or into an open window of your app), thereby indicating they want to use that folder. In response, the system makes the ~/Documents folder, its contents, and its subfolders available to your app.

If a user instead opens a specific file, or saves to a new file, the system makes the specified file, and that file alone, available to your app.

In addition, the system automatically permits a sandboxed app to:

  • Connect to system input methods

  • Invoke services chosen by the user from the Services menu (only those services flagged as “safe” by the service provider are available to a sandboxed app)

  • Open files chosen by the user from the Open Recent menu

  • Participate with other apps by way of user-invoked copy and paste

  • Read files that are world readable, in certain directories, including the following directories:

    • /bin

    • /sbin

    • /usr/bin

    • /usr/lib

    • /usr/sbin

    • /usr/share

    • /System

  • Read and write files in directories created by calling NSTemporaryDirectory.

After a user has specified a file they want to use, that file is within your app’s sandbox. The file is then vulnerable to attack if your app is exploited by malicious code: App Sandbox provides no protection. To provide protection for the files within your sandbox, follow the recommendations in Secure Coding Guide.

By default, files opened or saved by the user remain within your sandbox until your app terminates, except for files that were open at the time that your app terminates. Such files reopen automatically by way of the OS X Resume feature the next time your app launches, and are automatically added back to your app’s sandbox.

To provide persistent access to resources located outside of your container, in a way that doesn’t depend on Resume, use security-scoped bookmarks as explained in “Security-Scoped Bookmarks and Persistent Resource Access.”

Related Items

The related items feature of App Sandbox lets your app access files that have the same name as a user-chosen file, but a different extension. This feature consists of two parts: a list of related extensions in the application’s Info.plist file and code to tell the sandbox what you’re doing.

There are two common scenarios where this makes sense:

  • Scenario 1:

    Your app needs to be able to save a file with a different extension than that of the original file. For example, when you paste an image into an RTF file in TextEdit and save it, TextEdit changes the file’s extension from .rtf to .rtfd (and it becomes a directory).

    To handle this situation, you must use an NSFileCoordinator object to coordinate access to the file. Before you rename the file, call the itemAtURL:willMoveToURL: method. After you rename the file, call the itemAtURL:didMoveToURL: method.

  • Scenario 2:

    Your app needs to be able to open or save multiple related files with the same name and different extensions (for example, to automatically open a subtitle file with the same name as a movie file, or to allow for a SQLite journal file).

    To gain access to that secondary file, create a class that conforms to the NSFilePresenter protocol. This object should provide the main file’s URL as its primaryPresentedItemURL property, and should provide the secondary file’s URL as its presentedItemURL property.

    After the user opens the main file, your file presenter object should call the addFilePresenter: class method on the NSFileCoordinator class to register itself.

In both scenarios, you must make a small change to the application’s Info.plist file. Your app should already declare a Document Types (CFBundleDocumentTypes) array that declares the file types your app can open.

For each file type dictionary in that array, if that file type should be treated as a potentially related type for open and save purposes, add the key NSIsRelatedItemType with a boolean value of YES.

To learn more about file presenters and file coordinators, read File System Programming Guide.

Open and Save Dialog Behavior with App Sandbox

Certain NSOpenPanel and NSSavePanel methods behave differently when App Sandbox is enabled for your app:

In addition, the effective, runtime inheritance path for the NSOpenPanel and NSSavePanel classes is different with App Sandbox, as illustrated in Table 2-2.

Table 2-2  Open and Save class inheritance with App Sandbox

Without App Sandbox

NSOpenPanel : NSSavePanel : NSPanel : NSWindow : NSResponder : NSObject

With App Sandbox

NSOpenPanel : NSSavePanel : NSObject

Because of this runtime difference, an NSOpenPanel or NSSavePanel object inherits fewer methods with App Sandbox. If you attempt to send a message to an NSOpenPanel or NSSavePanel object, and that method is defined in the NSPanel, NSWindow, or NSResponder classes, the system raises an exception. The Xcode compiler does not issue a warning or error to alert you to this runtime behavior.

Security-Scoped Bookmarks and Persistent Resource Access

Your app’s access to file-system locations outside of its container—as granted to your app by way of user intent, such as through Powerbox—does not automatically persist across app launches or system restarts. When your app reopens, you have to start over. (The one exception to this is for files open at the time that your app terminates, which remain in your sandbox thanks to the OS X Resume feature).

Starting in OS X v10.7.3, you can retain access to file-system resources by employing a security mechanism, known as security-scoped bookmarks, that preserves user intent. Here are a few examples of app features that can benefit from this:

Two Distinct Types of Security-Scoped Bookmark

Security-scoped bookmarks, available starting in OS X v10.7.3, support two distinct use cases:

  • An app-scoped bookmark provides your sandboxed app with persistent access to a user-specified file or folder.

    For example, if your app employs a download or processing folder that is outside of the app container, obtain initial access by presenting an NSOpenPanel dialog to obtain the user’s intent to use a specific folder. Then, create an app-scoped bookmark for that folder and store it as part of the app’s configuration (perhaps in a property list file or using the NSUserDefaults class). With the app-scoped bookmark, your app can obtain future access to the folder.

  • A document-scoped bookmark provides a specific document with persistent access to a file.

    For example, a video editing app typically supports the notion of a project document that refers to other files and needs persistent access to those files. Such a project document can store security-scoped bookmarks to the files it refers to.

    Obtain initial access to a referred item by asking for user intent to use that item. Then, create a document-scoped bookmark for the item and store the bookmark as part of the document’s data.

    A document-scoped bookmark can be resolved by any app that has access to the bookmark data itself and to the document that owns the bookmark. This supports portability, allowing a user, for example, to send a document to another user; the document’s secure bookmarks remain usable for the recipient. The document can be a single flat file or a package containing multiple files.

    A document-scoped bookmark can point only to a file, not a folder, and only to a file that is not in a location used by the system (such as /private or /Library).

Using Security-Scoped Bookmarks

To use either type of security-scoped bookmark requires you to perform five steps:

  1. Set the appropriate entitlement in the target that needs to use security-scoped bookmarks.

    Do this once per target as part of configuring your Xcode project.

  2. Create a security-scoped bookmark.

    Do this when a user has indicated intent (such as via Powerbox) to use a file-system resource outside of your app’s container, and you want to preserve your app’s ability to access the resource.

  3. Resolve the security-scoped bookmark.

    Do this when your app later (for example, after app relaunch) needs access to a resource you bookmarked in step 2. The result of this step is a security-scoped URL.

  4. Explicitly indicate that you want to use the file-system resource whose URL you obtained in step 3.

    Do this immediately after obtaining the security-scoped URL (or, when you later want to regain access to the resource after having relinquished your access to it).

  5. When done using the resource, explicitly indicate that you want to stop using it.

    Do this as soon as you know that you no longer need access to the resource (typically, after you close it).

    After you relinquish access to a file-system resource, to use that resource again you must return to step 4 (to again indicate you want to use the resource).

    If your app is relaunched, you must return to step 3 (to resolve the security-scoped bookmark).

The first step in the preceding list, requesting entitlements, is the prerequisite for using either type of security-scoped bookmark. Perform this step as follows:

  • To use app-scoped bookmarks in a target, set the com.apple.security.files.bookmarks.app-scope entitlement value to true.

  • To use document-scoped bookmarks in a target, set the com.apple.security.files.bookmarks.document-scope entitlement value to true.

You can request either or both of these entitlements in a target, as needed. These entitlements are available starting in OS X v10.7.3 and are described in “Enabling Security-Scoped Bookmark and URL Access” in Entitlement Key Reference.

With the appropriate entitlements, you can create a security-scoped bookmark by calling the bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error: method of the NSURL class.

When you later need access to a bookmarked resource, resolve its security-scoped bookmark by calling the the URLByResolvingBookmarkData:options:relativeToURL:bookmarkDataIsStale:error: method of the NSURL class.

In a sandboxed app, you cannot access the file-system resource that a security-scoped URL points to until you call the startAccessingSecurityScopedResource method on the URL.

When you no longer need access to a resource that you obtained using security scope (typically, after you close the resource) you must call the stopAccessingSecurityScopedResource method on the resource’s URL.

Calls to start and stop access are not nested. When you call the stopAccessingSecurityScopedResource method, you immediately lose access to the resource. If you call this method on a URL whose referenced resource you do not have access to, nothing happens.

For detailed descriptions of the methods, constants, and entitlements to use for implementing security-scoped bookmarks in your app, read NSURL Class Reference, and read “Enabling Security-Scoped Bookmark and URL Access” in Entitlement Key Reference.

App Sandbox and Code Signing

After you enable App Sandbox and specify other entitlements for a target in your Xcode project, you must code sign the project. Take note of the distinction between how you set entitlements and how you set a code signing identity:

You must perform code signing because entitlements (including the special entitlement that enables App Sandbox) are built into an app’s code signature. From another perspective, an unsigned app is not sandboxed and has only default entitlements, regardless of settings you’ve applied in the Xcode target editor.

OS X enforces a tie between an app’s container and the app’s code signature. This important security feature ensures that no other sandboxed app can access your container. The mechanism works as follows:

  1. When the system creates a container for an app, it sets an access control list (ACL) on that container. The initial access control entry in that list contains the app’s Designated Requirement (DR), which is part of the app’s signature that describes how future versions of the app can be recognized.

  2. Each time an app with the same bundle ID launches, the system checks that the app’s code signature matches the designated requirements specified in one of the entries in the container’s ACL. If the system does not find a match, it prevents the app from launching.

OS X’s enforcement of container integrity impacts your development and distribution cycle. This is because, in the course of creating and distributing an app, the app is code signed using various signatures. Here’s how the process works:

  1. Before you create a project, you obtain three code signing certificates from Apple: a development certificate, a distribution certificate, and (optionally) a Developer ID certificate. (To learn how to obtain these code signing certificates, read App Distribution Guide.)

    When used in conjunction with the corresponding private keys from your keychain, these certificates form three separate digital identities. For development and testing, you sign your app with your development identity. When you submit a version to the app store, you use your distribution identity. If you are distributing a version outside the app store, you use your Developer ID identity.

  2. When the Mac App Store distributes your app, it is signed with an Apple code signature.

For testing and debugging, you may want to run both versions of your app: the version you sign and the version Apple signs. But OS X sees the Apple-signed version of your app as an intruder and won’t allow it to launch: Its code signature does not match the one expected by your app’s existing container.

If you try to run the Apple-signed version of your app, you get a crash report containing a statement similar to this:

Exception Type:  EXC_BAD_INSTRUCTION (SIGILL)

The solution is to adjust the access control list (ACL) on your app’s container to recognize the Apple-signed version of your app. Specifically, you add the designated code requirement of the Apple-signed version of your app to the app container’s ACL.

bullet
To adjust an ACL to recognize an Apple-signed version of your app
  1. Open Terminal (in /Applications/Utilities).

  2. Open a Finder window that contains the Apple-signed version of your app.

  3. In Terminal, enter the following command:

    asctl container acl add -file <path/to/app>

    In place of the <path/to/app> placeholder, substitute the path to the Apple-signed version of your app. Instead of manually typing the path, you can drag the app’s Finder icon to the Terminal window.

The container’s ACL now includes the designated code requirements for both versions of your app. OS X then allows you to run either version of your app.

You can use this same technique to share a container between (1) a version of an app that you initially signed with a development identity, such as the one you used in “App Sandbox Quick Start,” and (2) a released version downloaded from the Mac App Store.

You can view the list of code requirements in a container’s ACL. For example, after adding the designated code requirement for the Apple-signed version of your app, you can confirm that the container’s ACL lists two permissible code requirements.

bullet
To display the list of code requirements in a container’s ACL
  1. Open Terminal (in /Applications/Utilities).

  2. In Terminal, enter the following command:

    asctl container acl list -bundle <container name>

    In place of the <container name> placeholder, substitute the name of your app’s container directory. (The name of your app’s container directory is typically the same as your app’s bundle identifier.)

For more information about working with App Sandbox container access control lists and their code requirements, read the man page for the asctl (App Sandbox control) tool.

External Tools, XPC Services, and Privilege Separation

Some app operations are more likely to be targets of malicious exploitation. Examples are the parsing of data received over a network, and the decoding of video frames. By using XPC, you can improve the effectiveness of the damage containment offered by App Sandbox by separating such potentially dangerous activities into their own address spaces.

Your app can also launch existing helper apps using launch services, but only if certain conditions are met.

The sections below explain these concepts in more detail.

XPC Services

XPC is an OS X interprocess communication technology that complements App Sandbox by enabling privilege separation. Privilege separation, in turn, is a development strategy in which you divide an app into pieces according to the system resource access that each piece needs. The component pieces that you create are called XPC services.

You create an XPC service as an individual target in your Xcode project. Each service gets its own sandbox—specifically, it gets its own container and its own set of entitlements.

In addition, an XPC service that you include with your app is accessible only by your app. These advantages add up to making XPC the best technology for implementing privilege separation in an OS X app.

By contrast, a child process created by using the posix_spawn function, by calling fork and exec (discouraged), or by using the NSTask class simply inherits the sandbox of the process that created it. You cannot configure a child process’s entitlements. For these reasons, child processes do not provide effective privilege separation.

To use XPC with App Sandbox:

  • Confer minimal privileges to each XPC service, according to its needs.

  • Design the data transfers between the main app and each XPC service to be secure.

  • Structure your app’s bundle appropriately.

The life cycle of an XPC service, and its integration with Grand Central Dispatch (GCD), is managed entirely by the system. To obtain this support, you need only to structure your app’s bundle correctly.

For more on XPC, see “Creating XPC Services” in Daemons and Services Programming Guide.

Launching Helpers with Launch Services

A sandboxed app is allowed to launch a helper using Launch Services if at least one of these conditions has been met:

  • Both the app and helper pass the Gatekeeper assessment. By default that means both are signed by the Mac App Store or with a Developer ID.

  • The app is installed in /Applications and the app bundle and all contents are owned by root.

  • The helper has been (manually) run at least once by the user.

If none of these conditions have been met, you'll see errors like the following:

  • "Not allowing process 19920 to launch '/Applications/Main.app/Contents/Resources/Helper.app' because the security assessment verdict was denied."

    This message means that the Gatekeeper assessment was denied. You can confirm that with the spctl tool as follows:

    $ spctl --assess -vvvv /Applications/Main.app/
     
    /Applications/Main.app/: rejected
    origin=Mac Developer: Developer Name
     
    $ spctl --assess -vvvv /Applications/Main.app/Contents/Resources/Helper.app/
     
    /Applications/Main.app/Contents/Resources/Helper.app/: rejected
    origin=Mac Developer: Developer Name
  • "The application “Helper” could not be launched because it is corrupt."

    "The operation couldn’t be completed. (OSStatus error -10827.)"

    This is the typical error if none of the above conditions have been fulfilled.

The results are the same whether you use Launch Services directly (by calling LSOpenCFURLRef, for example) or indirectly (by calling the launchApplicationAtURL:options:configuration:error: method in NSWorkspace, for example).

In addition, upon failure, in OS X v10.7.5 and earlier, you will also see a bogus deny file-write-data /Applications/Main.app/Contents/Resources/Helper.app sandbox violation. This error has no functional impact and can be ignored.

IPC and POSIX Semaphores and Shared Memory

Normally, sandboxed apps cannot use Mach IPC, POSIX semaphores and shared memory, or UNIX domain sockets (usefully). However, by specifying an entitlement that requests membership in an application group, an app can use these technologies to communicate with other members of that application group.

UNIX domain sockets are straightforward; they work just like any other file.

Any semaphore or Mach port that you wish to access within a sandboxed app must be named according to a special convention:

For example, if your application group’s name is Z123456789.com.example.app-group, you might create two semaphore named Z123456789.myappgroup/rdyllwflg and Z123456789.myappgroup/bluwhtflg. You might create a Mach port named Z123456789.com.example.app-group.Port_of_Kobe.

To learn more about application groups, read “The Application Group Container Directory,” then read “Adding an Application to an Application Group” in Entitlement Key Reference.