AIコーディング 2026.04.19

Claude Code制御ルールが機能しない原因と5つの検証方法【コード例付き】

タグ:Claude Code / セキュリティ / ベストプラクティス / AI開発 / コード生成

「ルールを設定した=安全」は誤解

CLAUDE.mdに「本番DBへの接続禁止」「外部APIキーをコードに書かない」と書いても、それはAIへのお願いであって、強制ではありません。Claude Codeが生成したコードにハードコードされたAPIキーが混入していても、設定ファイルは何も教えてくれません。

# これは「守ってほしい」指示にすぎない(強制ではない)
# CLAUDE.md
# 禁止: APIキーをコードに直接書かない
# 禁止: .envファイルを読み取らない
# 必須: 環境変数経由でシークレットを渡す

問題1:ルールが適用されているか確認できない

CLAUDE.mdのルールがClaude Codeの動作にどう反映されたかを見るログが存在しません。確認できるのは最終的に生成されたコードだけです。

生成されたコード ← 「これがルールを守っているか」を人間が判断するしかない

Claude Code(CLAUDE.mdを読む)

CLAUDE.md(ルールが書かれている)

解決策1:--allowedToolsでツール使用を制限する

操作ツールのレベルでは--allowedToolsが有効です:

# 読み取りと特定ファイルの編集のみを許可(ファイル削除・シェル実行は禁止)
claude --allowedTools "Read,Edit(src/**)" \
       -p "src/auth/login.ts のバグを修正して"

# 読み取り専用(コードレビューのみ、変更禁止)
claude --allowedTools "Read" \
       -p "このコードのセキュリティ問題を列挙して"

# Gitコマンドを完全に禁止
claude --disallowedTools "Bash(git *)" \
       -p "テストを追加して"

解決策2:生成コードをpre-commitフックでスキャン

# .git/hooks/pre-commit(実行権限: chmod +x .git/hooks/pre-commit)
#!/bin/bash
set -e

echo "Claude Code生成コードのセキュリティスキャン中..."

# APIキー・シークレットのパターンを検出
PATTERNS=(
  'sk-[a-zA-Z0-9]{32,}'           # Anthropic APIキー
  'ghp_[a-zA-Z0-9]{36}'           # GitHub Personal Access Token
  'AKIA[0-9A-Z]{16}'              # AWS Access Key
  'password\s*=\s*["\047][^\047"]{4,}' # ハードコードされたパスワード
  'api_key\s*=\s*["\047][^\047"]{8,}'  # ハードコードされたAPIキー
)

STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)

for file in $STAGED_FILES; do
  for pattern in "${PATTERNS[@]}"; do
    if grep -qE "$pattern" "$file" 2>/dev/null; then
      echo "❌ 危険なパターン検出: $file"
      echo "   パターン: $pattern"
      echo "   コミットを中止します"
      exit 1
    fi
  done
done

echo "✅ セキュリティスキャン通過"

解決策3:生成コードをPythonで自動スキャン

# scan_generated_code.py
import subprocess
import re
import sys
from pathlib import Path

DANGER_PATTERNS = [
    (r'sk-[a-zA-Z0-9]{32,}', 'Anthropic APIキー'),
    (r'ghp_[a-zA-Z0-9]{36}', 'GitHub PAT'),
    (r'AKIA[0-9A-Z]{16}', 'AWS Access Key'),
    (r'password\s*=\s*["\047][^\047"]{4,}', 'ハードコードパスワード'),
    (r'subprocess\.call\([^)]*shell=True', 'shell=True (コマンドインジェクション危険)'),
    (r'eval\(', 'eval() 使用'),
    (r'exec\(', 'exec() 使用'),
    (r'__import__\(', '動的インポート'),
]

def scan_file(filepath: Path) -> list[dict]:
    findings = []
    try:
        content = filepath.read_text(encoding='utf-8')
        for pattern, description in DANGER_PATTERNS:
            matches = re.finditer(pattern, content, re.IGNORECASE)
            for match in matches:
                line_num = content[:match.start()].count('\n') + 1
                findings.append({
                    'file': str(filepath),
                    'line': line_num,
                    'pattern': description,
                    'snippet': match.group()[:50],
                })
    except (UnicodeDecodeError, PermissionError):
        pass
    return findings

def scan_git_diff() -> list[dict]:
    """git diffで変更ファイルのみをスキャン"""
    result = subprocess.run(
        ['git', 'diff', '--cached', '--name-only', '--diff-filter=ACM'],
        capture_output=True, text=True
    )
    changed_files = result.stdout.strip().split('\n')
    
    all_findings = []
    for filepath_str in changed_files:
        if filepath_str:
            all_findings.extend(scan_file(Path(filepath_str)))
    return all_findings

if __name__ == '__main__':
    findings = scan_git_diff()
    
    if findings:
        print(f"⚠️  {len(findings)}件の問題を検出:")
        for f in findings:
            print(f"  {f['file']}:{f['line']} - {f['pattern']}")
            print(f"    → {f['snippet']}")
        sys.exit(1)
    else:
        print("✅ スキャン通過 - 問題なし")
        sys.exit(0)
# GitHub Actionsでの自動実行
# .github/workflows/scan.yml
name: Claude Code Security Scan
on: [pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Scan for dangerous patterns
        run: python scan_generated_code.py

解決策4:CLAUDE.mdのバージョン管理

ルール変更を追跡するには、CLAUDE.mdをgitで管理してバージョン履歴を残します:

# CLAUDE.mdの変更をgit logで追跡
git log --oneline -- CLAUDE.md

# 変更内容の確認
git diff HEAD~1 HEAD -- CLAUDE.md

# どのバージョンのルールで開発されたかをコミットメッセージに記録
git log --format="%H %s" | grep -i "claude"
<!-- CLAUDE.md のバージョン管理例 -->
# CLAUDE.md
# version: 2026-08-19
# last-updated: 2026-08-19
# チームでの変更は必ずレビュー必須

## 禁止事項(v3から追加)
- 本番DBへの直接接続禁止(事故防止のため)
- APIキーのハードコード禁止

解決策5:多段階の検証フロー

Claude Code生成

pre-commitフック(シークレット・危険パターン自動検出)

コードレビュー(人間によるロジック確認)

SAST(Snyk / Semgrep などの静的解析)

ステージング環境テスト

本番デプロイ
# Semgrepによる静的解析をCIに追加
semgrep --config=auto src/ --json | jq '.results[] | {path, message, severity}'

# Snykでの依存関係スキャン
snyk test --json | jq '.vulnerabilities[] | {title, severity, packageName}'

リスク別の対策まとめ

リスク影響対策
シークレットの混入APIキー漏洩pre-commitフック
危険なコードパターンセキュリティ脆弱性SAST(Semgrep/Snyk)
ルールの形骸化ガバナンス崩壊CLAUDE.mdバージョン管理
意図しない操作データ消失--allowedTools制限
監査証跡なしインシデント調査困難git commitメッセージ記録

CLAUDE.mdのルールは「指示」です。その指示が実際に守られているかを確認するのは人間とツールの仕事です。設定と検証をセットで運用することが、Claude Codeを安全に使うための基本です。


あわせて読みたい

参考ソース