Claude Code Hooks 安全栅栏:PreToolUse 高危拦截 + PostToolUse 自动格式化

5 阅读3分钟

Hooks 确定性安全栅栏:PreToolUse 拦截 + PostToolUse 自动格式化

本文是「Claude 企业级工程实战手册」专栏第 07 篇。CLAUDE.md 是建议性的(80% 遵守率),Hooks 是确定性的(100% 执行)。本文提供完整可用的拦截脚本和配置文件。


核心认知:建议性 vs 确定性

机制类型遵守率适用场景
CLAUDE.md 规则建议性(Advisory)~80%代码风格、命名规范、架构偏好
Hooks确定性(Deterministic)100%安全拦截、格式化、审计日志

凡是必须每次都发生的事,用 Hook。凡是"最好这样做"的规范,放 CLAUDE.md。


一、Hook 的触发机制

Claude 试图执行 Bash 工具(rm -rf /)
                │
                ▼
        PreToolUse Hook 触发
                │
        调用 pre_bash_sandbox_check.sh
                │
        ┌───────┴───────┐
   (高危命令)      (安全命令)
        ▼                ▼
   Exit Code 2       Exit Code 0
        │                │
   阻断执行           执行命令
   返回原因

支持的 Hook 事件:

  • PreToolUse:工具调用前(Bash、Write、Edit 等)
  • PostToolUse:工具调用后
  • Stop:会话结束时
  • SubagentStop:子 Agent 停止时

二、完整配置文件

.claude/settings.json

{
  "$schema": "https://code.claude.com/docs/schema/settings.json",
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/pre_bash_sandbox_check.sh"
          }
        ]
      },
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/pre_write_protect.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/post_write_format.sh"
          }
        ]
      }
    ]
  },
  "compactPrompt": "在压缩会话历史时,必须完整保留 CLAUDE.md 中定义的禁忌规则、安全拦截规范,以及所有未解决的 TODO。严禁泛化合并。"
}

三、PreToolUse:高危命令拦截脚本

.claude/hooks/pre_bash_sandbox_check.sh

#!/bin/bash
# PreToolUse 拦截:阻断危险 Shell 命令与未授权操作

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# 空命令直接放行
if [ -z "$COMMAND" ]; then
  exit 0
fi

# ── 高危命令黑名单 ──
HIGH_RISK_PATTERNS=(
  "rm -rf /"
  "rm -rf ~"
  "DROP TABLE"
  "TRUNCATE TABLE"
  "DELETE FROM .* WHERE 1=1"
  "git push .* --force"
  "chmod 777"
  "> /etc"
  "curl .* | bash"
  "wget .* | sh"
)

for pattern in "${HIGH_RISK_PATTERNS[@]}"; do
  if echo "$COMMAND" | grep -Ei -q "$pattern"; then
    echo "🛑 SECURITY VIOLATION: 命令匹配高危禁忌正则 [$pattern]"
    echo "该操作已被物理护栏强制截断。如确需执行,请手动运行并添加 # APPROVED 注释。"
    exit 2
  fi
done

# ── 生产环境写操作警告 ──
if echo "$COMMAND" | grep -Ei -q "\bprod\b|\bproduction\b"; then
  echo "⚠️  WARN: 检测到命令涉及生产环境标示 [prod/production]"
  echo "请确认你已获得完整授权,且已在沙箱中验证过该操作。"
  # 仅警告,不阻断(如需阻断改为 exit 2)
fi

# ── 敏感文件写入检查 ──
SENSITIVE_FILES=(".env" ".env.production" "id_rsa" "*.pem" "secrets.yaml")
for pattern in "${SENSITIVE_FILES[@]}"; do
  if echo "$COMMAND" | grep -q "$pattern"; then
    echo "🔐 SECURITY: 检测到对敏感文件的写操作 [$pattern]"
    echo "请确认这是你的本意,敏感文件不应由自动化脚本直接修改。"
    exit 2
  fi
done

exit 0

.claude/hooks/pre_write_protect.sh

#!/bin/bash
# PreToolUse 拦截:保护受限文件不被直接覆写

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [ -z "$FILE_PATH" ]; then
  exit 0
fi

# 受保护文件列表
PROTECTED_FILES=(
  ".env"
  ".env.production"
  ".env.staging"
  "alembic.ini"
  "pyproject.toml"
)

for protected in "${PROTECTED_FILES[@]}"; do
  if [[ "$FILE_PATH" == *"$protected" ]]; then
    echo "🔒 PROTECTED: 文件 [$FILE_PATH] 已被锁定,Claude 不能直接修改。"
    echo "请手动编辑该文件,或在确认后移除保护。"
    exit 2
  fi
done

exit 0

四、PostToolUse:自动格式化脚本

.claude/hooks/post_write_format.sh

#!/bin/bash
# PostToolUse 自动格式化:保证落盘代码 100% 符合 Linter 标准

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# 空路径或文件不存在,跳过
if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
  exit 0
fi

# 按文件类型执行对应格式化
case "$FILE_PATH" in
  *.py)
    # Ruff:修正不规范导入、冗余变量,然后格式化
    ruff check "$FILE_PATH" --fix --quiet 2>/dev/null
    ruff format "$FILE_PATH" --quiet 2>/dev/null
    echo "✅ Python 格式化完成:$FILE_PATH"
    ;;
  *.ts|*.tsx|*.js|*.jsx)
    npx prettier --write "$FILE_PATH" --loglevel silent 2>/dev/null
    echo "✅ TypeScript/JavaScript 格式化完成:$FILE_PATH"
    ;;
  *.json)
    # 验证 JSON 格式
    if ! python3 -c "import json; json.load(open('$FILE_PATH'))" 2>/dev/null; then
      echo "⚠️  警告:$FILE_PATH 不是合法的 JSON 文件"
    fi
    npx prettier --write "$FILE_PATH" --parser json --loglevel silent 2>/dev/null
    ;;
  *.go)
    gofmt -w "$FILE_PATH" 2>/dev/null
    echo "✅ Go 格式化完成:$FILE_PATH"
    ;;
  *.yaml|*.yml)
    # YAML 格式验证
    if ! python3 -c "import yaml; yaml.safe_load(open('$FILE_PATH'))" 2>/dev/null; then
      echo "⚠️  警告:$FILE_PATH 不是合法的 YAML 文件"
    fi
    ;;
esac

exit 0

五、让脚本生效

# 1. 添加执行权限
chmod +x .claude/hooks/pre_bash_sandbox_check.sh
chmod +x .claude/hooks/pre_write_protect.sh
chmod +x .claude/hooks/post_write_format.sh

# 2. 确保依赖工具已安装
pip install ruff black        # Python 格式化
npm install -g prettier       # JS/TS 格式化

# 3. 验证 Hook 配置
cat .claude/settings.json | jq '.hooks'

# 4. 测试拦截效果(在 Claude Code 里运行)
# 输入:rm -rf /tmp/test
# 预期:被拦截,显示 SECURITY VIOLATION

六、企业进阶:集中式 Hook 管理

对于大型团队,Hook 脚本应集中管理:

# 通过 Git Submodule 引入团队共享 Hook 库
git submodule add https://github.com/your-org/claude-hooks .claude/shared-hooks

# 在 settings.json 中引用共享 Hook
# "command": "bash .claude/shared-hooks/security/pre_bash_check.sh"

下一篇:08. 三阶段工作流


专栏导航 · Claude 企业级工程实战手册

⬅️ 上一篇:06. CLAUDE.md 五层知识体系:让每个 Claude Code 会话自动对齐团队规范 ➡️ 下一篇:08. Claude Code 三阶段工作流:Plan Mode → 并行子 Agent → 物理验收标准

本专栏共 14 篇,系统覆盖 Claude 模型选型 / Prompt 工程 / Claude Code 工作流 / API 高级用法 / MCP / RAG / AI 安全合规全链路。欢迎收藏:Claude 企业级工程实战手册