Enterprise Security Patterns for Claude Code
Production-grade security architecture for Claude Code deployments with compliance, audit, and policy enforcement.
Permission Hardening by Role
Developer (Restricted Privileges)
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"Edit",
"Bash(npm test)",
"Bash(npm run dev)",
"Bash(git status)",
"Bash(git diff)",
"Bash(git log)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(sudo *)",
"Bash(chmod 777 *)",
"Write",
"WebFetch",
"WebSearch"
]
}
}
Code Reviewer (Read-Heavy)
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"Bash(git log *)",
"Bash(git show *)",
"Bash(git diff *)"
],
"deny": [
"Write",
"Edit",
"Bash(git commit *)",
"Bash(git push *)",
"Bash(git rebase *)"
]
}
}
Administrator (Full Access with Audit)
{
"permissions": {
"allow": [
"Read",
"Write",
"Edit",
"Glob",
"Grep",
"Bash(*)",
"Agent",
"WebFetch"
]
},
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bash /opt/audit/admin-audit-log.sh"
}
]
}
]
}
}
Secrets Management
Preventing Credential Exposure
PreToolUse hook to block access to credential files:
#!/bin/bash
# name: block-credential-files.sh
# Prevent Claude from reading .env, .pem, .key, .secret files
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name' 2>/dev/null)
PATH_ARG=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""' 2>/dev/null)
# List of blocked patterns
BLOCKED_PATTERNS=(
'\.env'
'\.env\..*'
'\.pem$'
'\.key$'
'\.secret$'
'\.jks$'
'credentials\.json'
'service-account\.json'
'firebase-key\.json'
'/vault/'
'/secrets/'
'/private/'
)
# Check each pattern
for pattern in "${BLOCKED_PATTERNS[@]}"; do
if echo "$PATH_ARG" | grep -qE "$pattern"; then
echo '{
"decision": "deny",
"reason": "Access to credential files is blocked",
"blocked_pattern": "'$pattern'"
}'
exit 0
fi
done
echo '{"decision": "approve"}'
Vault Integration Pattern
For HashiCorp Vault:
#!/bin/bash
# name: vault-credential-retrieval.sh
# Fetch credentials from Vault instead of storing locally
SECRET_NAME=$1
VAULT_ADDR=${VAULT_ADDR:-"https://vault.internal:8200"}
VAULT_TOKEN=${VAULT_TOKEN:-}
if [ -z "$VAULT_TOKEN" ]; then
echo '{"error": "VAULT_TOKEN not set"}'
exit 1
fi
curl -s \
-H "X-Vault-Token: $VAULT_TOKEN" \
"$VAULT_ADDR/v1/secret/data/$SECRET_NAME" | jq -r '.data.data'
AWS Secrets Manager Pattern
#!/bin/bash
# name: aws-secrets-retrieval.sh
SECRET_NAME=$1
REGION=${AWS_REGION:-us-east-1}
aws secretsmanager get-secret-value \
--secret-id "$SECRET_NAME" \
--region "$REGION" \
--query 'SecretString' \
--output text 2>/dev/null
Git Hook to Prevent Secret Commits
#!/bin/bash
# name: pre-commit-secret-scan.sh
# Placed in .git/hooks/pre-commit
# Patterns that suggest credentials
PATTERNS=(
'PRIVATE.*KEY'
'PASSWORD.*='
'APIKEY'
'api.key'
'secret_access_key'
'aws_secret'
'postgresql://.*:.*@'
'mongodb+srv://.*:.*@'
)
EXIT_CODE=0
for file in $(git diff --cached --name-only); do
for pattern in "${PATTERNS[@]}"; do
if git diff --cached "$file" | grep -qiE "$pattern"; then
echo "ERROR: Potential secret detected in $file (pattern: $pattern)"
EXIT_CODE=1
fi
done
done
exit $EXIT_CODE
Audit Logging
Comprehensive Audit Trail Hook
#!/bin/bash
# name: audit-trail.sh
# PostToolUse hook: log all tool usage with context
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name' 2>/dev/null)
RESULT=$(echo "$INPUT" | jq -r '.tool_result' 2>/dev/null)
USER=${CLAUDE_USER:-"unknown"}
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
AUDIT_LOG="/var/log/claude-code/audit.log"
# Truncate output for logging (first 200 chars)
RESULT_SUMMARY=$(echo "$RESULT" | head -c 200)
INPUT_SUMMARY=$(echo "$INPUT" | jq -r '.tool_input | @json' 2>/dev/null | head -c 200)
# Append to audit log with atomic write
{
flock -x 200
echo "{
\"timestamp\": \"$TIMESTAMP\",
\"user\": \"$USER\",
\"tool\": \"$TOOL\",
\"input_summary\": \"$INPUT_SUMMARY\",
\"output_length\": $(echo "$RESULT" | wc -c),
\"status\": \"success\"
}" >> "$AUDIT_LOG"
} 200>"/var/log/claude-code/audit.log.lock"
# Also log file modifications
if [[ "$TOOL" == "Write" ]] || [[ "$TOOL" == "Edit" ]]; then
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""')
echo "{
\"timestamp\": \"$TIMESTAMP\",
\"user\": \"$USER\",
\"action\": \"file_modified\",
\"file\": \"$FILE_PATH\",
\"tool\": \"$TOOL\"
}" >> "/var/log/claude-code/file-changes.log"
fi
Log Rotation Configuration
#!/bin/bash
# name: setup-audit-log-rotation.sh
# Configure logrotate for Claude Code audit trails
cat > /etc/logrotate.d/claude-code << 'EOF'
/var/log/claude-code/*.log {
daily
rotate 90
compress
delaycompress
missingok
notifempty
create 0600 root root
sharedscripts
postrotate
# Notify SIEM or archival system
/opt/claude-code/archive-audit-logs.sh
endscript
}
EOF
chmod 644 /etc/logrotate.d/claude-code
SOC2 Compliance Mapping
CC-6.1 Logical Access Control
Control: Restrict access to systems and data.
Implementation:
- Permission modes (plan, default, acceptEdits) limit tool access
- Allow/deny lists in settings.json prevent unauthorized tool use
- Managed settings prevent user override
- Pre-commit hooks block credential commits
Evidence Collection:
# Audit: Current permissions configuration
grep -r "permissions" ~/.claude/settings.json
# Audit: Session permission changes
grep "permission_mode" /var/log/claude-code/audit.log
# Audit: Denied tool uses
grep '"decision": "deny"' /var/log/claude-code/audit.log | wc -l
CC-7.1 System Monitoring and Anomaly Detection
Control: Monitor activity for anomalies.
Implementation:
- PostToolUse hooks capture all tool calls with timestamps
- File change tracking logs modifications with user
- Login events tracked via forceLoginMethod
- Cost tracking detects unusual API usage patterns
Evidence Collection:
# Daily activity summary
awk '$tool == "Bash(rm*)" {rm_count++} END {
print "Dangerous Bash uses: " rm_count
}' /var/log/claude-code/audit.log
# User activity timeline
grep '"user":' /var/log/claude-code/audit.log | \
jq -s 'group_by(.user) |
map({user: .[0].user, count: length})'
CC-8.1 Change Management
Control: Authorize and document changes.
Implementation:
- Git hooks require pre-commit message format
- File modification audit trail (tool, user, timestamp)
- Pre-push hooks verify branch protection
- PostToolUse hooks log all code changes
Evidence Collection:
# Git change log with user and timestamp
git log --pretty=format:"%h %an %ad %s" --date=short
# Files modified in past 30 days
find . -mtime -30 -type f | xargs ls -lt | head -20
HIPAA Compliance Template
Protected Health Information (PHI) prevention hooks:
#!/bin/bash
# name: hipaa-phi-guard.sh
# PreToolUse: Block tools that could expose PHI
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name' 2>/dev/null)
# Reject tools that could output to insecure locations
case "$TOOL" in
WebFetch|WebSearch)
echo '{"decision": "deny", "reason": "WebFetch/WebSearch blocked in HIPAA environment"}'
exit 0
;;
Bash)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command' 2>/dev/null)
# Block commands that might exfiltrate data
if echo "$CMD" | grep -qE '(curl|wget|scp|rsync|nc).*[^127\.0\.0\.1]'; then
echo '{"decision": "deny", "reason": "Network exfiltration patterns blocked"}'
exit 0
fi
;;
esac
echo '{"decision": "approve"}'
PostToolUse hook to prevent PHI in responses:
#!/bin/bash
# name: hipaa-output-sanitizer.sh
# PostToolUse: Scan outputs for PHI patterns
INPUT=$(cat)
OUTPUT=$(echo "$INPUT" | jq -r '.tool_result' 2>/dev/null)
# Common PHI patterns
PHI_PATTERNS=(
'[0-9]{3}-[0-9]{2}-[0-9]{4}' # SSN
'[0-9]{16,19}' # Credit card
'MRN[:\s]*[0-9]{6,12}' # Medical Record Number
)
for pattern in "${PHI_PATTERNS[@]}"; do
if echo "$OUTPUT" | grep -qE "$pattern"; then
echo "{
\"warning\": \"PHI pattern detected in output\",
\"pattern\": \"$pattern\",
\"action\": \"sanitize\"
}"
# Do not output the matched text
exit 1
fi
done
GDPR Data Handling
Retention Policy
{
"dataRetention": {
"auditLogs": "90 days",
"fileLogs": "30 days",
"sessionLogs": "7 days",
"backups": "365 days"
},
"zeroDataRetention": true
}
PII Detection Hook
#!/bin/bash
# name: gdpr-pii-detector.sh
# PreToolUse: Detect PII in inputs and flag for review
INPUT=$(cat)
# PII patterns
PII_PATTERNS=(
'name[:\s]*[A-Z][a-z]+ [A-Z]'
'email[:\s]*[a-z0-9._%+-]+@'
'phone[:\s]*\+?[0-9\s().-]{10,}'
'address[:\s]*[0-9]+ .{5,}'
)
for pattern in "${PII_PATTERNS[@]}"; do
if echo "$INPUT" | grep -qiE "$pattern"; then
echo "{
\"warning\": \"PII detected in input\",
\"pattern\": \"$pattern\",
\"user_confirmation_required\": true
}"
exit 1
fi
done
echo '{"decision": "approve"}'
SSO and Identity Integration
Anthropic API with Organization
{
"auth": {
"method": "apiKey",
"apiKey": "${ANTHROPIC_API_KEY}",
"organizationId": "${ANTHROPIC_ORG_ID}",
"forceLoginOrgUUID": "org-12345"
}
}
AWS Bedrock Integration
{
"auth": {
"method": "bedrock",
"region": "us-east-1",
"roleArn": "arn:aws:iam::123456789:role/claude-code-role",
"assumeRoleSessionName": "claude-code-session"
}
}
Google Cloud Vertex AI
{
"auth": {
"method": "vertexAI",
"projectId": "my-gcp-project",
"location": "us-central1",
"serviceAccountPath": "/etc/gcloud/service-account.json"
}
}
Network Security
Proxy Configuration for Egress Control
export GLOBAL_AGENT_HTTP_PROXY="http://proxy.internal:3128"
export GLOBAL_AGENT_HTTPS_PROXY="https://proxy.internal:3128"
export NO_PROXY="localhost,127.0.0.1,*.internal,.local"
Air-Gapped Setup (Bedrock)
For deployments without internet access:
{
"auth": {
"method": "bedrock",
"vpcEndpoint": "vpce-12345678",
"region": "us-east-1"
},
"network": {
"allowedDomains": [
"bedrock.us-east-1.amazonaws.com"
],
"blockPublicInternet": true
}
}
MCP Server Domain Allowlist
{
"mcpServers": {
"filesystem": {
"command": "node",
"args": ["mcp-filesystem-server.js"],
"allowedPaths": ["/app", "/data"],
"allowNetworkAccess": false
},
"web": {
"allowedDomains": [
"api.internal",
"docs.internal"
]
}
}
}
Managed Settings Enforcement
Organization-Level Settings (macOS)
Place at /Library/Application Support/ClaudeCode/CLAUDE.md and settings.json:
{
"managedSettings": {
"version": "1.0",
"enforced": true,
"permissions": {
"deny": [
"Bash(curl * | bash)",
"Bash(wget * | bash)",
"Bash(sudo *)",
"Bash(chmod 777 *)"
]
},
"model": "claude-sonnet-4-6",
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "/opt/audit/enterprise-audit.sh"
}
]
}
]
},
"network": {
"proxy": "https://proxy.corp:3128"
}
}
}
Linux System-Wide Settings
Place at /etc/claude-code/CLAUDE.md and /etc/claude-code/settings.json.
Windows Group Policy
Store at HKEY_LOCAL_MACHINE\Software\Policies\ClaudeCode\:
New-Item -Path "HKLM:\Software\Policies\ClaudeCode" -Force
New-ItemProperty -Path "HKLM:\Software\Policies\ClaudeCode" `
-Name "PermissionMode" -Value "plan" -PropertyType String
Incident Response Playbook
If Claude Generates Credentials
- Immediate: Stop the session (
Ctrl+C) - Contain: Run credential rotation script
- Audit: Check audit logs for exposure
- Remediate: Revoke any exposed credentials
- Document: Record incident in security log
#!/bin/bash
# name: incident-response-credential-leak.sh
INCIDENT_ID=$(date +%s)
INCIDENT_LOG="/var/log/claude-code/incidents.log"
echo "{
\"incident_id\": \"$INCIDENT_ID\",
\"type\": \"credential_exposure\",
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
\"status\": \"investigating\",
\"actions_taken\": []
}" > "/tmp/incident-$INCIDENT_ID.json"
# Rotate all API keys
echo " Revoking API keys..."
# Call credential rotation service
# Archive audit logs for forensics
echo " Archiving audit logs..."
tar czf "/var/log/claude-code/incident-$INCIDENT_ID.tar.gz" \
/var/log/claude-code/*.log
echo " Incident $INCIDENT_ID logged"
If Sensitive Data Output
- Check: Grep audit logs for the data pattern
- Scope: Determine who had access
- Notify: Alert security team
- Retain: Keep logs for compliance investigation
#!/bin/bash
# name: audit-forensics.sh
DATA_PATTERN="$1"
LOG_DIR="/var/log/claude-code"
echo "=== Forensic Analysis ==="
echo "Pattern: $DATA_PATTERN"
echo ""
echo "Occurrences in audit trail:"
grep -rn "$DATA_PATTERN" "$LOG_DIR" | wc -l
echo ""
echo "Timeline:"
grep -rn "$DATA_PATTERN" "$LOG_DIR" | cut -d: -f3 | jq '.timestamp'
Enterprise Security Checklist
- [ ] Permission mode set to non-permissive (not bypassPermissions)
- [ ] Deny list covers destructive bash commands (rm, mkfs, dd, chmod 777)
- [ ] PreToolUse hook blocks .env, .pem, .key file access
- [ ] PostToolUse hook logs all tool calls with timestamp and user
- [ ] Vault or Secrets Manager integration configured
- [ ] Pre-commit hook prevents credential commits
- [ ] Audit logs persist to /var/log/claude-code/
- [ ] Log rotation configured with 90-day retention
- [ ] Managed settings prevent user override
- [ ] SSO method configured (Anthropic, Bedrock, or Vertex)
- [ ] Network proxy configured for egress control
- [ ] MCP servers vetted for security
- [ ] SOC2/HIPAA/GDPR compliance policies documented
- [ ] Incident response playbook tested
- [ ] Security team trained on audit log access