返回更新列表
新发布Sep 12, 2026

sandbox-runtime v0.0.76

一个轻量级沙箱工具,用于在操作系统层面强制执行任意进程的文件系统和网络限制,而无需使用容器。

分享

Anthropic Sandbox Runtime (srt)

一个轻量级沙箱工具,用于在操作系统层面强制对任意进程实施文件系统和网络限制,无需容器。

srt 使用原生操作系统沙箱原语(macOS 上的 sandbox-exec,Linux 上的 bubblewrap)以及基于代理的网络过滤。它可用于对代理、本地 MCP 服务器、bash 命令和任意进程的行为进行沙箱化。

Beta 研究预览版

Sandbox Runtime 是为 Claude Code 开发的研究预览版,旨在实现更安全的 AI 代理。它作为早期开源预览版发布,以帮助更广泛的生态系统构建更安全的代理系统。由于这是早期研究预览版,API 和配置格式可能会发生变化。我们欢迎反馈和贡献,以让 AI 代理默认更安全!

安装```bash

npm install -g @anthropic-ai/sandbox-runtime

## 基本用法```bash
# Network restrictions
$ srt "curl anthropic.com"
Running: curl anthropic.com
<html>...</html>  # Request succeeds

$ srt "curl example.com"
Running: curl example.com
Connection blocked by network allowlist  # Request blocked

# Filesystem restrictions
$ srt "cat README.md"
Running: cat README.md
# Anthropic Sandb...  # Current directory access allowed

$ srt "cat ~/.ssh/id_rsa"
Running: cat ~/.ssh/id_rsa
cat: /Users/ollie/.ssh/id_rsa: Operation not permitted  # Specific file blocked

概述

此包提供了一个独立的沙箱实现,既可作为 CLI 工具使用,也可作为库使用。它采用默认安全的设计理念,针对常见的开发者使用场景量身定制:进程以最小权限启动,您只需显式地打开所需的缺口。

核心能力:

  • 网络限制:控制可通过 HTTP/HTTPS 及其他协议访问的主机/域名
  • 文件系统限制:控制可读取/写入的文件/目录
  • Unix 套接字限制:控制对本地 IPC 套接字的访问
  • 违规监控:在 macOS 上,接入系统的沙箱违规日志存储以获取实时警报

使用案例:沙箱化 MCP 服务器

一个关键用例是对模型上下文协议(MCP)服务器进行沙箱化,以限制其能力。例如,要对文件系统 MCP 服务器进行沙箱化:

不使用沙箱.mcp.json):```json { "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem"] } } }

**使用沙箱**(`.mcp.json`):```json
{
  "mcpServers": {
    "filesystem": {
      "command": "srt",
      "args": ["npx", "-y", "@modelcontextprotocol/server-filesystem"]
    }
  }
}

然后在 ~/.srt-settings.json 中配置限制:```json { "filesystem": { "denyRead": [], "allowWrite": ["."], "denyWrite": ["~/sensitive-folder"] }, "network": { "allowedDomains": [], "deniedDomains": [] } }

现在 MCP 服务器将被阻止写入被拒绝的路径:```
> Write a file to ~/sensitive-folder
✗ Error: EPERM: operation not permitted, open '/Users/ollie/sensitive-folder/test.txt'

工作原理

该沙箱使用操作系统级原语来强制执行适用于整个进程树的限制:

  • macOS:使用 sandbox-exec 配合动态生成的 Seatbelt 配置文件
  • Linux:使用 bubblewrap 进行容器化,并实现网络命名空间隔离
  • Windows:在专用的 srt-sandbox 本地用户账户下运行沙箱进程,并使用以该账户的 SID 为键的 Windows 筛选平台 出站防护,以及在工作树上的每会话显式 ACE

0d1c612947c798aef48e6ab4beb7e8544da9d41a-4096x2305

双重隔离模型

有效的沙箱化需要同时实现文件系统和网络隔离。如果没有文件隔离,被攻陷的进程可能会窃取 SSH 密钥或其他敏感文件。如果没有网络隔离,进程可能逃逸沙箱并获得不受限制的网络访问。

文件系统隔离 强制执行读和写限制:

  • (先拒绝后允许模式):默认情况下,允许在所有位置进行读取访问。你可以拒绝大范围区域(例如 /Users),然后重新允许其中的特定路径(例如 .)。allowRead 优先于 denyRead——这与写操作相反,在写操作中 denyWrite 优先于 allowWrite。比其所在的 allowRead 区域更具体的 denyRead 条目(例如 denyRead: ["**/.env"]["./secrets"] 配合 allowRead: ["."])仍然保持拒绝状态。
  • (仅允许模式):默认情况下,拒绝在所有位置进行写入访问。你必须显式允许路径(例如 ./tmp)。空的允许列表意味着没有写入访问权限。

网络隔离(仅允许模式):默认情况下,拒绝所有网络访问。你必须显式允许域名。空的 allowedDomains 列表意味着没有网络访问权限。网络流量通过运行在主机上的代理服务器进行路由:

  • Linux:请求通过 Unix 域套接字经由文件系统进行路由。沙箱进程的网络命名空间被完全移除,因此所有网络流量必须通过运行在主机上的代理(监听绑定挂载到沙箱中的 Unix 套接字)

  • macOS:Seatbelt 配置文件仅允许与特定 localhost 端口通信。代理监听此端口,为所有网络访问创建受控通道

  • Windows:机器范围的 WFP 筛选器集阻止源自 srt-sandbox 账户的所有出站连接,但到代理端口范围的环回连接除外。代理在该范围内监听,为所有网络访问创建受控通道

HTTP/HTTPS(通过 HTTP 代理)和其他 TCP 流量(通过 SOCKS5 代理)均由这些代理进行中介,这些代理强制执行你的域名允许列表和拒绝列表。

有关 Claude Code 中沙箱化的更多详细信息,请参阅:

架构```

src/ ├── index.ts # Library exports ├── cli.ts # CLI entrypoint (srt command) ├── utils/ # Shared utilities │ ├── debug.ts # Debug logging │ ├── settings.ts # Settings reader (permissions + sandbox config) │ ├── platform.ts # Platform detection │ └── exec.ts # Command execution utilities └── sandbox/ # Sandbox implementation ├── sandbox-manager.ts # Main sandbox manager ├── sandbox-schemas.ts # Zod schemas for validation ├── sandbox-violation-store.ts # Violation tracking ├── sandbox-utils.ts # Shared sandbox utilities ├── http-proxy.ts # HTTP/HTTPS proxy for network filtering ├── socks-proxy.ts # SOCKS5 proxy for network filtering ├── linux-sandbox-utils.ts # Linux bubblewrap sandboxing ├── macos-sandbox-utils.ts # macOS sandbox-exec sandboxing └── windows-sandbox-utils.ts # Windows srt-win sandboxing

## 用法

### 作为 CLI 工具

`srt` 命令(Anthropic Sandbox Runtime)用安全边界包装任意命令:```bash
# Run a command in the sandbox
srt echo "hello world"

# With debug logging
srt --debug curl https://example.com

# Specify custom settings file
srt --settings /path/to/srt-settings.json npm install

作为库使用```typescript

import { SandboxManager, type SandboxRuntimeConfig, } from '@anthropic-ai/sandbox-runtime' import { spawn } from 'child_process'

// Define your sandbox configuration const config: SandboxRuntimeConfig = { network: { allowedDomains: ['example.com', 'api.github.com'], deniedDomains: [], }, filesystem: { denyRead: ['~/.ssh'], allowWrite: ['.', '/tmp'], denyWrite: ['.env'], }, }

// Initialize the sandbox (starts proxy servers, etc.) await SandboxManager.initialize(config)

// Wrap a command with sandbox restrictions const sandboxedCommand = await SandboxManager.wrapWithSandbox( 'curl https://example.com', )

// Execute the sandboxed command const child = spawn(sandboxedCommand, { shell: true, stdio: 'inherit' })

// Handle exit and cleanup after child process completes child.on('exit', async code => { console.log(Command exited with code ${code}) // Cleanup when done (optional, happens automatically on process exit) await SandboxManager.reset() })

**违规归因(`commandId` / `commandText`)。** 在包装命令运行期间观察到的违规(seatbelt 日志行、seccomp 事件、代理拒绝)会存储在一个归因键下,而 `annotateStderrWithSandboxFailures(key, stderr)` / `getViolationsForCommand(key)` 会通过同一个键来查找它们。默认情况下,该键就是被包装的字符串本身。传入一个不透明的每次调用 `commandId`(例如工具使用 id)来以此作为键——推荐这样做:键会比较其前 100 个字符,因此共享前缀的长命令否则会交叉归因,并且相同文本的重新运行会继承先前运行的事件。如果你*执行*的字符串不是该调用*所代表*的命令(例如你包装了一个组装好的 `source <snapshot> && eval '<cmd>'`),还要传入 `commandText: '<cmd>'`:它是 `ignoreViolations` 命令模式所匹配的对象,也是每个违规报告为其 `command` 的内容。```typescript
const wrapped = await SandboxManager.wrapWithSandbox(
  assembledCommand, // what actually runs
  undefined,
  undefined,
  undefined,
  { commandId: invocationId, commandText: rawCommand },
)
// ... run it ...
const annotated = SandboxManager.annotateStderrWithSandboxFailures(invocationId, stderr)

可用的导出```typescript

// Main sandbox manager export { SandboxManager } from '@anthropic-ai/sandbox-runtime'

// Violation tracking export { SandboxViolationStore } from '@anthropic-ai/sandbox-runtime'

// TypeScript types export type { SandboxRuntimeConfig, NetworkConfig, FilesystemConfig, IgnoreViolationsConfig, SandboxAskCallback, FsReadRestrictionConfig, FsWriteRestrictionConfig, NetworkRestrictionConfig, } from '@anthropic-ai/sandbox-runtime'

## 配置

### 设置文件位置

默认情况下,沙箱运行时会在 `~/.srt-settings.json` 查找配置。你可以使用 `--settings` 标志指定自定义路径:```bash
srt --settings /path/to/srt-settings.json <command>

完整配置示例```json

{ "network": { "allowedDomains": [ "github.com", ".github.com", "lfs.github.com", "api.github.com", "npmjs.org", ".npmjs.org" ], "deniedDomains": ["malicious.com"], "allowUnixSockets": ["/var/run/docker.sock"], "allowLocalBinding": false }, "filesystem": { "denyRead": ["~/.ssh"], "allowRead": [], "allowWrite": [".", "src/", "test/", "/tmp"], "denyWrite": [".env", "config/production.json"] }, "ignoreViolations": { "*": ["/usr/bin", "/System"], "git push": ["/usr/bin/nc"], "npm": ["/private/tmp"] }, "enableWeakerNestedSandbox": false, "enableWeakerNetworkIsolation": false, "allowAppleEvents": false }

### 配置选项

#### 网络配置

采用**仅允许模式**——默认拒绝所有网络访问。

- `network.allowedDomains` - 允许的域名数组(支持 `*.example.com` 等通配符)。空数组 = 无网络访问。可选的 `:port` 后缀(`api.example.com:443`、`*.example.com:8443`)将该条目限制到该目标端口;不带端口的条目匹配任意端口。
  - IPv6 字面量必须按 RFC 3986 风格加方括号:`[::1]`、`[2001:db8::1]:443`。未加方括号的多冒号条目会因歧义被拒绝(`2001:db8::1:443` 本身就是一个合法地址)。
- `network.deniedDomains` - 拒绝的域名数组(先检查,优先于 allowedDomains)。同样支持 `:port` 后缀,并且接受裸 `*`(或 `*:22`)表示全部拒绝。
- `network.deniedDomainReasons` - 可选的映射,从 `deniedDomains` 条目(按精确字符串匹配)到面向模型的理由,当该条目拒绝连接时,该理由会出现在 `<sandbox_violations>` 行中——说明被阻止的内容以及被认可的替代方案(例如 `{"github.com:22": "SSH pushes to GitHub are blocked; use an https:// remote"}`)。没有理由的条目会报告一个通用理由。对于 SSH 目标(端口 22),理由还会带内传递:通过无认证 SOCKS ProxyCommand(例如 BSD `nc -X 5`)隧道的 SSH 客户端会收到一个密钥交换前的 SSH 断开连接,其描述即为该理由,OpenSSH 会原样打印——请将此类理由保持在约 400 个 ASCII 字符以内,以祈使句开头,因为 OpenSSH 会截断并转义非 ASCII 字符。
- `network.allowLocalBinding` - 允许绑定到本地端口(布尔值,默认:false)

**解析地址检查。** 允许/拒绝列表按_名称_匹配,但控制某个被允许名称的 DNS(或被允许通配符下的任何标签)的人,就控制着它解析到什么。因此,在直接拨号一个被允许的**主机名**之前,代理会解析它一次,丢弃拒绝集合中的任何地址,并连接到存活的地址(通过检查的地址就是被拨号的地址——不会进行第二次查找)。如果没有任何地址存活,连接会像任何其他策略拒绝一样被拒绝:HTTP/CONNECT 得到 `403`(`X-Proxy-Error: blocked-by-sandbox-runtime`,理由在响应体中),SOCKS 得到 "connection not allowed by ruleset",并且一条 `deny network-outbound host:port (resolved to a loopback address)` 行——指明地址类别(loopback、link-local、this host's、cloud metadata、deny-listed、listed、……),而不是地址本身,地址本身只出现在调试日志中——会被记录到违规存储中。

拒绝集合为:loopback(`127.0.0.0/8`、`::1`)、unspecified(`0.0.0.0/8`、`::`)、link-local(`169.254.0.0/16`、`fe80::/10`)、multicast(`224.0.0.0/4`、`ff00::/8`)、broadcast、位于 link-local 之外的云实例元数据 / 平台端点(`100.100.100.200`、`168.63.129.16`、`192.0.0.192`、`fd00:ec2::/32`、`fd20:ce::254`、`fd00:c1::a9fe:a9fe`、`fd00:42::42`)、当前分配给本机某个网络接口的每个地址(绑定到 `0.0.0.0` 的服务在 LAN 或全局地址上的应答与在 loopback 上完全相同)、`deniedDomains` 中列出的每个 IP 字面量(如果有 `:port` 则遵循它),以及 `deniedResolvedAddresses` 中的任何内容。IPv4 条目还会匹配携带 IPv4 地址的 IPv6 形式——IPv4-mapped、IPv4-compatible 和 IPv4-translated 地址、NAT64 知名前缀(`64:ff9b::/96`)和 6to4(`2002::/16`)都按其嵌入的 IPv4 地址来判定。本地使用的 NAT64 前缀 `64:ff9b:1::/48` 和网络特定前缀不会被解码——它们的布局(RFC 6052 允许 IPv4 出现在多个位置)无法仅从地址本身识别;在这样的网络上,请列出该前缀对你所拒绝范围的转换形式(例如 `<prefix>::a00:0/104` 对应 `10.0.0.0/8`)。到达本机但未分配给它的地址——云实例的 1:1-NAT 公网地址、路由器端口转发、容器或 VM 主机网关别名——不会自动覆盖;请将它们列入 `deniedResolvedAddresses`。

该检查不涉及的内容:**是** IP 字面量的允许列表条目(将 `127.0.0.1:3000` 加入允许列表是一个明确的选择)——同样地,当该 IP 字面量(在该端口上)本身位于 `allowedDomains` 中时,主机名可以解析到原本被拒绝的地址,因为通过名称访问它并不会授予字面量条目所没有的权限(`deniedDomains` 中的 IP 字面量仍然优先,正如它对字面量请求一样)。因此,在 `myapp.test` 通过 `/etc/hosts` 映射到本地服务器的开发环境中,允许列表为 `["myapp.test", "127.0.0.1:3000"]`;不存在单独的特例列表。`localhost` 和 `.localhost` 下的名称解析到 loopback(或允许列表中的字面量),别无其他。对于通过 `parentProxy`(包括从 srt 自身环境中的 `HTTP_PROXY` / `HTTPS_PROXY` 获取的代理)或 `mitmProxy` 路由的连接,不会评估该检查——那一跳负责解析名称并拥有自己的地址策略——并且它只约束代理拨号的内容:在 macOS 上,`allowLocalBinding` 单独允许沙箱进程连接到 loopback 端口,而完全不经过代理。

- `network.deniedResolvedAddresses` - 额外的 IP 地址 / CIDR 范围(IPv4 或 IPv6,不加方括号,任意端口),被允许的主机名不得解析到这些地址。默认不拒绝私有使用空间,因为将内网主机名加入允许列表是合法的;当被允许的名称必须避开它时,请在此列出,例如 `["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10", "fc00::/7"]`。请分别列出 IPv4 和 IPv6 范围——一个宽到足以覆盖 IPv4-mapped 块(`::ffff:0:0/96`)的 IPv6 范围,例如 `::/0`,在某些运行时上会匹配 IPv4 应答,但在其他运行时上不会,因此不要依赖它来拒绝 IPv4。

**TLS 终止**(`network.tlsTerminate`,实验性):设置后,HTTPS CONNECT 会在进程内终止,以便 SRT 能够看到(并通过 `network.filterRequest` 过滤)解密后的请求。沙箱进程被指向一个信任包,其中包含 MITM CA(`caCertPath`/`caKeyPath`,如果省略则为临时 CA)以及主机的常规根证书,因此代理签发的证书和真实上游证书都能通过验证。

- `network.tlsTerminate.excludeDomains` - **不**终止的域名模式(语法与 `allowedDomains` 相同)。匹配的 CONNECT 改为不透明隧道:它们仍受域名允许列表约束,但沙箱内的客户端与真实上游完成自己的 TLS 握手,并且 `filterRequest` / 凭据注入不适用于其 HTTPS 流量。用于 TLS 终止从根本上会破坏的两种情况:
  - **mTLS 上游** - 只有沙箱内客户端持有客户端证书,因此代理无法代表它重新发起连接。
  - **证书固定客户端** - 自行验证上游身份(自定义 CA、SAN 固定)并拒绝 MITM 证书的客户端。
- `network.tlsTerminate.extraCaCertPaths` - 追加到该信任包的 PEM CA 证书文件路径,位于 MITM CA 和主机常规根证书之后。被排除(未终止)的主机由沙箱内的客户端验证,而 SRT 设置的信任环境变量(`SSL_CERT_FILE`、`GIT_SSL_CAINFO`、……)会_替换_每个工具自身的信任配置,因此站点本地根证书(例如内部 mTLS CA)必须位于该包中,否则这些主机永远无法通过验证。每个文件中只有 `CERTIFICATE` 块会被复制到该包中(其他任何内容,例如组合 PEM 中的私钥,绝不会暴露给沙箱);缺失、不可读或不包含 PEM `CERTIFICATE` 块的文件会被跳过,因此列出仅存在于某些主机上的路径是安全的。```json
{
  "network": {
    "allowedDomains": ["*.example.com", "internal-mtls.example.net"],
    "deniedDomains": [],
    "tlsTerminate": {
      "excludeDomains": ["internal-mtls.example.net"],
      "extraCaCertPaths": ["/etc/internal-mtls-roots.pem"]
    }
  }
}

Unix Socket 设置(平台特定行为):

设置macOSLinux
allowUnixSockets: string[]socket 路径的允许列表忽略(seccomp 无法按路径过滤)
allowAllUnixSockets: boolean允许所有 socket禁用 seccomp 阻止

Unix socket 在两个平台上默认被阻止

  • macOS:使用 allowUnixSockets 允许特定路径(例如 ["/var/run/docker.sock"]),或使用 allowAllUnixSockets: true 允许所有。
  • Linux:阻止使用 seccomp 过滤器(仅限 x64/arm64)。如果 seccomp 不可用,socket 将不受限制,并显示警告。使用 allowAllUnixSockets: true 显式禁用阻止。

文件系统配置

使用两种不同的模式:

读取限制(先拒绝后允许模式)- 默认允许所有读取:

  • filesystem.denyRead - 要拒绝读取访问的路径数组。空数组 = 完全读取访问。
  • filesystem.allowRead - 在拒绝区域内重新允许读取访问的路径数组(优先于 denyRead)。注意: 这与写入相反,写入时 denyWrite 优先于 allowWrite

写入限制(仅允许模式)- 默认拒绝所有写入:

  • filesystem.allowWrite - 要允许写入访问的路径数组。空数组 = 无写入访问。
  • filesystem.denyWrite - 在允许路径内拒绝写入访问的路径数组(优先于 allowWrite)

路径语法(macOS):

在 macOS 上,路径支持 git 风格的 glob 模式,类似于 .gitignore 语法:

  • * - 匹配除 / 外的任何字符(例如 *.ts 匹配 foo.ts 但不匹配 foo/bar.ts
  • ** - 匹配包括 / 在内的任何字符(例如 src/**/*.ts 匹配 src/ 中的所有 .ts 文件)
  • ? - 匹配除 / 外的任何单个字符(例如 file?.txt 匹配 file1.txt
  • [abc] - 匹配集合中的任何字符(例如 file[0-9].txt 匹配 file3.txt

示例:

  • "allowWrite": ["src/"] - 允许写入整个 src/ 目录
  • "allowWrite": ["src/**/*.ts"] - 允许写入 src/ 及子目录中的所有 .ts 文件
  • "denyRead": ["~/.ssh"] - 拒绝读取 SSH 目录
  • "denyRead": ["/Users"], "allowRead": ["."] - 拒绝读取整个 /Users,但重新允许当前目录
  • "denyWrite": [".env"] - 拒绝写入 .env 文件(即使当前目录被允许)

路径语法(Linux):

Linux 目前不支持 glob 匹配。 仅使用字面路径:

  • "allowWrite": ["src/"] - 允许写入 src/ 目录
  • "denyRead": ["/home/user/.ssh"] - 拒绝读取 SSH 目录
  • "denyRead": ["/home"], "allowRead": ["."] - 拒绝读取整个 /home,但重新允许当前目录

所有平台:

  • 路径可以是绝对路径(例如 /home/user/.ssh)或相对于当前工作目录的路径(例如 ./src
  • ~ 展开为用户的主目录

其他配置

  • ignoreViolations - 将命令模式映射到应忽略违规的路径数组的对象
  • enableWeakerNestedSandbox - 为 Docker 环境启用较弱的沙箱模式(布尔值,默认:false)
  • javaAgentJarPath - macOS/Linux:srt-proxy-agent.jar 的绝对路径,这是通过 JAVA_TOOL_OPTIONS 注入的 JVM agent(参见网络隔离下的 "JVM 工具")。仅由捆绑 sandbox-runtime 并单独提供 jar 的消费者需要;正常的 npm 安装会在 vendor/java-proxy-agent/ 下找到它。
  • enableWeakerNetworkIsolation - 允许在 macOS 沙箱中访问 com.apple.trustd.agent(布尔值,默认:false)。当使用 httpProxyPort 配合 MITM 代理和自定义 CA 时,Go 程序(ghgcloudterraformkubectl 等)需要此选项来验证 TLS 证书。安全警告: 启用此选项会通过 trustd 服务打开潜在的数据泄露途径。
  • allowAppleEvents - 允许从 macOS 沙箱发送 Apple Events 和 Launch Services 打开请求(布尔值,默认:false)。如果不启用,像 openosascript 以及任何通过 AppleScript 打开 URL 或脚本其他应用的命令都会失败,并出现 AppleScript 错误 -600("应用程序未运行")或 LaunchServices 错误(-10822-54)。安全警告: 启用此选项意味着沙箱不再提供代码执行隔离。沙箱化的命令可以通过 open 启动其他应用程序而无需用户提示,并且它启动的任何内容都在沙箱的文件系统和网络限制之外运行;通过 Apple Events 脚本化已在运行的应用还额外受用户按应用 TCC 自动化同意的限制。嵌入方应仅从受信任的用户级配置中获取此选项——绝不能从已检出仓库中的项目本地文件获取,否则攻击者编写的项目可能会提升其自身的沙箱权限。

常见配置方案

允许 GitHub 访问(所有必要的端点):```json { "network": { "allowedDomains": [ "github.com", "*.github.com", "lfs.github.com", "api.github.com" ], "deniedDomains": [] }, "filesystem": { "denyRead": [], "allowWrite": ["."], "denyWrite": [] } }

**限制到特定目录:**```json
{
  "network": {
    "allowedDomains": [],
    "deniedDomains": []
  },
  "filesystem": {
    "denyRead": ["~/.ssh"],
    "allowWrite": [".", "src/", "test/"],
    "denyWrite": [".env", "secrets/"]
  }
}

仅工作区文件系统访问(拒绝读取工作区之外的内容):```json { "network": { "allowedDomains": [], "deniedDomains": [] }, "filesystem": { "denyRead": ["/Users"], "allowRead": ["."], "allowWrite": ["."], "denyWrite": [] } }

这会拒绝读取 `/Users`(或 Linux 上的 `/home`)下的任何内容,然后重新允许当前工作目录。系统路径(`/usr`、`/lib` 等)保持可读。

### 常见问题与提示

**运行 Jest:** 使用 `--no-watchman` 标志以避免沙箱违规:```bash
srt "jest --no-watchman"

Watchman 会访问沙箱边界之外的文件,从而触发权限错误。禁用它可以让 Jest 使用内置的文件监视器运行。

平台支持

  • macOS:使用带有自定义配置文件的 sandbox-exec(无额外依赖)
  • Linux:使用 bubblewrap (bwrap) 进行容器化
  • Windows:Alpha 阶段 — 使用捆绑的 srt-win.exe 辅助程序(无额外依赖)。有关设置、安全模型和已知限制,请参阅下方的 Windows (alpha)

平台特定依赖

Linux 需要:

  • bubblewrap - 容器运行时
    • Ubuntu/Debian:apt-get install bubblewrap
    • Fedora:dnf install bubblewrap
    • Arch:pacman -S bubblewrap
  • socat - 用于代理桥接的套接字中继
    • Ubuntu/Debian:apt-get install socat
    • Fedora:dnf install socat
    • Arch:pacman -S socat
  • ripgrep - 用于拒绝路径检测的快速搜索工具
    • Ubuntu/Debian:apt-get install ripgrep
    • Fedora:dnf install ripgrep
    • Arch:pacman -S ripgrep

Ubuntu 24.04+ 注意事项: 这些版本默认启用 kernel.apparmor_restrict_unprivileged_userns,这允许 unshare(CLONE_NEWUSER) 但会剥离结果命名空间的能力。bubblewrap 和 seccomp 隔离层都需要具有能力的用户命名空间。使用以下命令禁用该限制:```bash sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

或者添加一个 AppArmor 配置文件,为相关二进制文件授予 `userns` 权限。

**可选的 Linux 依赖项(用于 seccomp 回退):**

该软件包包含针对 x86-64 和 arm 架构预生成的 seccomp BPF 过滤器。仅当您处于预生成过滤器不可用的其他架构时,才需要这些依赖项:

- `gcc` 或 `clang` - C 编译器
- `libseccomp-dev` - Seccomp 库开发文件
  - Ubuntu/Debian:`apt-get install gcc libseccomp-dev`
  - Fedora:`dnf install gcc libseccomp-devel`
  - Arch:`pacman -S gcc libseccomp`

**macOS 需要:**

- `ripgrep` - 用于拒绝路径检测的快速搜索工具
  - 通过 Homebrew 安装:`brew install ripgrep`
  - 或从以下地址下载:https://github.com/BurntSushi/ripgrep/releases

**Windows 需要:**

- 无额外依赖项。`srt-win.exe` 辅助程序(x64 和 arm64)已随 npm 包捆绑提供。需要执行一次性的提权 `windows-install` 步骤——见下文。

## Windows(alpha)

Windows 支持处于 **alpha** 阶段。沙箱化进程在专用的 `srt-sandbox` 本地用户账户下运行,通过原生 Windows 安全原语与调用用户隔离——一个以沙箱账户的 SID 为键的 Windows 筛选平台(WFP)出口围栏,以及按会话的显式 ACE,授予或拒绝该 SID 对已配置文件系统路径的访问。

### 设置

每台机器运行一次(自动提权;一次 UAC 提示):```powershell
npx @anthropic-ai/sandbox-runtime windows-install

这会创建 srt-sandbox 本地用户账户(其随机密码以 DPAPI 加密形式存储在 HKLM\SOFTWARE\sandbox-runtime 中——这是机器范围的,因此以 SYSTEM 身份运行的批量安装可以正常工作,且一个用户的轮换会更新其他用户读取的副本)、sandbox-runtime-users 本地组,并安装以 srt-sandbox SID 为键的机器范围 WFP 过滤器集。它是幂等的——重新运行会轮换沙箱账户的密码并协调过滤器集。

无需注销。 WFP 过滤器以专用沙箱账户的 SID 为键,因此你自己的网络、服务以及机器上的所有其他主体都不受影响。

安装后,SandboxManager.initialize()srt CLI 与其他平台上的工作方式相同。initialize() 会验证沙箱账户和 WFP 围栏是否处于活动状态,如果不是,则会以可操作的错误失败。

程序化安装/卸载导出为 installWindowsSandbox() / uninstallWindowsSandbox()

安全模型

沙箱化命令srt-sandbox 账户身份运行,而不是以调用用户身份运行。捆绑的 srt-win.exe 辅助程序执行两跳启动:代理调用 CreateProcessWithLogonWsrt-sandbox 身份启动运行器,运行器在作业对象内以受限令牌生成目标。子进程继承沙箱账户的隔离配置文件(%USERPROFILE%%TEMP%HKCU)以及一个仅覆盖代理的 PATH 和生成的代理变量的全新环境。

在不同用户 SID 下运行从结构上关闭了代理生成类逃逸(任务计划程序、将 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 指向代理拥有的进程、BITS、使用 RunAs="Interactive User" 的进程外 COM):子进程设法带外生成的任何进程仍然携带 srt-sandbox SID,因此它仍然受 WFP 出口围栏约束,并且对调用用户的文件没有权限。

网络隔离是在 FWPM_LAYER_ALE_AUTH_CONNECT_V4/V6 处的双过滤器 WFP 集:对配置的代理端口范围(默认 60080–60089)内的环回目标进行 PERMIT,以及对任何令牌携带 srt-sandbox SID 的连接进行 BLOCK。沙箱化进程只能通过监听该范围的 JS HTTP/SOCKS5 代理访问互联网;剥离其代理环境并直接连接的进程会在内核处被阻止。

文件系统隔离由 NTFS 自由访问控制列表(DACL)强制执行。srt-sandbox 账户对调用用户的文件没有固有权限,因此在 initialize() 时,沙箱写入仅针对 srt-sandbox SID 的附加、继承显式 ACE——它从不重写或替换路径的现有安全描述符:

  • filesystem.allowWrite → 一个继承的 MODIFY ALLOW ACE(READ|WRITE|EXECUTE|DELETE,并保留 FILE_DELETE_CHILD)。沙箱化进程可以在工作树内创建、修改和删除文件;从授权中保留 FILE_DELETE_CHILD 是对下面拒绝标记的纵深防御,而不是对树根的防护。
  • filesystem.allowRead → 一个继承的 READ|EXECUTE ALLOW ACE
  • filesystem.denyRead / filesystem.denyWrite → 对目标的一个继承 DENY ACE,以及对其父目录的一个继承 FILE_DELETE_CHILD DENY——结合工作树授权中保留的 FILE_DELETE_CHILD,这阻止沙箱化进程通过其父目录重命名或删除被拒绝的路径

reset() 移除本次会话添加的每个 ACE(通过每用户会话数据库在此用户的并发主机之间进行引用计数;下一次 initialize() 时的崩溃恢复过程会在非正常退出后进行清理)。支持目录目标(ACE 会继承到整个子树)。Glob 模式在 initialize() 时展开为具体路径——之后出现的匹配路径不会被覆盖。

Windows 上的 TLS 终止

network.tlsTerminate 要求 MITM CA 存在于沙箱用户的 CurrentUser\Root 证书存储中(schannel——System32\curl.exe、PowerShell Invoke-WebRequest、.NET 和默认后端的 git 使用的 TLS 后端——仅信任操作系统存储,而不信任环境变量)。这是一个安装时步骤,与 windows-install 分开:```typescript import { windowsTrustCa } from '@anthropic-ai/sandbox-runtime' windowsTrustCa('/path/to/mitm-ca.crt') // or: srt-win user trust-ca

`initialize()` 会将会话 CA 的指纹与已安装的指纹进行比较,并在不匹配时给出可操作的错误信息,因此过期的安装时 CA 不会在沙箱内静默破坏 TLS。

由 OpenSSL 支持的客户端(msys2 `curl`、`git -c http.sslBackend=openssl`、Node、Python、cargo)由环境变量信任层覆盖:在 macOS/Linux 上使用的同一信任包通过 `NODE_EXTRA_CA_CERTS`、`SSL_CERT_FILE`、`CURL_CA_BUNDLE`、`GIT_SSL_CAINFO`、`CARGO_HTTP_CAINFO` 等传入沙箱,并且该信任包路径会被添加到会话的 `allowRead` 授权中,以便沙箱账户可以打开它。

### Windows 特定配置

跨平台的 `filesystem` 和 `network` 块按上述方式适用。仅限 Windows 的设置位于 `windows` 下:

- `windows.proxyPortRange` — JS 代理在内部绑定的 `[low, high]` 闭区间端口范围。**必须匹配**传递给 `windows-install --proxy-port-range` 的范围(默认 `[60080, 60089]`)——WFP 环回 PERMIT 仅覆盖该范围。
- `windows.sublayerGuid` — 安装过滤器时所在的 WFP 子层 GUID。省略则使用编译时默认值;仅当企业工具在自定义子层下安装了过滤器时才设置。
- `windows.srtWin.path` — `srt-win` 二进制文件的路径。省略则解析打包的 `vendor/srt-win/<arch>/srt-win.exe`。当将 `srt-win` 的 CLI 嵌入到 multicall 二进制文件中时设置;此时 spawn 会传递 `--srt-win` 作为 `argv[1]`,以便嵌入方的调度器可以路由到 `srt_win::run_from_args`。

### 已知限制

- **schannel 下的证书吊销。** CryptoAPI 的 CRL/OCSP 获取通过 WinHTTP 在调用方令牌下发出,忽略代理环境,因此会被 WFP 出口围栏阻止。默认开启吊销检查并使用 schannel 的工具会失败并报 `CRYPT_E_REVOCATION_OFFLINE`(`0x80092013`),除非按工具禁用吊销检查:`curl --ssl-no-revoke`、`git -c http.schannelCheckRevoke=false`、`CARGO_HTTP_CHECK_REVOKE=false`。`Invoke-WebRequest`、.NET `HttpClient` 和 `gh` 默认不检查吊销,因此不受影响。计划通过由环回代理提供 CRL 分发点来移除这一变通方法。
- **按用户安装的工具不可访问。** 沙箱进程以 `srt-sandbox` 身份运行,而不是你的身份,因此安装在你配置文件下的工具(nvm/fnm 管理的 Node、按用户的 `winget`/Scoop 包、`pip install --user`、`%LOCALAPPDATA%\Programs\…`)会在继承的 `PATH` 上解析,但沙箱账户无法打开它们。优先使用机器范围的安装(`Program Files`、`choco`/`winget --scope machine`),或将特定的配置文件路径添加到 `filesystem.allowRead`。
- **不支持按执行的 `filesystem.allowRead` / `filesystem.allowWrite` 覆盖。** 会话级 `allowRead`/`allowWrite`(在传递给 `initialize()` 的配置中)按上述方式工作;在 `wrapWithSandbox` 的 `customConfig` 中按命令传递它们会抛出异常——授权在 `initialize()` 时通过 `srt-win acl grant` 应用于整个会话,而 `srt-win exec` 仅暴露按执行的拒绝。
- **`proxyAuthToken` 在运行器的命令行中可见。** 代理环境(包括 `HTTP_PROXY=http://srt:<token>@127.0.0.1:…`)作为 `--env` 参数传递给两跳运行器,出现在 `srt-win exec` 的 argv 中,因此任何能够以 `PROCESS_QUERY_LIMITED_INFORMATION` 打开运行器进程的本地主体都可以读取该令牌。该令牌的存在是为了让沙箱进程能够向环回代理进行身份验证,因此它对沙箱本身并非秘密;在单用户开发机器上这通常可以接受,但在共享主机上应将代理允许列表视为同一会话中其他主体可访问。
- **通过系统解析器的 DNS 解析未被围栏。** `getaddrinfo()` 由以 `NETWORK SERVICE` 运行的 `Dnscache` 服务处理,因此即使来自沙箱进程的后续 `connect()` 被阻止,名称解析仍然成功。自行进行 UDP/53 查询的工具(`nslookup`、`dig`)会被围栏。这与 macOS 的行为一致。

### 卸载```powershell
npx @anthropic-ai/sandbox-runtime windows-uninstall

移除 WFP 筛选器集、srt-sandbox 账户及其配置文件、sandbox-runtime-users 组,并删除 HKLM\SOFTWARE\sandbox-runtime 注册表项(凭据、标记、CA 记录)——一次 UAC 提示。%ProgramData%\sandbox-runtime(CA 密钥材料)保留在原处;如需彻底清理,请手动删除它(以及每个用户的 %LOCALAPPDATA%\sandbox-runtime)。

开发```bash

Install dependencies

npm install

Build the project

npm run build

Run tests

npm test

Type checking

npm run typecheck

Lint code

npm run lint

Format code

npm run format

### 构建 Seccomp 二进制文件

BPF 过滤器和 `apply-seccomp` 加载器通过 `npm run build:seccomp` 从 `vendor/seccomp-src/` 中的 C 源代码编译(仅限 Linux;需要 `gcc` 和 `libseccomp-dev`)。CI 在每个 Linux 架构上于测试前运行它,发布工作流会构建两个架构并将它们打包到发布的包中。

## 实现细节

### 网络隔离架构

沙箱在主机上运行 HTTP 和 SOCKS5 代理服务器,根据权限规则过滤所有网络请求:

1. **HTTP/HTTPS 流量**:HTTP 代理服务器拦截请求并根据允许/拒绝的域名进行验证
2. **其他网络流量**:SOCKS5 代理处理所有其他 TCP 连接(SSH、数据库连接等)
3. **权限执行**:代理强制执行配置中的 `permissions` 规则

**平台特定的代理通信:**

- **Linux**:请求通过 Unix 域套接字经由文件系统路由(使用 `socat` 进行桥接)。网络命名空间从 bubblewrap 容器中移除,确保所有网络流量必须经过代理。

- **macOS**:Seatbelt 配置文件仅允许与代理监听的特定 localhost 端口通信。所有其他网络访问均被阻止。

- **Windows**:WFP `ALE_AUTH_CONNECT` 过滤器阻止来自 `srt-sandbox` 账户的每个出站连接,除了到配置的代理端口范围的环回连接。代理绑定在该范围内。环境变量(`HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY` 等)将工具指向代理,但 WFP 过滤器才是边界——忽略或取消设置这些变量的进程仍然被隔离。

**JVM 工具(macOS/Linux):** JVM 忽略 `HTTPS_PROXY`/`NO_PROXY`,并且没有用于代理凭据的环境变量——代理选择来自 `https.proxyHost` 系统属性,凭据只能通过 `java.net.Authenticator` 提供。因此基于 JVM 的工具(Bazel 的 gRPC 远程缓存、Gradle、Maven 等)否则会直接连接目标并失败,或者在没有令牌的情况下到达代理并收到 407。为了弥补这一差距,srt 通过 `JAVA_TOOL_OPTIONS` 注入一个小的 `-javaagent`(环境变量仅携带 jar 路径,凭据保留在 `HTTPS_PROXY` 中)。在 JVM 启动时,代理从代理环境变量设置 `http[s].proxyHost`/`Port` 和 `http.nonProxyHosts`,为 CONNECT 隧道重新启用 Basic 认证,并为代理端点安装 Authenticator。JVM 命令行上显式的 `-D` 代理属性仍然优先,任何继承的 `JAVA_TOOL_OPTIONS` 都会被保留(除非它是被拒绝的凭据环境变量)。因此每个 JVM 都会向 stderr 打印一行 `Picked up JAVA_TOOL_OPTIONS: …`;在没有 `java.instrument` 模块的情况下构建的 jlink'd 运行时无法加载代理,并将在沙箱下拒绝启动——对于此类工具,请在命令中取消设置 `JAVA_TOOL_OPTIONS`。该 jar 作为 `vendor/java-proxy-agent/srt-proxy-agent.jar` 随 npm 包发布(源代码:`vendor/java-proxy-agent-src/`;由发布工作流构建,或在本地使用 `npm run build:java-agent` 构建——需要 JDK ≥ 17)。如果未找到它,`JAVA_TOOL_OPTIONS` 将保持不变,JVM 的行为与之前相同;打包工具可以通过 `javaAgentJarPath` 指向自己的副本。

### 文件系统隔离

文件系统限制在操作系统级别强制执行:

- **macOS**:使用 `sandbox-exec` 和动态生成的 Seatbelt 配置文件,指定允许的读/写路径
- **Linux**:使用 `bubblewrap` 和绑定挂载,根据配置将目录标记为只读或读写
- **Windows**:将附加的 `(OI)(CI)` 显式 ACE 写入配置路径上的 `srt-sandbox` SID(在 `allowRead`/`allowWrite` 上为 ALLOW,在 `denyRead`/`denyWrite` 上为 DENY),然后在 `reset()` 时移除它们

**默认文件系统权限:**

- **读取**(先拒绝后允许):默认在所有位置允许。你可以拒绝大范围区域,然后重新允许其中的特定路径。`allowRead` 优先于 `denyRead`。

  - 示例:`denyRead: ["~/.ssh"]` 阻止对 SSH 密钥的访问
  - 示例:`denyRead: ["/Users"], allowRead: ["."]` 阻止整个 `/Users` 除了工作区
  - 空的 `denyRead: []` = 完全读取访问(不拒绝任何内容)

- **写入**(仅允许):默认在所有位置拒绝。你必须显式允许路径。
  - 示例:`allowWrite: [".", "/tmp"]` 允许写入当前目录和 /tmp
  - 空的 `allowWrite: []` = 无写入访问(不允许任何内容)
  - `denyWrite` 在允许的路径内创建例外(拒绝优先)

**读取与写入的优先级故意相反:** `allowRead` 覆盖 `denyRead`,而 `denyWrite` 覆盖 `allowWrite`。这让你可以在被拒绝的区域中划出可读区域,并在可写区域中划出受保护区域。

### 强制拒绝路径(自动保护文件)

某些敏感文件和目录**始终被阻止写入**,即使它们位于允许的写入路径内。这提供了针对沙箱逃逸和配置篡改的纵深防御。

**始终阻止的文件:**

- Shell 配置文件:`.bashrc`、`.bash_profile`、`.zshrc`、`.zprofile`、`.profile`
- Git 配置文件:`.gitconfig`、`.gitmodules`
- 其他敏感文件:`.ripgreprc`、`.mcp.json`

**始终阻止的目录:**

- IDE 目录:`.vscode/`、`.idea/`
- Claude 配置目录:`.claude/commands/`、`.claude/agents/`
- Git 钩子和配置:`.git/hooks/`、`.git/config`

这些路径会被自动阻止——你不需要将它们添加到 `denyWrite`。例如,即使使用 `allowWrite: ["."]`,写入 `.bashrc` 或 `.git/hooks/pre-commit` 也会失败:```bash
$ srt 'echo "malicious" >> .bashrc'
/bin/bash: .bashrc: Operation not permitted

$ srt 'echo "bad" > .git/hooks/pre-commit'
/bin/bash: .git/hooks/pre-commit: Operation not permitted

注意(Linux): 在 Linux 上,强制拒绝路径仅阻止已存在的文件。这些模式中不存在的文件无法通过 bubblewrap 的绑定挂载方式阻止。macOS 使用 glob 模式,可同时阻止已存在和新创建的文件。

Linux 搜索深度: 在 Linux 上,沙箱使用 ripgrep 扫描允许写入路径的子目录中的危险文件。默认情况下,为提升性能,搜索深度最多为 3 层。你可以通过 mandatoryDenySearchDepth 进行配置:```json { "mandatoryDenySearchDepth": 5, "filesystem": { "allowWrite": ["."] } }

- 默认值:`3`(最多搜索 3 层深度)
- 范围:`1` 到 `10`
- 值越高提供的保护越多,但性能越慢
- 无论此设置如何,CWD(深度 0)中的文件始终受到保护

### Unix 套接字限制(Linux)

在 Linux 上,沙箱使用 **seccomp BPF(Berkeley Packet Filter)** 在系统调用层面阻止 Unix 域套接字的创建。这提供了一层额外的安全保护,防止进程为本地 IPC 创建新的 Unix 域套接字(除非明确允许)。

**工作原理:**

1. **内置 BPF 过滤器**:该软件包附带一个静态的 `apply-seccomp` 二进制文件,适用于 x64 和 arm64,其中编译了 seccomp BPF 过滤器。该过滤器是特定于架构的,但与 libc 无关,因此该二进制文件可同时用于 glibc 和 musl。

2. **运行时检测**:沙箱会自动检测你系统的架构,并使用匹配的 `apply-seccomp` 二进制文件。

3. **系统调用过滤**:BPF 过滤器拦截 `socket()` 系统调用,并通过返回 `EPERM` 来阻止创建 `AF_UNIX` 套接字。这可以防止沙箱代码创建新的 Unix 域套接字。

4. **使用 apply-seccomp 二进制文件的两阶段应用**:
   - 外层 bwrap 创建具有文件系统、网络和 PID 命名空间限制的沙箱
   - 网络桥接进程(socat)在沙箱内启动(需要 Unix 套接字)
   - apply-seccomp 创建一个嵌套的用户 + PID + 挂载命名空间,并重新挂载 `/proc`
   - 在嵌套命名空间内,apply-seccomp 充当 PID 1(不可转储的 init/reaper)
   - apply-seccomp 进行 fork,通过 `prctl()` 应用 seccomp 过滤器,并执行用户命令
   - 用户命令在具有所有沙箱限制以及 Unix 套接字创建阻止的情况下运行

**PID 命名空间隔离**:嵌套的 PID 命名空间确保用户命令无法看到或寻址任何在未应用 seccomp 过滤器的情况下运行的进程(bwrap 的 init、shell 包装器或 socat 辅助进程)。无论 `kernel.yama.ptrace_scope` 如何,这都能保持 seccomp 边界完整,因为未过滤的辅助进程无法通过 `ptrace` 或 `/proc/N/mem` 访问。内部 PID 1 设置 `PR_SET_DUMPABLE=0`,因此它也无法被 ptrace。如果嵌套命名空间创建失败,apply-seccomp 会中止,而不是在没有隔离的情况下运行。

**安全限制**:该过滤器阻止 `socket(AF_UNIX, ...)` 以及 `io_uring_setup`/`io_uring_enter`/`io_uring_register` 系统调用(后三者是因为在 Linux 5.19+ 上 `IORING_OP_SOCKET` 否则会绕过 `socket()` 规则)。它不会阻止对从父进程继承或通过 `SCM_RIGHTS` 传递的 Unix 套接字文件描述符的操作。对于大多数沙箱场景,阻止套接字创建足以防止未经授权的 IPC。

**零运行时依赖**:为 x64 和 arm64 架构包含了预构建的静态 apply-seccomp 二进制文件和预生成的 BPF 过滤器。运行时不需要编译工具或外部依赖。

**架构支持**:x64 和 arm64 通过预构建的二进制文件得到完全支持。目前不支持其他架构。要在不支持的架构上使用沙箱而不阻止 Unix 套接字,请在配置中设置 `allowAllUnixSockets: true`。

### 违规检测与监控

当沙箱进程尝试访问受限资源时:

1. **在操作系统层面阻止该操作**(返回 `EPERM` 错误)
2. **记录违规**(平台特定机制)
3. **通知用户**(在 Claude Code 中,这会触发权限提示)

**macOS**:沙箱运行时接入 macOS 的系统沙箱违规日志存储。这提供了实时通知,其中包含有关尝试了什么以及为什么被阻止的详细信息。这与 Claude Code 用于违规检测的机制相同。```bash
# View sandbox violations in real-time
log stream --predicate 'process == "sandbox-exec"' --style syslog

Linux:Bubblewrap 不提供内置的违规报告。使用 strace 跟踪系统调用并识别被阻止的操作:```bash

Trace all denied operations

strace -f srt 2>&1 | grep EPERM

Trace specific file operations

strace -f -e trace=open,openat,stat,access srt 2>&1 | grep EPERM

Trace network operations

strace -f -e trace=network srt 2>&1 | grep EPERM

### 高级:自带代理

对于更复杂的网络过滤,你可以配置沙箱使用你自己的代理,而不是内置代理。这可以实现:

- **流量检查**:使用 [mitmproxy](https://mitmproxy.org/) 等工具检查和修改流量
- **自定义过滤逻辑**:实现超越简单域名允许列表的复杂规则
- **审计日志**:记录所有网络请求以满足合规或调试需求

**使用 mitmproxy 的示例:**```bash
# Start mitmproxy with custom filtering script
mitmproxy -s custom_filter.py --listen-port 8888

注意:新配置格式尚不支持自定义代理配置。此功能将在未来版本中添加。

重要的安全考虑: 即使使用域名允许列表,也可能存在数据外泄途径。例如,允许 github.com 会让进程能够推送到任意仓库。通过自定义 MITM 代理和适当的证书设置,你可以检查并过滤特定的 API 调用以防止这种情况。

安全限制

  • 网络沙箱限制:网络过滤系统通过限制进程允许连接的域名来运作。它不会以其他方式检查通过代理的流量,用户有责任确保其策略中只允许受信任的域名。允许的主机名还会在直接拨号前根据一组被拒绝的已解析地址进行检查(参见上文 已解析地址检查),因此被允许的名称无法指向回环地址、链路本地地址、本机自身地址或你在 deniedDomains 中列出的 IP;其他私有地址范围只有在你将其列入 deniedResolvedAddresses 时才会被覆盖(否则,对你不控制其 DNS 的域名使用通配符条目可能会被指向你局域网上的服务),而通过 parentProxy/mitmProxy 离开的连接则依赖该跳点进行等效检查。
用户应意识到允许像 `github.com` 这样的宽泛域名可能带来的潜在风险,因为这可能允许数据外泄。此外,在某些情况下,可能通过[域名前置](https://en.wikipedia.org/wiki/Domain_fronting)绕过网络过滤。
  • 通过 Unix 套接字提权:allowUnixSockets 配置可能会无意中授予对强大系统服务的访问权限,从而导致沙箱绕过。例如,如果用它来允许访问 /var/run/docker.sock,这实际上会通过利用 docker 套接字授予对宿主系统的访问权限。建议用户仔细考虑他们允许通过沙箱的任何 unix 套接字。
  • 文件系统权限提升:过于宽泛的文件系统写权限可能引发提权攻击。允许写入 $PATH 中包含可执行文件的目录、系统配置目录或用户 shell 配置文件(.bashrc.zshrc)可能导致在其他用户或系统进程访问这些文件时,在不同安全上下文中执行代码。
  • Linux 沙箱强度:Linux 实现提供了强大的文件系统和网络隔离,但包含一个 enableWeakerNestedSandbox 模式,使其能够在没有特权命名空间的 Docker 环境中工作。此选项会显著削弱安全性,只应在另有额外隔离措施的情况下使用。
  • 较弱的网络隔离(macOS):enableWeakerNetworkIsolation 选项重新启用了对 com.apple.trustd.agent 的访问,这是 Go 程序通过 macOS Security 框架验证 TLS 证书所需的。这会通过 trustd 服务打开一个潜在的数据外泄途径,只应在需要 Go TLS 验证时启用(例如,在使用 httpProxyPort 配合 MITM 代理和自定义 CA 时)。
  • Apple Events(macOS):allowAppleEvents 选项重新启用了发送 Apple Events 和 Launch Services 打开请求((allow appleevent-send)(allow lsopen),以及对 com.apple.coreservices.appleeventscom.apple.CoreServices.coreservicesdcom.apple.coreservices.quarantine-resolver 的 mach-lookups),openosascript 和 URL 打开辅助工具需要这些。允许这些后,沙箱内的命令可以在没有用户提示的情况下启动任意应用程序,而且启动的应用程序完全在沙箱之外运行——因此此选项是移除了代码执行隔离,而不仅仅是削弱它。通过 Apple Events 对已运行应用程序进行脚本编写还额外受 macOS TCC 自动化同意的限制,但通过 open 启动则不受此限制。只有在沙箱内的命令确实需要打开 URL 或应用程序时才启用此选项。

已知限制和未来工作

Linux 代理绕过:目前使用环境变量(HTTP_PROXYHTTPS_PROXYALL_PROXY)将流量导向代理。这对大多数应用程序有效,但可能被不遵守这些变量的程序忽略,导致它们无法连接到互联网。

未来改进:

  • Proxychains 支持:在 Linux 上添加对 proxychains 配合 LD_PRELOAD 的支持,以在更低层级拦截网络调用,使绕过更加困难

  • Linux 违规监控:为 Linux 实现基于 strace 的自动违规检测,并与违规存储集成。目前,Linux 用户必须手动运行 strace 才能看到违规,而 macOS 则通过系统日志存储具有自动违规监控

分类