This tutorial provides a practical overview of macOS sandbox profiles (.sb
files) used with sandbox-exec
. It focuses on understanding the structure, common elements, and how to craft them for specific use cases.
What is Sandbox-Exec?
sandbox-exec
is a command-line utility on macOS that allows you to execute a program within a restricted environment, defined by a sandbox profile. This profile specifies what resources the program can access, enhancing security by limiting the potential damage from vulnerabilities.
Why Use Sandbox Profiles?
- Defense in Depth: Adds an extra layer of security, even if the application itself has vulnerabilities.
- Limit Privilege Escalation: Prevents compromised processes from gaining elevated privileges.
- Data Protection: Restricts access to sensitive user data.
- System Stability: Prevents runaway processes from damaging the system.
Sandbox Profile Structure
Sandbox profiles are written in a Lisp-like syntax. Here's a breakdown of the key components:
(version 1) ; Profile version (usually 1)
(deny default) ; Deny everything by default
; Import common rules (optional)
(import "system.sb")
(import "com.apple.corefoundation.sb")
; Define reusable filters (optional)
(define (home-subpath home-relative-subpath)
(subpath (string-append (param "HOME") home-relative-subpath)))
; Allow specific actions
(allow file-read* (home-subpath "/Documents")) ; Allow read access to Documents folder
(allow network-outbound (remote ip)) ; Allow outbound network connections
(allow user-preference-read (preference-domain "com.example.myapp")) ; Allow reading preferences
; More complex rules with conditions
(with-filter (process-path "/usr/bin/some_helper_tool")
(allow file-read* (literal "/etc/some_config_file")))
Key Elements Explained
(version 1)
: Specifies the version of the sandbox profile language.(deny default)
: This is crucial. It sets the default policy to deny all access. You then selectively allow specific resources.(import "...")
: Includes other.sb
files.system.sb
provides basic system access, andcom.apple.corefoundation.sb
enables CoreFoundation functionality.allow
: Grants specific permissions.deny
: Explicitly denies specific permissions.file-read*
,file-write*
: Allow read and write access to files. The*
means "and execute".subpath
,literal
,prefix
,regex
: These are filters used withfile-read*
andfile-write*
to specify which files are affected by the rule.subpath
: Matches any path that starts with the given string.literal
: Matches the exact path.prefix
: Matches paths that have the given string as a prefix.regex
: Matches paths based on a regular expression.
mach-lookup
: Allows a process to find and connect to a Mach service (XPC service).iokit-open
: Allows a process to open an I/O Kit device.user-preference-read
,user-preference-write
: Allows access to user preferences.(param "VAR_NAME")
: Retrieves the value of an environment variable. Useful for making profiles more flexible. Common variables includeHOME
,TMPDIR
,DARWIN_USER_CACHE_DIR
.(with-filter ...)
: Applies a condition to a rule.process-attribute
: Checks attributes of the process being sandboxed.require-entitlement
: Checks for the presence of a specific entitlement.
Common Patterns and Best Practices
-
Start with
deny default
: This is the foundation of a secure profile. -
Use Specific Filters: Avoid broad permissions. Use
literal
orsubpath
whenever possible to restrict access to the minimum set of files needed. Regular expressions should be used sparingly and carefully. -
Leverage
param
: Use environment variables to make profiles more portable and adaptable to different environments. -
Understand Entitlements: Many system services require specific entitlements. If your application needs access to Contacts, Camera, or other protected resources, you'll need to declare the corresponding entitlement in your application's code signature. The sandbox profile must then allow access based on the presence of that entitlement.
-
Handle Temporary Files Carefully: Use
TMPDIR
for temporary files and clean them up when no longer needed. -
Consider the User Experience: If a sandbox profile is too restrictive, it can break functionality and frustrate users. Test your profiles thoroughly.
-
Telemetry and Reporting (for Internal Use): The
(with telemetry)
and(with report)
options are useful for debugging and identifying violations during development, but should be removed in production profiles. -
Use helper functions: The provided
.sb
files often define helper functions for common tasks like creating home-relative paths or checking for specific conditions.
Example Use Cases
-
Restricting a Command-Line Tool:
Suppose you have a command-line tool that should only be able to read files in
/usr/bin
and write to a temporary directory:(version 1) (deny default) (import "system.sb") (allow file-read* (subpath "/usr/bin")) (allow file-read* file-write* (subpath (param "TMPDIR")))
-
Sandboxing an Application with Camera Access:
If your application needs to use the camera, you'll need the
com.apple.security.device.camera
entitlement. The sandbox profile would then look like this:(version 1) (deny default) (import "system.sb") (allow file-read* (subpath "/usr/bin")) (allow file-read* file-write* (subpath (param "TMPDIR"))) (allow device-camera) (allow user-preference-read (preference-domain "com.example.myapp"))
-
Allowing a specific XPC service:
If your application needs to use a specific XPC service, you'll need to allow it in the sandbox profile.
(version 1) (deny default) (import "system.sb") (allow mach-lookup (global-name "com.example.myservice"))
How to Test and Debug
-
sandbox-exec -f your_profile.sb /path/to/executable
: Run your application with the sandbox profile. -
Check System Logs: Use the Console application or
syslog
to look for sandbox violation messages. These messages will tell you what resources were denied. -
Iterate: Add permissions to your sandbox profile as needed, testing after each change to ensure you're not granting more access than necessary.
Important Considerations
- Security is a Trade-off: Stricter sandboxing improves security but can reduce functionality. Find the right balance for your application.
- Dynamic Code Generation: Avoid dynamic code generation if possible, as it's generally disallowed in sandboxed environments.
- Third-Party Libraries: Be aware of the resources used by any third-party libraries your application depends on, and ensure your sandbox profile allows access to those resources.
Example: Running a Command with a Sandbox Profile
-
Create a Sandbox Profile (
my_profile.sb
):(version 1) (deny default) (allow file-read* (literal "/etc/hosts")) (allow file-write* (subpath (param "TMPDIR")))
-
Execute the Command:
TMPDIR=/tmp sandbox-exec -f my_profile.sb /bin/cat /etc/hosts > /tmp/output.txt
This command executes
/bin/cat /etc/hosts > /tmp/output.txt
within the sandbox defined bymy_profile.sb
. TheTMPDIR=/tmp
part sets theTMPDIR
environment variable, which the sandbox profile uses to allow writing to the temporary directory.
Conclusion
macOS sandbox profiles are a powerful tool for enhancing the security of your applications. By understanding the structure and common patterns, you can create profiles that effectively limit the resources a process can access, reducing the risk of security breaches and system instability. Remember to test your profiles thoroughly and iterate to find the right balance between security and functionality.