安全与沙箱

📑 目录

在 AI Agent 深度参与软件开发的今天,安全问题已经从"可选配置"变成了"核心基础设施"。Claude Code 作为 Anthropic 推出的命令行智能助手,其安全体系的设计尤为值得研究。本文将深入解析 Claude Code 的沙箱(Sandbox)架构、权限验证机制、Undercover Mode 以及安全审计系统,帮助开发者理解一个生产级 AI Agent 如何在开放环境中保护用户系统和敏感信息。

一、沙箱架构全景

Claude Code 的沙箱并非从零构建,而是基于 @anthropic-ai/sandbox-runtime 这一底层包,通过适配层将其与 CLI 的设置系统、工具调用和 UI 渲染深度集成。这种分层架构既保证了安全策略的底层可靠性,又提供了面向用户的灵活配置能力。

1.1 目录结构与职责

沙箱相关的代码主要分布在两个目录:

src/utils/sandbox/
├── sandbox-adapter.ts      # 35KB,核心适配层
└── sandbox-ui-utils.ts     # 389字节,UI 辅助函数

src/components/sandbox/
├── SandboxConfigTab.tsx        # 16KB,配置展示页签
├── SandboxDependenciesTab.tsx  # 17KB,依赖检查页签
├── SandboxDoctorSection.tsx    # 6KB,诊断面板片段
├── SandboxOverridesTab.tsx     # 20KB,覆盖规则页签
└── SandboxSettings.tsx         # 29KB,设置主面板

src/components/
└── SandboxViolationExpandedView.tsx  # 11KB,违规事件展示

sandbox-adapter.ts 是整个沙箱系统的枢纽。它在源码第 1–5 行的注释中明确阐述了设计目标:

"Adapter layer that wraps @anthropic-ai/sandbox-runtime with Claude CLI-specific integrations. This file provides the bridge between the external sandbox-runtime package and Claude CLI’s settings system, tool integration, and additional features."

换言之,sandbox-adapter.ts 的职责是将外部沙箱运行时与 Claude Code 的权限系统、设置持久化、以及工具调用链无缝衔接。

1.2 架构分层图

flowchart TB
    subgraph UI["UI 层 (React/Ink)"]
        SS[SandboxSettings.tsx]
        SVE[SandboxViolationExpandedView.tsx]
        SD[SandboxDoctorSection.tsx]
    end

    subgraph Adapter["适配层"]
        SA[sandbox-adapter.ts]
    end

    subgraph Runtime["底层运行时"]
        BSM[BaseSandboxManager
@anthropic-ai/sandbox-runtime] SVS[SandboxViolationStore] end subgraph Tools["工具层"] BT[BashTool] FET[FileEditTool] WFT[WebFetchTool] end SS --> SA SVE --> SVS SA --> BSM SA --> SVS BT --> SA FET --> SA WFT --> SA BSM --> |bubblewrap / seatbelt| OS[操作系统沙箱]

如上图所示,UI 层通过 sandbox-adapter.ts 与底层运行时通信,而工具层(BashTool、FileEditTool、WebFetchTool)则在执行命令前由适配层决定是否包裹沙箱命令。底层在 Linux 上使用 bubblewrap,在 macOS 上使用 seatbelt,实现操作系统级别的进程隔离。

二、沙箱模式的启用与限制

2.1 启用条件

沙箱并非默认开启,其启用需要满足一系列条件。isSandboxingEnabled() 函数(sandbox-adapter.ts 第 532–547 行)定义了完整的检查链:

function isSandboxingEnabled(): boolean {
  if (!isSupportedPlatform()) {
    return false
  }

  if (checkDependencies().errors.length > 0) {
    return false
  }

  if (!isPlatformInEnabledList()) {
    return false
  }

  return getSandboxEnabledSetting()
}

具体条件包括:

  1. 平台支持:仅支持 macOS、Linux 和 WSL2+,WSL1 因内核限制不在支持范围内(第 491–493 行)。
  2. 依赖可用性:Linux 需要 bubblewrapsocat 等工具,macOS 需要 seatbelt 相关组件。checkDependencies() 使用 lodash-esmemoize 缓存结果(第 451–457 行)。
  3. 平台白名单enabledPlatforms 是一个未文档化的设置项,允许管理员仅对特定平台启用沙箱。例如 NVIDIA 的企业部署初期只希望 macOS 启用沙箱(第 505–526 行)。
  4. 用户显式启用sandbox.enabled 必须在设置中为 true

如果用户显式启用了沙箱但环境不满足,getSandboxUnavailableReason()(第 562–592 行)会返回具体原因,避免"静默失效"这一安全陷阱。

2.2 三种运行模式

Claude Code 的沙箱有三种互斥模式,在 SandboxSettings.tsx 第 21 行定义为 SandboxMode 类型:

type SandboxMode = 'auto-allow' | 'regular' | 'disabled'
  • auto-allow:沙箱启用,且所有在沙箱内运行的 Bash 命令自动获得批准。这是推荐的安全与便利平衡点。
  • regular:沙箱启用,但 Bash 命令仍需走正常的权限审批流程。
  • disabled:完全不启用沙箱。

2.3 文件系统隔离

文件系统隔离是沙箱最核心的功能之一。convertToSandboxRuntimeConfig() 函数(第 172–381 行)负责将 Claude Code 的权限规则转换为底层运行时配置。其策略非常精细:

写保护策略

  • 默认允许写入当前工作目录(.)和 Claude 临时目录(getClaudeTempDir())。
  • 禁止写入所有来源的 settings.json 文件,防止通过修改配置实现沙箱逃逸(第 232–236 行)。
  • 禁止写入 .claude/skills 目录,因为 skills 与 commands/agents 具有相同的自动发现和执行权限(第 248–255 行)。

Git 裸仓库攻击防护

这是一个非常精妙的安全设计。攻击者可以在工作目录中放置 HEADobjects/refs/ 等文件,让 Git 将其识别为 bare repository,再通过 core.fsmonitor 配置执行命令实现沙箱逃逸。代码在第 257–280 行处理了这一场景:

const bareGitRepoFiles = ['HEAD', 'objects', 'refs', 'hooks', 'config']
for (const dir of cwd === originalCwd ? [originalCwd] : [originalCwd, cwd]) {
  for (const gitFile of bareGitRepoFiles) {
    const p = resolve(dir, gitFile)
    try {
      statSync(p)
      denyWrite.push(p)
    } catch {
      bareGitRepoScrubPaths.push(p)
    }
  }
}

如果文件已存在,直接加入 denyWrite;如果不存在但后续被创建,cleanupAfterCommand() 会调用 scrubBareGitRepoFiles() 在命令结束后立即清除(第 404–414 行)。

路径解析规则

Claude Code 使用特殊的路径前缀约定(第 99–119 行):

  • //path → 文件系统根目录下的绝对路径(/.aws/**
  • /path → 相对于设置文件所在目录
  • ~/path 和相对路径 → 由 sandbox-runtime 处理

2.4 网络隔离

网络隔离通过域名白名单和黑名单实现。convertToSandboxRuntimeConfig() 从两个来源提取域名规则:

  1. settings.sandbox.network.allowedDomains 等显式配置
  2. permissions.allow 中的 WebFetch(domain:...) 规则

allowManagedDomainsOnly 策略启用时,只允许来自 policySettings 的域名,用户个人设置中的域名将被忽略(第 181–210 行)。这一设计满足企业环境中"IT 部门统一管控网络出口"的需求。

网络配置还涵盖了 Unix Socket、本地绑定、HTTP/SOCKS 代理端口等细粒度控制(第 360–368 行)。

三、权限验证体系

3.1 多层权限检查

Claude Code 的权限系统采用多层纵深防御。除了沙箱层面的系统级隔离,应用层还有独立的权限规则引擎。sandbox-adapter.ts 第 62–81 行实现了与权限系统的本地适配函数,避免循环依赖:

function permissionRuleValueFromString(ruleString: string): PermissionRuleValue {
  const matches = ruleString.match(/^([^(]+)\(([^)]+)\)$/)
  if (!matches) {
    return { toolName: ruleString }
  }
  const toolName = matches[1]
  const ruleContent = matches[2]
  // ...
}

权限规则格式为 ToolName(ruleContent),例如 Bash(npm run test)WebFetch(domain:api.github.com)FileEdit(./src/**/*.ts)

3.2 分类器自动审批

classifierApprovals.ts(88 行,约 2.4KB)记录了哪些工具使用被分类器(classifier)自动批准,无需用户手动确认。这是权限验证的"快速通道"。

type ClassifierApproval = {
  classifier: 'bash' | 'auto-mode'
  matchedRule?: string
  reason?: string
}

const CLASSIFIER_APPROVALS = new Map<string, ClassifierApproval>()

该模块支持两种分类器(第 10 行):

  • bash classifier:专门用于 Bash 命令的风险评估。setClassifierApproval()(第 19–30 行)在特征开关 BASH_CLASSIFIER 启用时记录审批结果,包含匹配到的规则名。
  • auto-mode classifier(Yolo):基于对话上下文的整体风险评估。setYoloClassifierApproval()(第 41–49 行)在 TRANSCRIPT_CLASSIFIER 启用时工作,记录审批原因。

这两个分类器通过信号机制(createSignal())与 UI 联动:

export function setClassifierChecking(toolUseID: string): void {
  if (!feature('BASH_CLASSIFIER') && !feature('TRANSCRIPT_CLASSIFIER')) return
  CLASSIFIER_CHECKING.add(toolUseID)
  classifierChecking.emit()
}

当分类器正在评估某个工具使用时,UI 可以展示"正在检查…"的状态,提升用户体验。

3.3 危险命令检测

在沙箱模式下,Bash 命令默认会被包裹在沙箱中执行。wrapWithSandbox() 函数(第 704–725 行)是这一过程的入口:

async function wrapWithSandbox(
  command: string,
  binShell?: string,
  customConfig?: Partial<SandboxRuntimeConfig>,
  abortSignal?: AbortSignal,
): Promise<string> {
  if (isSandboxingEnabled()) {
    if (initializationPromise) {
      await initializationPromise
    } else {
      throw new Error('Sandbox failed to initialize. ')
    }
  }

  return BaseSandboxManager.wrapWithSandbox(command, binShell, customConfig, abortSignal)
}

注意第 715 行的严格检查:如果沙箱已启用但初始化未完成(initializationPromise 不存在),直接抛出错误,避免在未准备好的沙箱环境中执行命令。

3.4 安全策略的强制与锁定

企业部署中,安全策略需要防止终端用户私自关闭。areSandboxSettingsLockedByPolicy()(第 647–664 行)检查沙箱设置是否被高优先级配置源锁定:

function areSandboxSettingsLockedByPolicy(): boolean {
  const overridingSources = ['flagSettings', 'policySettings'] as const
  for (const source of overridingSources) {
    const settings = getSettingsForSource(source)
    if (
      settings?.sandbox?.enabled !== undefined ||
      settings?.sandbox?.autoAllowBashIfSandboxed !== undefined ||
      settings?.sandbox?.allowUnsandboxedCommands !== undefined
    ) {
      return true
    }
  }
  return false
}

flagSettingspolicySettings 的优先级高于用户本地的 localSettings。如果管理员通过这两个渠道配置了沙箱参数,用户界面会显示设置已被锁定,无法修改。

四、Undercover Mode:防止内部信息泄露

Undercover Mode 是 Claude Code 中一个独特且有趣的安全功能,专门用于防止 Anthropic 内部信息通过 AI 生成的代码贡献泄露到公共仓库。

4.1 功能定位

undercover.ts(89 行,约 3.6KB)的头部注释(第 1–22 行)清晰地描述了这一功能:

"When active, Claude Code adds safety instructions to commit/PR prompts and strips all attribution to avoid leaking internal model codenames, project names, or other Anthropic-internal information. The model is not told what model it is."

简而言之,当 Claude Code 检测到正在向公共或开源仓库提交代码时,它会自动进入"卧底模式",在提示词中注入安全指令,要求模型不要泄露任何内部信息。

4.2 激活逻辑

isUndercover() 函数(第 28–37 行)实现了激活判断:

export function isUndercover(): boolean {
  if (process.env.USER_TYPE === 'ant') {
    if (isEnvTruthy(process.env.CLAUDE_CODE_UNDERCOVER)) return true
    return getRepoClassCached() !== 'internal'
  }
  return false
}

激活逻辑有三层:

  1. 强制开启:设置环境变量 CLAUDE_CODE_UNDERCOVER=1,即使在内部仓库也强制启用。
  2. 自动检测:如果仓库远程地址不在内部白名单(INTERNAL_MODEL_REPOS)中,则自动启用。
  3. 无强制关闭:注释中强调"There is NO force-OFF"。如果系统不确定当前是否在内部仓库,默认保持 undercover 状态。

值得注意的是,所有代码路径都被 process.env.USER_TYPE === 'ant' 守卫。由于 USER_TYPE 是构建时的 --define 变量,打包器会在外部构建中常量折叠并死码消除这些分支。因此,在公开发布的 Claude Code 版本中,undercover.ts 中的所有函数都会退化为空操作(第 18–22 行注释)。

4.3 内部代号过滤

getUndercoverInstructions()(第 39–72 行)返回注入模型的安全指令。其中明确列出了需要过滤的内部信息类型:

NEVER include in commit messages or PR descriptions:
- Internal model codenames (animal names like Capybara, Tengu, etc.)
- Unreleased model version numbers (e.g., opus-4-7, sonnet-4-8)
- Internal repo or project names (e.g., claude-cli-internal, anthropics/…)
- Internal tooling, Slack channels, or short links (e.g., go/cc, #claude-code-…)
- The phrase "Claude Code" or any mention that you are an AI
- Any hint of what model or version you are
- Co-Authored-By lines or any other attribution

指令还给出了正反示例。良好的提交信息应该是:

"Fix race condition in file watcher initialization"

而绝对不应该出现:

"Fix bug found while testing with Claude Capybara"
"1-shotted by claude-opus-4-6"
"Generated with Claude Code"

4.4 员工使用场景

Undercover Mode 主要面向 Anthropic 内部员工在以下场景使用:

  • 向开源项目(如 React、TypeScript 等)贡献代码
  • 在公共 GitHub 仓库中提交 PR
  • /tmp 等临时目录进行 crash repro 时,当前目录本身不是 git checkout,但可能推送代码到公共远程

shouldShowUndercoverAutoNotice()(第 80–89 行)还会检测是否首次自动进入 undercover 模式,并向用户展示一次性说明,避免用户在不知情的情况下发现提交行为"异常保守"。

五、安全审计与监控

5.1 违规事件追踪

沙箱系统内置了完整的违规事件追踪机制。SandboxViolationStore 来自 @anthropic-ai/sandbox-runtime,负责收集所有被沙箱拦截的操作。SandboxViolationExpandedView.tsx(99 行,约 11KB)则在 UI 中实时展示这些事件:

export function SandboxViolationExpandedView() {
  const [violations, setViolations] = useState([])
  const [totalCount, setTotalCount] = useState(0)

  useEffect(() => {
    const store = SandboxManager.getSandboxViolationStore()
    const unsubscribe = store.subscribe(allViolations => {
      setViolations(allViolations.slice(-10))
      setTotalCount(store.getTotalCount())
    })
    return unsubscribe
  }, [])
  // ...
}

组件订阅 SandboxViolationStore,始终展示最近的 10 条违规记录,同时显示总计数。每条记录包含时间戳、被拦截的命令和具体行信息(第 96–98 行)。

值得注意的是第 50 行的平台判断:

if (!SandboxManager.isSandboxingEnabled() || getPlatform() === "linux") {
  return null
}

Linux 平台不展示违规扩展视图,因为 Linux 使用 bubblewrap,其违规报告的粒度与 macOS 的 seatbelt 不同。

5.2 遥测上报

虽然源码中没有直接展示遥测发送的代码(可能位于闭源的 sandbox-runtime 包中),但从架构设计可以推断审计数据的流向。logForDebugging() 函数在多个关键点被调用:

  • 沙箱配置更新时:logForDebugging('Sandbox configuration updated from settings change')(第 780 行)
  • 初始化失败时:logForDebugging(\Failed to initialize sandbox: ${errorMessage(error)}`)`(第 787 行)
  • 网络请求被策略拦截时:logForDebugging(\[sandbox] Blocked network request to ${hostPattern.host} (allowManagedDomainsOnly)`)`(第 749 行)
  • 清理 bare repo 攻击文件时:logForDebugging(\[Sandbox] scrubbed planted bare-repo file: ${p}`)`(第 409 行)

这些调试日志为后续的安全审计和事件溯源提供了关键线索。

5.3 诊断与自检

sandbox-ui-utils.ts 虽然只有 12 行,但提供了一个实用的文本清理函数:

export function removeSandboxViolationTags(text: string): string {
  return text.replace(/<sandbox_violations>[\s\S]*?<\/sandbox_violations>/g, '')
}

该函数用于在 UI 展示前移除错误消息中的 <sandbox_violations> 标签,避免向终端输出过多内部技术细节。

SandboxDoctorSection.tsx(46 行)则集成在 /doctor 命令的诊断面板中,主动检查沙箱依赖状态并向用户报告:

export function SandboxDoctorSection() {
  if (!SandboxManager.isSupportedPlatform()) {
    return null
  }
  if (!SandboxManager.isSandboxEnabledInSettings()) {
    return null
  }
  const depCheck = SandboxManager.checkDependencies()
  const hasErrors = depCheck.errors.length > 0
  const hasWarnings = depCheck.warnings.length > 0
  // ...
}

如果存在错误,提示用户运行 /sandbox 获取安装指引;如果只有警告,则显示"可用(但有警告)"。

5.4 异常检测的工作流

sequenceDiagram
    participant User as 用户
    participant BT as BashTool
    participant SA as sandbox-adapter.ts
    participant BSM as BaseSandboxManager
    participant SVS as SandboxViolationStore
    participant UI as SandboxViolationExpandedView

    User->>BT: 执行命令
    BT->>SA: wrapWithSandbox(command)
    SA->>BSM: wrapWithSandbox(...)
    BSM-->>BSM: 系统级隔离执行
    alt 发生违规
        BSM->>SVS: 记录违规事件
        SVS->>UI: emit() 通知订阅者
        UI->>UI: 更新状态展示最近10条
    else 正常完成
        BSM->>SA: cleanupAfterCommand()
        SA->>SA: scrubBareGitRepoFiles()
    end

上图展示了从命令执行到异常检测的完整工作流。无论命令是否触发违规,清理阶段都会执行,确保攻击者通过命令执行植入的 bare repo 文件被及时清除。

六、总结

Claude Code 的安全体系展现了生产级 AI Agent 应有的纵深防御思维:

  1. 系统级隔离通过 sandbox-runtime 将进程限制在最小权限空间,文件系统和网络访问都受到严格管控。
  2. 应用层权限通过分类器自动审批和用户确认相结合,在安全与效率之间取得平衡。
  3. 信息泄露防护通过 Undercover Mode 这一独特设计,防止 AI 模型在公共场景中"说漏嘴"。
  4. 持续监控与自检通过违规事件追踪、诊断面板和清理机制,确保安全策略在整个会话周期内持续有效。

对于正在构建 AI Agent 的开发者而言,Claude Code 的沙箱设计提供了一个优秀的参考模板:安全不是单一功能的堆砌,而是从架构分层、策略执行到审计追踪的系统性工程。尤其是 bare repo 攻击防护和 undercover 信息过滤这两个细节,体现了安全设计中对"边缘场景"和"社会工程"威胁的深刻洞察。


参考源码版本:基于 yasasbanukaofficial/claude-code 仓库 main 分支的源码分析,核心文件包括 src/utils/sandbox/sandbox-adapter.tssrc/utils/classifierApprovals.tssrc/utils/undercover.tssrc/components/SandboxViolationExpandedView.tsx 等。