关于 seatbelt-sandboxer
用于 macOS 应用安全分析的技能,可生成基于允许列表、权限尽量精简的 Seatbelt 沙盒配置。它指导需求分析、配置构建,以及对沙盒内应用进行迭代测试。
主要功能
- 01分析应用的资源访问需求
- 02设置默认拒绝的 Seatbelt 配置
- 03限定文件读写权限范围
- 04配置网络访问策略
- 05迭代测试并调整沙盒
适用场景
- 01限制 macOS 应用的文件访问
- 02为不同子命令分别配置沙盒
- 03阻止构建工具访问网络
- 04使用生成的配置测试应用
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ trailofbits/ skills --skill seatbelt-sandboxer安装后在 Claude Code 中输入/seatbelt-sandboxer确认已加载。
附带文件agents/openai.yamlassets/trail-of-bits-mark.svg
详细介绍
macOS Seatbelt 沙盒配置分析
为应用生成遵循最小权限原则、基于允许列表的 Seatbelt 沙盒配置。
适用场景
- 用户要求在 macOS 上对应用进行“沙盒化”“隔离”或“限制”
- 对需要限制文件或网络访问权限的 macOS 进程进行沙盒化
- 如果担心供应链攻击,则创建纵深防御隔离
不适用场景
- Linux 容器(应改用 seccomp-bpf、AppArmor 或命名空间)
- Windows 应用
- 确实需要广泛系统访问权限的应用
- 沙盒化开销不合理的一次性脚本
配置分析方法
步骤 1:确定应用需求
确定应用在以下资源类别中的需求:
| 类别 | 操作 | 常见用途 |
|---|---|---|
| 文件读取 | file-read-data、file-read-metadata、file-read-xattr、file-test-existence、file-map-executable | 读取源文件、配置、库 |
| 文件写入 | file-write-data、file-write-create、file-write-unlink、file-write-mode、file-write-xattr、file-clone、file-link | 输出文件、缓存、临时文件 |
| 网络 | network-bind、network-inbound、network-outbound | 服务端、API 调用、软件包下载 |
| 进程 | process-fork、process-exec、process-exec-interpreter、process-info*、process-codesigning* | 启动子进程、运行脚本 |
| Mach IPC | mach-lookup、mach-register、mach-bootstrap、mach-task-name | 系统服务、XPC、通知 |
| POSIX IPC | ipc-posix-shm*、ipc-posix-sem* | 共享内存、信号量 |
| Sysctl | sysctl-read、sysctl-write | 读取系统信息(CPU、内存) |
| IOKit | iokit-open、iokit-get-properties、iokit-set-properties | 硬件访问、设备驱动程序 |
| 信号 | signal | 进程间信号处理 |
| 伪终端 | pseudo-tty | 终端模拟 |
| 系统 | system-fsctl、system-socket、system-audit、system-info | 底层系统调用 |
| 用户偏好设置 | user-preference-read、user-preference-write | 读取/写入用户默认设置 |
| 通知 | darwin-notification-post、distributed-notification-post | 系统通知 |
| AppleEvents | appleevent-send | 应用间通信(AppleScript) |
| 摄像头/麦克风 | device-camera、device-microphone | 媒体采集 |
| 动态代码 | dynamic-code-generation | JIT 编译 |
| NVRAM | nvram-get、nvram-set、nvram-delete | 固件变量 |
对于每个类别,确定:是否需要?以及具体范围(路径、服务等)。
如果应用包含多个执行操作有显著差异的子命令,例如 Webpack 这类 Javascript 打包工具的 build 和 serve 命令,请执行以下操作:
- 分别分析各子命令
- 为每个子命令创建单独的沙盒配置
- 创建一个辅助脚本,作为原始二进制文件的直接替代品,根据传入的子命令使用相应的 Seatbelt 配置执行沙盒化应用。
步骤 2:从最小配置开始
从默认拒绝所有访问并仅允许必要进程操作的配置开始,并将其保存到名称合适、扩展名为 .sb 的 Seatbelt 配置文件中。
(version 1)
(deny default)
;; Essential for any process
(allow process-exec*)
(allow process-fork)
(allow sysctl-read)
;; Metadata access (stat, readdir) - doesn't expose file contents
(allow file-read-metadata)
步骤 3:添加文件读取权限(允许列表)
使用 file-read-data(而非 file-read*)实现基于允许列表的读取:
(allow file-read-data
;; System paths (required for most runtimes)
(subpath "/usr")
(subpath "/bin")
(subpath "/sbin")
(subpath "/System")
(subpath "/Library")
(subpath "/opt") ;; Homebrew
(subpath "/private/var")
(subpath "/private/etc")
(subpath "/private/tmp")
(subpath "/dev")
;; Root symlinks for path resolution
(literal "/")
(literal "/var")
(literal "/etc")
(literal "/tmp")
(literal "/private")
;; Application-specific config (customize as needed)
(regex (string-append "^" (regex-quote (param "HOME")) "/\\.myapp(/.*)?$"))
;; Working directory
(subpath (param "WORKING_DIR")))
为什么用 file-read-data 而不是 file-read*?
file-read*允许对任意路径执行所有文件读取操作file-read-data仅允许读取所列路径中的文件内容- 与广泛允许的
file-read-metadata配合使用,可实现:- ✅ 可在任何位置执行 stat/readdir(路径解析所需)
- ❌ 无法读取允许列表之外的文件内容
步骤 4:添加文件写入权限(受限)
(allow file-write*
;; Working directory only
(subpath (param "WORKING_DIR"))
;; Temp directories
(subpath "/private/tmp")
(subpath "/tmp")
(subpath "/private/var/folders")
;; Device files for output
(literal "/dev/null")
(literal "/dev/tty"))
步骤 5:配置网络
网络访问分为三个级别:
;; OPTION 1: Block all network (most restrictive - use for build tools)
(deny network*)
;; OPTION 2: Localhost only (use for dev servers, local services)
;; Bind to local ports
(allow network-bind (local tcp "*:*"))
;; Accept inbound connections
(allow network-inbound (local tcp "*:*"))
;; Outbound to localhost + DNS only
(allow network-outbound
(literal "/private/var/run/mDNSResponder") ;; DNS resolution
(remote ip "localhost:*")) ;; localhost only
;; OPTION 3: Allow all network (least restrictive - avoid if possible)
(allow network*)
网络过滤器语法:
(local tcp "*:*")- 任意本地 TCP 端口(local tcp "*:8080")- 指定本地端口(remote ip "localhost:*")- 仅允许出站连接到 localhost(remote tcp)- 允许出站连接到任意主机(literal "/private/var/run/mDNSResponder")- 用于 DNS 的 Unix 套接字
步骤 6:迭代测试
生成或编辑 Seatbelt 配置文件后,请在沙盒中测试目标应用的功能。如果有任何功能无法正常工作,请修改 Seatbelt 配置文件。重复此过程,直到生成权限最小化的 Seatbelt 文件,并通过实测确认应用在使用生成的 Seatbelt 配置文件进行沙盒隔离时能够正常运行。
如果程序需要外部输入才能完整运行(例如需要对应用进行打包的 Javascript 打包工具),请从知名且最好是官方的来源查找示例输入。例如,Rspack 打包工具的这些示例项目:https://github.com/rstackjs/rstack-examples/tree/main/rspack/
# Test basic execution
sandbox-exec -f profile.sb -D WORKING_DIR=/path -D HOME=$HOME /bin/echo "test"
# Test the actual application
sandbox-exec -f profile.sb -D WORKING_DIR=/path -D HOME=$HOME \
/path/to/application --args
# Test security restrictions
sandbox-exec -f profile.sb -D WORKING_DIR=/tmp -D HOME=$HOME \
cat ~/.ssh/id_rsa
# Expected: Operation not permitted
常见故障模式:
| 症状 | 原因 | 修复方法 |
|---|---|---|
| 退出代码 134(SIGABRT) | 沙盒违规 | 检查被阻止的操作 |
| 退出代码 65 + 语法错误 | 配置文件语法无效 | 检查 Seatbelt 语法 |
现有文件出现 ENOENT | 缺少 file-read-metadata | 添加 (allow file-read-metadata) |
| 进程挂起 | 缺少 IPC 权限 | 如有需要,添加 (allow mach-lookup) |
Seatbelt 语法参考
路径过滤器
(subpath "/path") ;; /path and all descendants
(literal "/path/file") ;; Exact path only
(regex "^/path/.*\\.js$") ;; Regex match
参数替换
(param "WORKING_DIR") ;; Direct use
(subpath (param "WORKING_DIR")) ;; In subpath
(string-append (param "HOME") "/.config") ;; Concatenation
(regex-quote (param "HOME")) ;; Escape for regex
操作
文件操作:
(allow file-read-data ...) ;; Read file contents
(allow file-read-metadata) ;; stat, lstat, readdir (no contents)
(allow file-read-xattr ...) ;; Read extended attributes
(allow file-test-existence ...) ;; Check if file exists
(allow file-map-executable ...) ;; mmap executable (dylibs)
(allow file-write-data ...) ;; Write to existing files
(allow file-write-create ...) ;; Create new files
(allow file-write-unlink ...) ;; Delete files
(allow file-write* ...) ;; All write operations
(allow file-read* ...) ;; All read operations (use sparingly)
进程操作:
(allow process-exec* ...) ;; Execute binaries
(allow process-fork) ;; Fork child processes
(allow process-info-pidinfo) ;; Query process info
(allow signal) ;; Send/receive signals
网络操作:
(allow network-bind (local tcp "*:*")) ;; Bind to any local TCP port
(allow network-bind (local tcp "*:8080")) ;; Bind to specific port
(allow network-inbound (local tcp "*:*")) ;; Accept TCP connections
(allow network-outbound (remote ip "localhost:*")) ;; Outbound to localhost only
(allow network-outbound (remote tcp)) ;; Outbound TCP to any host
(allow network-outbound
(literal "/private/var/run/mDNSResponder")) ;; DNS via Unix socket
(allow network*) ;; All network (use sparingly)
(deny network*) ;; Block all network
IPC 操作:
(allow mach-lookup ...) ;; Mach IPC lookups
(allow mach-register ...) ;; Register Mach services
(allow ipc-posix-shm* ...) ;; POSIX shared memory
(allow ipc-posix-sem* ...) ;; POSIX semaphores
系统操作:
(allow sysctl-read) ;; Read system info
(allow sysctl-write ...) ;; Modify sysctl (rare)
(allow iokit-open ...) ;; IOKit device access
(allow pseudo-tty) ;; Terminal emulation
(allow dynamic-code-generation) ;; JIT compilation
(allow user-preference-read ...) ;; Read user defaults
已知限制
- 已弃用但仍可用:Apple 已弃用 sandbox-exec,但它在 macOS 14+ 中仍可使用
- 通常需要访问临时目录:许多应用需要访问
/tmp和/var/folders
示例:通用 CLI 应用
(version 1)
(deny default)
;; Process
(allow process-exec*)
(allow process-fork)
(allow sysctl-read)
;; File metadata (path resolution)
(allow file-read-metadata)
;; File reads (allowlist)
(allow file-read-data
(literal "/") (literal "/var") (literal "/etc") (literal "/tmp") (literal "/private")
(subpath "/usr") (subpath "/bin") (subpath "/sbin") (subpath "/opt")
(subpath "/System") (subpath "/Library") (subpath "/dev")
(subpath "/private/var") (subpath "/private/etc") (subpath "/private/tmp")
(subpath (param "WORKING_DIR")))
;; File writes (restricted)
(allow file-write*
(subpath (param "WORKING_DIR"))
(subpath "/private/tmp") (subpath "/tmp") (subpath "/private/var/folders")
(literal "/dev/null") (literal "/dev/tty"))
;; Network disabled
(deny network*)
用法:
sandbox-exec -f profile.sb \
-D WORKING_DIR=/path/to/project \
-D HOME=$HOME \
/path/to/application