mcpField
S
seatbelt-sandboxer

seatbelt-sandboxer

github.com/trailofbits安全7.9k3.3k 次安装今天更新
代码仓库

关于 seatbelt-sandboxer

用于 macOS 应用安全分析的技能,可生成基于允许列表、权限尽量精简的 Seatbelt 沙盒配置。它指导需求分析、配置构建,以及对沙盒内应用进行迭代测试。

主要功能

  1. 01分析应用的资源访问需求
  2. 02设置默认拒绝的 Seatbelt 配置
  3. 03限定文件读写权限范围
  4. 04配置网络访问策略
  5. 05迭代测试并调整沙盒

适用场景

  1. 01限制 macOS 应用的文件访问
  2. 02为不同子命令分别配置沙盒
  3. 03阻止构建工具访问网络
  4. 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 IPCmach-lookup、mach-register、mach-bootstrap、mach-task-name系统服务、XPC、通知
POSIX IPCipc-posix-shm*、ipc-posix-sem*共享内存、信号量
Sysctlsysctl-read、sysctl-write读取系统信息(CPU、内存)
IOKitiokit-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系统通知
AppleEventsappleevent-send应用间通信(AppleScript)
摄像头/麦克风device-camera、device-microphone媒体采集
动态代码dynamic-code-generationJIT 编译
NVRAMnvram-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

已知限制

  1. 已弃用但仍可用:Apple 已弃用 sandbox-exec,但它在 macOS 14+ 中仍可使用
  2. 通常需要访问临时目录:许多应用需要访问 /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

参考资料

评论

支持 Markdown