本教程面向需要在团队或生产环境中安全使用 Claude Code 的开发者和安全工程师。你将学会:配置 settings.json 的 allow/deny 权限规则、使用 Hooks 拦截危险操作、搭建 Docker 沙箱实现文件系统与网络隔离,最终构建一套多层防御的 Claude Code 安全体系。适合有一定 Claude Code 使用基础、关注安全合规的技术人员。
适用人群:中高级开发者、DevOps 工程师、安全工程师、技术团队负责人
预计学习时长:3-4 小时(含动手实践)
| 要求 | 说明 |
|---|---|
| Claude Code CLI | 已安装并完成基本配置(npm install -g @anthropic-ai/claude-code) |
| Node.js 18+ | Claude Code 运行环境 |
| Docker(可选) | 第四章沙箱实践需要 |
| Linux/macOS | 本教程命令以 Linux/macOS 为主 |
| 基础 CLI 能力 | 熟悉终端操作和 JSON 编辑 |
Claude Code 的每一次工具调用(Tool Call)——无论是执行 Bash 命令、编辑文件,还是发起网络请求——都必须经过权限系统的审核。这个系统基于三条核心规则运作:
┌─────────────────────────────────────────────────┐
│ Claude Code 权限决策树 │
├─────────────────────────────────────────────────┤
│ │
│ 工具调用请求 │
│ │ │
│ ▼ │
│ ┌─────────┐ 匹配 ┌──────────────────┐ │
│ │ Deny 规则├────────►│ ❌ 直接拒绝(静默)│ │
│ └────┬────┘ └──────────────────┘ │
│ │ 不匹配 │
│ ▼ │
│ ┌──────────┐ 匹配 ┌───────────────────┐ │
│ │ Allow 规则├───────►│ ✅ 自动放行(无提示)│ │
│ └────┬─────┘ └───────────────────┘ │
│ │ 不匹配 │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ 🟡 弹出确认对话框(Ask 模式)│ │
│ │ 用户手动选择 Allow / Deny │ │
│ └──────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────┘
关键原则:Deny 优先于 Allow。 如果一条规则同时匹配 deny 和 allow,deny 始终获胜。这确保了安全策略的绝对权威。
Claude Code 的配置文件分布在三个层级,形成一套继承与覆盖的体系:
~/.claude/settings.json ← 全局配置(对所有项目生效)
└── 项目根目录/.claude/settings.json ← 项目配置(仅对当前项目生效)
└── settings.local.json ← 本地配置(不提交到 Git)
优先级规则:
- deny 规则:全局 deny + 项目 deny 取并集,全部生效
- allow 规则:项目级 allow 覆盖全局级 allow
- settings.local.json:覆盖同目录下的 settings.json(用于个人偏好,不进版本控制)
{
"permissions": {
"allow": [
"Bash(git status)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(npm test *)",
"Bash(npm run lint *)",
"Read",
"Glob",
"Grep"
],
"deny": [
"Bash(rm -rf *)",
"Bash(curl * | bash *)",
"Bash(sudo *)",
"Bash(chmod 777 *)",
"Edit(*.env*)",
"Write(*.env*)",
"Write(*secret*)",
"Write(*credential*)"
]
}
}
语法解析:
| 模式 | 含义 | 示例 |
|---|---|---|
"Tool" |
允许该工具的所有调用 | "Read" → 可读任意文件 |
"Tool(prefix)" |
匹配以 prefix 开头的参数 | "Bash(git *)" → 允许所有 git 命令 |
"Tool(*keyword*)" |
参数包含 keyword | "Bash(*npm test*)" |
"Tool(path/*)" |
路径 glob 匹配 | "Edit(src/**)" → 只允许编辑 src 目录 |
Bash 是最需要严格控制的工具,因为它能执行任意系统命令:
{
"permissions": {
"allow": [
"Bash(git *)",
"Bash(npm *)",
"Bash(npx *)",
"Bash(node *)",
"Bash(python *)",
"Bash(ls *)",
"Bash(cat *)",
"Bash(echo *)",
"Bash(mkdir *)",
"Bash(cp *)",
"Bash(mv *)"
],
"deny": [
"Bash(rm -rf /)",
"Bash(rm -rf ~*)",
"Bash(sudo *)",
"Bash(su *)",
"Bash(chmod 777 *)",
"Bash(curl * | sh)",
"Bash(curl * | bash)",
"Bash(wget * | sh)",
"Bash(aws s3 rm *)",
"Bash(docker rm *)",
"Bash(kubectl delete *)",
"Bash(pkill *)",
"Bash(kill -9 *)"
]
}
}
{
"permissions": {
"allow": [
"Read",
"Edit(src/**)",
"Edit(tests/**)",
"Write(src/**)",
"Write(tests/**)"
],
"deny": [
"Edit(.env*)",
"Edit(*.pem)",
"Edit(*.key)",
"Write(.env*)",
"Write(*.pem)",
"Write(*.key)",
"Write(/etc/*)",
"Write(/var/*)",
"Edit(package.json)",
"Edit(package-lock.json)"
]
}
}
{
"permissions": {
"allow": [
"WebFetch",
"WebSearch"
],
"deny": [
"WebFetch(http://localhost*)",
"WebFetch(http://127.0.0.1*)",
"WebFetch(http://192.168.*)",
"WebFetch(http://10.*)",
"WebFetch(http://172.16.*)"
]
}
}
为什么禁止访问内网地址? 在 CI/CD 或共享环境中,Claude Code 可能通过 WebFetch 发起 SSRF(Server-Side Request Forgery)攻击,访问内部服务。
除了手动编辑 JSON,Claude Code 提供了交互式权限管理:
# 启动时预设权限
claude --allow "Bash(git *)" --allow "Bash(npm test *)" --deny "Bash(sudo *)"
# 在会话中管理权限
/permissions # 打开权限管理面板
/permissions allow # 查看所有 allow 规则
/permissions deny # 查看所有 deny 规则
常见问题与解决:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 规则不生效 | JSON 语法错误 | 用 jq . ~/.claude/settings.json 验证 |
| allow 了还是弹窗 | deny 规则也匹配了 | 检查是否有冲突的 deny 规则 |
| 路径匹配不到 | glob 模式不对 | 使用 src/** 而非 src/* 递归匹配 |
| 权限太多太乱 | 配置文件膨胀 | 按工具分组,定期清理无用规则 |
settings.json 的 allow/deny 是静态的字符串匹配,能力有限。Hooks 则允许你在工具调用的生命周期中注入任意 Shell 脚本逻辑——你可以检查文件内容、调用外部 API、执行复杂的条件判断,然后决定放行还是拦截。
sequenceDiagram
participant User as 用户/Claude
participant HookSystem as Hook 引擎
participant PreHook as PreToolUse Hook
participant Tool as 目标工具
participant PostHook as PostToolUse Hook
User->>HookSystem: 发起工具调用
HookSystem->>PreHook: 传递工具名+参数(JSON)
alt PreHook 返回 exit 0
PreHook->>HookSystem: 放行
HookSystem->>Tool: 执行工具
Tool->>HookSystem: 返回结果
HookSystem->>PostHook: 传递结果(JSON)
PostHook->>HookSystem: 后处理完成
HookSystem->>User: 返回最终结果
else PreHook 返回 exit 2
PreHook->>HookSystem: 拦截(反馈给 Claude)
HookSystem->>User: 返回拦截原因
else PreHook 返回 exit 1 或超时
PreHook->>HookSystem: 静默放行
HookSystem->>Tool: 执行工具
end
截至 2026 年初,Claude Code 支持以下 Hook 事件:
| 事件 | 触发时机 | 典型用途 |
|---|---|---|
PreToolUse |
工具执行之前 | 拦截危险操作、参数校验 |
PostToolUse |
工具执行之后 | 自动格式化、日志记录、触发构建 |
Notification |
Claude 通知用户时 | 自定义通知渠道(Slack/钉钉) |
Stop |
Claude 完成回复时 | 生成报告、触发 CI |
SubagentStop |
子 Agent 完成时 | 聚合子任务结果 |
Hooks 配置在 settings.json 的 hooks 字段中:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 /path/to/security-guard.py"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write $CLAUDE_FILE_PATH 2>/dev/null || true"
}
]
}
]
}
}
字段说明:
matcher:正则表达式,匹配工具名。"Bash" 精确匹配,"Edit|Write" 匹配两者hooks[].type:目前支持 "command"(执行 Shell 命令)hooks[].command:要执行的命令Hook 脚本通过 stdin 接收 JSON 数据:
{
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf /tmp/test",
"description": "清理临时文件"
},
"session_id": "abc123"
}
PreToolUse 的退出码语义:
| 退出码 | 含义 | 行为 |
|---|---|---|
0 |
放行 | 工具正常执行 |
2 |
拦截 | 阻止执行,stdout 内容作为反馈给 Claude |
| 其他 / 超时 | 忽略 | 工具正常执行(安全失败策略) |
#!/usr/bin/env python3
"""danger-guard.py — 拦截危险的 Bash 命令"""
import sys, json
DANGEROUS_PATTERNS = [
"rm -rf /", "rm -rf ~", "rm -rf /*",
"sudo rm", ":(){ :|:& };:", # fork bomb
"dd if=/dev/zero",
"mkfs.", "format ",
"> /dev/sd", "chmod -R 777 /",
"curl * | bash", "curl * | sh",
"wget * | bash", "wget * | sh",
]
def check(data):
if data.get("tool_name") != "Bash":
sys.exit(0)
cmd = data.get("tool_input", {}).get("command", "")
for pattern in DANGEROUS_PATTERNS:
# 简单的通配符匹配
if "*" in pattern:
parts = pattern.split("*")
if all(p in cmd for p in parts if p):
print(f"⛔ 危险命令已被拦截!匹配模式: {pattern}")
print(f" 命令: {cmd}")
sys.exit(2)
elif pattern in cmd:
print(f"⛔ 危险命令已被拦截!匹配模式: {pattern}")
print(f" 命令: {cmd}")
sys.exit(2)
sys.exit(0) # 放行
data = json.load(sys.stdin)
check(data)
配置:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/danger-guard.py"
}
]
}
]
}
}
#!/usr/bin/env python3
"""file-guard.py — 保护敏感文件不被修改或删除"""
import sys, json, re
PROTECTED_PATTERNS = [
r"\.env($|\.|$)", r"\.env\.",
r"\.pem$", r"\.key$", r"\.p12$",
r"credentials", r"secrets?[/\\]",
r"id_rsa", r"id_ed25519",
r"\.ssh[/\\]",
r"\.gnupg[/\\]",
r"\.aws[/\\]credentials",
]
def check(data):
tool = data.get("tool_name", "")
if tool not in ("Edit", "Write", "Bash"):
sys.exit(0)
if tool == "Bash":
cmd = data.get("tool_input", {}).get("command", "")
# 检查是否涉及敏感文件的删除或覆盖
for p in PROTECTED_PATTERNS:
if re.search(p, cmd):
print(f"🔒 操作被阻止:涉及敏感文件/目录 (模式: {p})")
print(f" 命令: {cmd}")
sys.exit(2)
sys.exit(0)
file_path = data.get("tool_input", {}).get("file_path", "")
for p in PROTECTED_PATTERNS:
if re.search(p, file_path):
print(f"🔒 文件被保护,禁止 {tool} 操作: {file_path}")
sys.exit(2)
sys.exit(0)
data = json.load(sys.stdin)
check(data)
#!/usr/bin/env python3
"""auto-format.py — 文件编辑后自动格式化"""
import sys, json, subprocess, os
def format_file(data):
tool = data.get("tool_name", "")
if tool not in ("Edit", "Write"):
sys.exit(0)
file_path = data.get("tool_input", {}).get("file_path", "")
if not file_path or not os.path.exists(file_path):
sys.exit(0)
ext = os.path.splitext(file_path)[1]
# 根据文件类型选择格式化工具
formatters = {
".js": ["npx", "prettier", "--write"],
".ts": ["npx", "prettier", "--write"],
".jsx": ["npx", "prettier", "--write"],
".tsx": ["npx", "prettier", "--write"],
".css": ["npx", "prettier", "--write"],
".json": ["npx", "prettier", "--write"],
".py": ["python3", "-m", "black"],
".go": ["gofmt", "-w"],
".rs": ["rustfmt"],
}
formatter = formatters.get(ext)
if formatter:
try:
subprocess.run(
formatter + [file_path],
capture_output=True, timeout=10
)
except Exception:
pass # 格式化失败不影响主流程
sys.exit(0)
data = json.load(sys.stdin)
format_file(data)
#!/usr/bin/env python3
"""git-guard.py — 防止在 main/master 分支上直接修改"""
import sys, json, subprocess
def check(data):
if data.get("tool_name") != "Bash":
sys.exit(0)
cmd = data.get("tool_input", {}).get("command", "")
# 只检查 git commit / git push 操作
if "git commit" not in cmd and "git push" not in cmd:
sys.exit(0)
try:
result = subprocess.run(
["git", "rev-parse", "--abbrev-ref", "HEAD"],
capture_output=True, text=True, timeout=5
)
branch = result.stdout.strip()
if branch in ("main", "master"):
print(f"🛡️ 禁止在 {branch} 分支上直接 commit/push!")
print(f" 请创建新分支: git checkout -b feature/xxx")
sys.exit(2)
except Exception:
pass
sys.exit(0)
data = json.load(sys.stdin)
check(data)
#!/usr/bin/env python3
"""audit-logger.py — 记录所有工具调用到审计日志"""
import sys, json, os
from datetime import datetime
LOG_FILE = os.path.expanduser("~/.claude/audit.log")
def log(data):
entry = {
"timestamp": datetime.now().isoformat(),
"tool": data.get("tool_name"),
"input_summary": str(data.get("tool_input", {}))[:500],
"session_id": data.get("session_id", "unknown"),
}
with open(LOG_FILE, "a") as f:
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
sys.exit(0) # 只记录,不拦截
data = json.load(sys.stdin)
log(data)
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/danger-guard.py"
},
{
"type": "command",
"command": "python3 ~/.claude/hooks/git-guard.py"
}
]
},
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/file-guard.py"
}
]
},
{
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/audit-logger.py"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/auto-format.py"
}
]
}
]
}
}
注意:同一个 matcher 下可以有多个 Hook,它们按顺序执行。任何一个返回 exit 2 就会拦截。
权限系统和 Hooks 不是二选一的关系,而是多层防线:
flowchart TB
subgraph "第一层:静态权限防线"
A[工具调用] --> B{Deny 规则匹配?}
B -->|是| C[❌ 静默拒绝]
B -->|否| D{Allow 规则匹配?}
D -->|是| E[✅ 自动放行]
D -->|否| F{PreToolUse Hook}
end
subgraph "第二层:动态 Hook 防线"
F -->|exit 2| G[❌ Hook 拦截]
F -->|exit 0| H{用户确认}
end
subgraph "第三层:人工确认"
H -->|拒绝| I[❌ 用户拒绝]
H -->|允许| J[执行工具]
end
subgraph "第四层:后置处理"
J --> K[PostToolUse Hook]
K --> L[日志 / 格式化 / 验证]
end
style C fill:#ff6b6b
style G fill:#ff6b6b
style I fill:#ff6b6b
style E fill:#51cf66
style J fill:#51cf66
style L fill:#74c0fc
不同场景需要不同的安全级别:
| 场景 | 权限策略 | Hook 策略 | 推荐模式 |
|---|---|---|---|
| 个人学习/实验 | 宽松 allow + 少量 deny | 审计日志 | --auto-accept-edits |
| 团队项目开发 | 精确 allow + deny 敏感操作 | 全部 5 个 Hook | 标准 Ask 模式 |
| CI/CD 自动化 | 严格 allow 白名单 | 守卫 + 审计 | --dangerously-skip-permissions + Docker |
| 安全审计 | 严格 deny + 最小 allow | 全 Hook + 额外验证 | 最严格模式 |
创建 .claude/settings.json(提交到 Git,团队共享):
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"WebSearch",
"Bash(git status)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(npm test *)",
"Bash(npm run lint *)",
"Bash(npm run build *)",
"Bash(npm run typecheck *)",
"Bash(npx *)",
"Bash(node *)",
"Bash(ls *)",
"Bash(cat *)",
"Bash(head *)",
"Bash(tail *)",
"Bash(find *)",
"Bash(grep *)",
"Bash(wc *)",
"Bash(echo *)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(sudo *)",
"Bash(chmod 777 *)",
"Bash(curl * | sh*)",
"Bash(curl * | bash*)",
"Bash(wget * | sh*)",
"Bash(docker rm *)",
"Bash(kubectl delete *)",
"Bash(psql *)",
"Bash(mysql *)",
"Bash(mongosh *)",
"Write(.env*)",
"Write(*.pem)",
"Write(*.key)",
"Write(*.secret*)",
"Edit(.env*)",
"Edit(*.pem)",
"Edit(*.key)",
"Edit(*.secret*)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 scripts/hooks/danger-guard.py"
}
]
},
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "python3 scripts/hooks/file-guard.py"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "python3 scripts/hooks/auto-format.py"
}
]
}
]
}
}
创建 .claude/settings.local.json(不提交到 Git,个人覆盖):
{
"permissions": {
"allow": [
"Bash(docker *)",
"Bash(kubectl *)",
"Bash(terraform *)",
"Bash(aws *)"
]
}
}
在 .gitignore 中添加:
.claude/settings.local.json
权限和 Hooks 保护的是代码层面的操作安全,但无法防御以下场景: - Claude Code 进程被利用执行任意代码 - 文件系统被越权访问 - 网络被用来攻击内网服务 - 环境变量中的密钥被读取
Docker 沙箱提供了操作系统级别的隔离,即使 Claude Code 被完全控制,攻击者也只能在容器内部活动。
flowchart LR
subgraph "方案A:内置沙箱"
A1[Claude Code] --> A2[Seatbelt / Landlock]
A2 --> A3[受限文件系统]
end
subgraph "方案B:Docker 容器"
B1[Claude Code] --> B2[Docker Container]
B2 --> B3[命名空间隔离]
B2 --> B4[网络隔离]
B2 --> B5[资源限制]
end
subgraph "方案C:Dev Container"
C1[Claude Code] --> C2[VS Code Dev Container]
C2 --> C3[完整开发环境]
end
style A2 fill:#ffd43b
style B2 fill:#51cf66
style C2 fill:#74c0fc
| 方案 | 隔离级别 | 易用性 | 适用场景 |
|---|---|---|---|
| 内置沙箱(macOS Seatbelt / Linux Landlock) | 中 | ⭐⭐⭐⭐⭐ | 个人开发、macOS 用户 |
| Docker 容器 | 高 | ⭐⭐⭐ | CI/CD、团队共享、Linux 服务器 |
| Dev Container | 高 | ⭐⭐⭐⭐ | VS Code 用户、团队项目 |
# claude-sandbox/Dockerfile
FROM node:20-slim
# 安装基础工具
RUN apt-get update && apt-get install -y \
git \
python3 \
python3-pip \
curl \
jq \
&& rm -rf /var/lib/apt/lists/*
# 安装 Claude Code
RUN npm install -g @anthropic-ai/claude-code
# 创建非 root 用户
RUN useradd -m -s /bin/bash claude
USER claude
WORKDIR /home/claude/workspace
# 设置安全的环境变量
ENV NODE_ENV=development
ENV CLAUDE_CODE_SANDBOX=true
ENTRYPOINT ["claude"]
# claude-sandbox/docker-compose.yml
version: '3.8'
services:
claude-sandbox:
build: .
container_name: claude-code-sandbox
# 关键安全配置
security_opt:
- no-new-privileges:true
read_only: false
# 资源限制
deploy:
resources:
limits:
cpus: '2.0'
memory: 4G
reservations:
cpus: '0.5'
memory: 512M
# 网络隔离(仅 DNS,无外网)
networks:
- claude-net
# 文件系统挂载(只挂载项目目录)
volumes:
- ../:/home/claude/workspace:rw
- claude-home:/home/claude/.claude
# 环境变量(从 .env 传入 API Key)
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
# 禁止特权升级
cap_drop:
- ALL
stdin_open: true
tty: true
networks:
claude-net:
driver: bridge
internal: false # 设为 true 可完全禁止外网
volumes:
claude-home:
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"Bash(git *)",
"Bash(npm *)",
"Bash(npx *)",
"Bash(node *)",
"Bash(python3 *)",
"Bash(ls *)",
"Bash(cat *)",
"Bash(find *)",
"Bash(echo *)",
"Bash(mkdir *)",
"Bash(test *)",
"Edit(src/**)",
"Edit(tests/**)",
"Write(src/**)",
"Write(tests/**)"
],
"deny": [
"Bash(sudo *)",
"Bash(su *)",
"Bash(rm -rf /)",
"Bash(curl * | *)",
"Bash(wget * | *)",
"Bash(apt *)",
"Bash(yum *)",
"Bash(pip install *)",
"Bash(npm install -g *)",
"Write(*)",
"Edit(*)"
]
}
}
# 构建镜像
cd claude-sandbox
docker compose build
# 交互式启动(连接到终端)
docker compose run --rm claude-sandbox
# 启动时传入项目特定的权限配置
docker compose run --rm \
-v $(pwd)/sandbox-settings.json:/home/claude/.claude/settings.json:ro \
claude-sandbox
# 以 -p 模式执行任务(非交互式)
docker compose run --rm claude-sandbox \
-p "分析 src/ 目录的代码质量,给出改进建议"
# 执行完成后自动销毁容器
docker compose down
如果需要禁止 Claude Code 访问外网,但允许访问特定 API:
# 在 docker-compose.yml 中替换 networks 配置
networks:
claude-net:
driver: bridge
internal: true # 完全禁止外网访问
# 需要额外配置 DNS 和 API 代理
如果需要允许访问 Anthropic API 但禁止其他外网:
services:
claude-sandbox:
networks:
- claude-net
dns:
- 8.8.8.8 # 允许 DNS 解析
api-proxy:
image: nginx:alpine
networks:
- claude-net
- external-net
# 配置 nginx 仅代理 api.anthropic.com
networks:
claude-net:
internal: true
external-net:
driver: bridge
在不方便使用 Docker 的环境(如 macOS),可以使用 Claude Code 内置的沙箱:
# macOS:使用 Seatbelt 沙箱
claude --sandbox
# 或在 settings.json 中配置
# macOS 会自动使用 Seatbelt
# Linux 会使用 Landlock LSM
内置沙箱的限制: - macOS Seatbelt:限制文件系统访问、禁止网络(可配置) - Linux Landlock:限制文件系统访问(需要内核 5.13+) - 不如 Docker 隔离彻底,但零配置即用
为一个 5 人前端团队搭建 Claude Code 安全开发环境,要求: 1. 团队成员共享安全策略 2. 个人可自定义覆盖 3. CI/CD 中可无人值守运行 4. 敏感文件绝对保护 5. 所有操作可审计
my-project/
├── .claude/
│ ├── settings.json # 团队共享安全策略(提交 Git)
│ ├── hooks/
│ │ ├── danger-guard.py # 危险命令拦截
│ │ ├── file-guard.py # 敏感文件保护
│ │ ├── git-guard.py # Git 分支保护
│ │ ├── auto-format.py # 自动格式化
│ │ └── audit-logger.py # 审计日志
│ └── audit/ # 审计日志存储(gitignore)
│ └── .gitkeep
├── .claude-sandbox/ # Docker 沙箱配置
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── sandbox-settings.json # 沙箱内的权限配置
├── .github/
│ └── workflows/
│ └── claude-review.yml # CI 中使用 Claude Code
├── CLAUDE.md # 项目指令文件
├── .gitignore
├── package.json
└── src/
# Project: MyProject
## Tech Stack
- React 18 + TypeScript
- Vite
- TailwindCSS
- Vitest + React Testing Library
## Architecture
- src/components/ — UI 组件
- src/hooks/ — 自定义 React Hooks
- src/services/ — API 调用层
- src/utils/ — 工具函数
- tests/ — 测试文件
## Rules
- 所有新代码必须包含对应测试
- 使用 TypeScript strict mode
- 组件使用函数式组件 + Hooks
- 不要修改 package.json 中的依赖版本
- 不要删除 .env.example 中的变量
## Commands
- 构建: npm run build
- 测试: npm run test
- Lint: npm run lint
- 类型检查: npm run typecheck
# .github/workflows/claude-review.yml
name: Claude Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
claude-review:
runs-on: ubuntu-latest
container:
image: node:20-slim
options: --user root
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
npm ci
npm install -g @anthropic-ai/claude-code
- name: Run Claude Code Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
claude -p "Review the changes in this PR for:
1. Security vulnerabilities
2. Code quality issues
3. Missing tests
4. Breaking changes
Be specific and cite file paths." \
--allowedTools "Read,Glob,Grep,Bash(git diff *)" \
--max-turns 10
关键安全措施:
- 使用 --allowedTools 限制可用工具为只读
- --max-turns 防止无限循环
- API Key 存储在 GitHub Secrets 中
- 容器隔离运行
创建一个验证脚本:
#!/bin/bash
# scripts/verify-security.sh — 验证 Claude Code 安全配置
set -e
echo "🔍 验证 Claude Code 安全配置..."
echo ""
# 1. 检查 settings.json 是否存在且语法正确
echo "✅ 检查 settings.json..."
if [ -f ".claude/settings.json" ]; then
jq empty .claude/settings.json 2>/dev/null && echo " JSON 语法正确" || echo " ❌ JSON 语法错误!"
else
echo " ❌ .claude/settings.json 不存在!"
fi
# 2. 检查 deny 规则是否覆盖关键危险操作
echo ""
echo "✅ 检查 deny 规则覆盖..."
DENOULEMENTS=("rm -rf" "sudo" "chmod 777" "curl.*bash")
for pattern in "${DENOULEMENTS[@]}"; do
if grep -q "$pattern" .claude/settings.json 2>/dev/null; then
echo " ✓ $pattern 已被 deny"
else
echo " ⚠ $pattern 未被 deny"
fi
done
# 3. 检查 Hook 脚本是否存在且可执行
echo ""
echo "✅ 检查 Hook 脚本..."
for hook in danger-guard file-guard git-guard auto-format audit-logger; do
if [ -f ".claude/hooks/${hook}.py" ]; then
echo " ✓ ${hook}.py 存在"
python3 -m py_compile ".claude/hooks/${hook}.py" 2>/dev/null && echo " 语法正确" || echo " ❌ 语法错误"
else
echo " ⚠ ${hook}.py 不存在"
fi
done
# 4. 检查 Docker 沙箱配置
echo ""
echo "✅ 检查 Docker 沙箱..."
if [ -f ".claude-sandbox/Dockerfile" ]; then
echo " ✓ Dockerfile 存在"
if grep -q "cap_drop" .claude-sandbox/docker-compose.yml 2>/dev/null; then
echo " ✓ 已配置 cap_drop"
else
echo " ⚠ 未配置 cap_drop"
fi
if grep -q "no-new-privileges" .claude-sandbox/docker-compose.yml 2>/dev/null; then
echo " ✓ 已配置 no-new-privileges"
else
echo " ⚠ 未配置 no-new-privileges"
fi
else
echo " ⚠ Docker 沙箱未配置"
fi
# 5. 检查 .gitignore 是否排除了敏感文件
echo ""
echo "✅ 检查 .gitignore..."
for item in "settings.local.json" ".env"; do
if grep -q "$item" .gitignore 2>/dev/null; then
echo " ✓ $item 已被 gitignore"
else
echo " ⚠ $item 未被 gitignore"
fi
done
echo ""
echo "🎉 安全配置验证完成!"
运行验证:
chmod +x scripts/verify-security.sh
./scripts/verify-security.sh
| 问题现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
| Hook 不触发 | 脚本不可执行或路径错误 | ls -la .claude/hooks/ |
chmod +x .claude/hooks/*.py |
| 所有操作被拦截 | Hook 脚本有 Bug | 检查审计日志 | python3 .claude/hooks/danger-guard.py <<< '{"tool_name":"Read","tool_input":{}}' |
| Docker 容器启动失败 | 权限不足 | docker compose logs |
确保用户在 docker 组 |
| API Key 泄露 | settings.json 包含密钥 | git log --all --full-history -- '*.env' |
使用 Git history 重写 |
| Claude Code 异常慢 | Docker 资源不足 | docker stats |
增大内存/CPU 限制 |
# 查看今天的审计日志
cat ~/.claude/audit.log | jq -c 'select(.timestamp | startswith("2026-06-11"))'
# 统计各工具调用次数
cat ~/.claude/audit.log | jq -r '.tool' | sort | uniq -c | sort -rn
# 查找涉及敏感文件的操作
cat ~/.claude/audit.log | jq -c 'select(.input_summary | test("\\.env|\\.pem|\\.key|secret"))'
# 查看特定会话的所有操作
cat ~/.claude/audit.log | jq -c 'select(.session_id == "abc123")'
在生产环境部署 Claude Code 前,逐项确认:
□ settings.json 配置了 deny 规则覆盖 rm -rf、sudo、curl|bash
□ 敏感文件 (.env, .pem, .key) 被 deny 保护
□ PreToolUse Hook 已部署并测试
□ 审计日志已启用
□ API Key 不在代码仓库中
□ settings.local.json 在 .gitignore 中
□ Docker 沙箱已配置资源限制
□ Docker 容器使用非 root 用户
□ Docker 已配置 cap_drop: ALL
□ 网络策略已按需配置(internal: true 或 API 代理)
□ CI/CD 中 Claude Code 使用只读工具
□ 定期审查审计日志
企业团队可以通过 Anthropic 的管理后台(Anthropic Console)下发不可覆盖的安全策略,确保即使开发者修改本地 settings.json 也无法绕过:
{
"managedSettings": {
"permissions": {
"deny": [
"Bash(sudo *)",
"Bash(rm -rf *)",
"Bash(curl * | *)",
"Write(.env*)",
"Write(*.pem)"
]
}
}
}
Managed settings 的优先级高于所有本地配置,且用户无法修改。
# 开发环境:宽松
claude --settings .claude/settings.dev.json
# 测试环境:标准
claude --settings .claude/settings.test.json
# 生产环境:严格
claude --settings .claude/settings.prod.json
或使用环境变量驱动:
#!/usr/bin/env python3
"""env-settings.py — 根据环境变量选择配置"""
import os, json, shutil
env = os.getenv("CLAUDE_ENV", "dev")
settings_files = {
"dev": "settings.dev.json",
"staging": "settings.staging.json",
"prod": "settings.prod.json",
}
src = f".claude/profiles/{settings_files[env]}"
dst = ".claude/settings.json"
shutil.copy2(src, dst)
print(f"已切换到 {env} 环境配置")
VS Code 和 JetBrains 的 Claude Code 插件会读取同一套 settings.json 配置。确保:
- IDE 插件版本与 CLI 版本一致
- IDE 的 workspace settings 不覆盖安全策略
- 在 IDE 中也启用审计日志
| 评估维度 | 说明 |
|---|---|
| 适合训练营类型 | 企业内训(AI 安全专题)、高级开发者训练营、DevOps 安全培训 |
| 学员基础要求 | 已完成 Claude Code 入门 + 至少一个实战项目 |
| 预计教学时长 | 3-4 小时(讲解 1.5h + 动手实践 1.5h + 讨论 1h) |
| 实验环境要求 | 每人一台 Linux/macOS 机器 + Docker 环境 |
| 核心产出 | 学员带走一套完整的安全配置模板 + 5 个 Hook 脚本 + Docker 沙箱配置 |
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
