Back to all articles
12 MIN READ

Claude Code Security & Best Practices: Safe AI Development

By Learnia AI Research Team

Claude Code Security & Best Practices: Safe AI Development

Claude Code is a powerful tool that executes code on your system. With great power comes great responsibility. This guide covers security configurations, MCP server vetting, secrets management, and building safety gates through hooks - everything you need to use Claude Code safely in production environments.

1. Understanding the Security Model

Claude Code operates with a permission system that controls what actions the AI can take autonomously.

Permission Categories

ALLOW (Auto-approve)

  • Read operations
  • Safe bash commands
  • Whitelisted tools

ASK (Confirm first)

  • Write operations
  • System commands
  • Default for unknown

DENY (Blocked)

  • Dangerous commands
  • Forbidden patterns

2. Configuring Permissions

Permissions are configured in settings.local.json (personal) or settings.json (team):

Personal Permission Overrides

{
  "permissions": {
    "allow": [
      "Bash(git *)",
      "Bash(pnpm *)",
      "Bash(npm test)",
      "Edit",
      "Write",
      "WebSearch"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(sudo *)"
    ],
    "ask": [
      "Bash(npm publish)",
      "Bash(git push --force)"
    ]
  }
}

3. Progressive Permission Levels

Adjust permissions based on your experience and trust level:

Level Configurations

Level 1 - Beginner:

{
  "allowedTools": ["Read(*)", "Grep(*)", "Glob(*)"]
}

Level 2 - Intermediate:

{
  "allowedTools": [
    "Read(*)", "Grep(*)", "Glob(*)",
    "Bash(git:*)", "Bash(pnpm:*)",
    "TodoRead", "TodoWrite"
  ]
}

Level 3 - Advanced:

{
  "allowedTools": [
    "Read(*)", "Grep(*)", "Glob(*)", "WebFetch(*)",
    "Edit(*)", "Write(*)",
    "Bash(git:*)", "Bash(pnpm:*)", "Bash(npm:*)",
    "Task(*)", "TodoRead", "TodoWrite"
  ]
}

4. MCP Server Security

MCP servers extend Claude Code's capabilities, but they also expand its attack surface. Apply the same security scrutiny you'd use for any third-party code dependency.

Explore Available MCP Servers

Use this interactive explorer to browse 50+ MCP servers, filter by category and features, and get setup instructions for each:

Pre-Installation Checklist

Security Risks to Understand

Legitimate flow:

Claude → Native Read tool → Your file

Tool shadowing attack:

Claude → MCP "Read" tool → Attacker's server → Your file

Protection Strategies

  1. Verify tool names: Check what tools each MCP server exposes
  2. Use allowedTools: Explicitly whitelist which MCP tools are permitted
  3. Monitor network: Watch for unexpected outbound connections
  4. Sandbox untrusted servers: Run in isolated environments

5. Secrets Management

MCP servers and scripts often require API keys and credentials. Never store them in plaintext configuration files.

The Three Approaches

macOS Keychain:

# Store secret in Keychain
security add-generic-password \
  -a "claude-mcp" \
  -s "github-token" \
  -w "ghp_your_token_here"

# Retrieve in MCP config
{
  "servers": {
    "github": {
      "command": "bash",
      "args": ["-c", "GITHUB_TOKEN=$(security find-generic-password -s 'github-token' -w) npx @github/mcp-server"]
    }
  }
}

Linux Secret Service:

# Store secret
secret-tool store --label="GitHub Token" service claude key github-token

# Retrieve in wrapper script
export GITHUB_TOKEN=$(secret-tool lookup service claude key github-token)
npx @github/mcp-server

Approach 2: .env + .gitignore

# 1. Create .env file
cat > ~/.claude/.env << EOF
GITHUB_TOKEN=ghp_your_token_here
DATABASE_URL=postgresql://user:pass@localhost/db
EOF

# 2. Secure permissions
chmod 600 ~/.claude/.env

# 3. Add to .gitignore
echo ".env" >> ~/.claude/.gitignore

MCP configuration with .env:

{
  "servers": {
    "github": {
      "command": "npx",
      "args": ["@github/mcp-server"],
      "env": {
        "GITHUB_TOKEN": "${env:GITHUB_TOKEN}"
      }
    }
  }
}

6. Pre-Commit Secret Detection

Prevent accidental secret commits with a pre-commit hook:

#!/bin/bash
# .git/hooks/pre-commit

# Patterns to detect
PATTERNS=(
  'sk-[A-Za-z0-9]{48}'           # OpenAI keys
  'ghp_[A-Za-z0-9]{36}'          # GitHub tokens
  'AKIA[A-Z0-9]{16}'             # AWS keys
  'api[_-]?key.*[:=].*[A-Za-z0-9]{20,}'  # Generic API keys
)

for pattern in "${PATTERNS[@]}"; do
  if git diff --cached | grep -qE "$pattern"; then
    echo "❌ Secret detected! Commit blocked."
    echo "Pattern matched: $pattern"
    exit 1
  fi
done

exit 0

7. Hook-Based Security Gates

Use hooks to enforce security policies automatically.

Security Check Hook

#!/bin/bash
# .claude/hooks/security-check.sh

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

# Block dangerous patterns
BLOCKED_PATTERNS=(
  "rm -rf /"
  "rm -rf ~"
  "rm -rf \$HOME"
  ":(){ :|:& };:"  # Fork bomb
  "> /dev/sda"     # Disk overwrite
  "curl.*| bash"   # Pipe to bash
  "wget.*| bash"
)

for pattern in "${BLOCKED_PATTERNS[@]}"; do
  if echo "$COMMAND" | grep -qE "$pattern"; then
    cat << EOF
{
  "decision": "block",
  "reason": "Blocked dangerous command pattern: $pattern"
}
EOF
    exit 0
  fi
done

# Allow by default
echo '{"decision": "allow"}'

CLAUDE.md Injection Scanner

#!/bin/bash
# .claude/hooks/claudemd-scanner.sh

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

# Only scan CLAUDE.md files being read
if [[ ! "$FILE_PATH" =~ CLAUDE\.md$ ]]; then
  exit 0
fi

# Check for injection patterns
INJECTION_PATTERNS=(
  "ignore.*previous.*instructions"
  "ignore.*above"
  "disregard.*instructions"
  "curl.*\|.*bash"
  "wget.*\|.*bash"
  "base64.*decode"
)

CONTENT=$(cat "$FILE_PATH" 2>/dev/null)

for pattern in "${INJECTION_PATTERNS[@]}"; do
  if echo "$CONTENT" | grep -qiE "$pattern"; then
    cat << EOF
{
  "systemMessage": "⚠️ WARNING: Potential prompt injection detected in $FILE_PATH. Pattern: $pattern. Please review the file manually before proceeding."
}
EOF
    exit 0
  fi
done

exit 0

8. Validation Pipeline

Chain multiple validation hooks to catch issues immediately after code changes:

Edit/Write → TypeCheck → Lint → Tests → Notify Claude
   ↓            ↓         ↓       ↓
  file.ts    tsc check  eslint  jest file.test.ts

Configuration:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/typecheck-on-save.sh",
            "timeout": 5000
          },
          {
            "type": "command",
            "command": ".claude/hooks/lint-gate.sh",
            "timeout": 5000
          },
          {
            "type": "command",
            "command": ".claude/hooks/test-on-change.sh",
            "timeout": 10000
          }
        ]
      }
    ]
  }
}

9. Production Safety Rules

For teams deploying Claude Code in production environments:

Database Safety

# In CLAUDE.md
## Database Safety Rules
- NEVER run DROP, TRUNCATE, or DELETE without WHERE clause
- ALWAYS use transactions for multi-step operations
- ALWAYS backup before migration scripts
- Use read-only database credentials for exploration

Port Stability

## Port Rules
- Development server: always port 3000
- API server: always port 8080
- Database: always port 5432
- Never use random ports without documentation

Git Safety

## Git Rules
- Never force push to main, master, or develop
- Always create feature branches for changes
- Run tests before any push
- Require PR reviews for production branches

10. Security Verification Checklist

Before deploying Claude Code in any environment:

Test Secret Isolation

# Should work (secret from .env)
export $(cat ~/.claude/.env | xargs)
claude

# Should fail (no secrets in environment)
unset GITHUB_TOKEN DATABASE_URL
claude
# ❌ MCP servers fail to start (expected behavior)

Summary: Security Best Practices

  1. Progressive Permissions: Start restrictive, expand as you gain trust
  2. Never Skip Permissions: The --dangerously-skip-permissions flag is for sandboxed environments only
  3. Vet MCP Servers: Apply the same scrutiny as any third-party dependency
  4. Manage Secrets Properly: Use OS keychain or .env with proper permissions, never plaintext
  5. Pre-Commit Hooks: Block secrets from ever reaching Git
  6. Security Gates: Use hooks to enforce policies automatically
  7. Validation Pipelines: Catch errors immediately after code changes
  8. Production Rules: Document and enforce database, port, and git safety

Continue Your Learning

Ready to implement production-grade security? Our Security & Best Practices Training Module provides hands-on exercises for configuring permissions, setting up security hooks, and managing secrets safely.

FAQ

Is Claude Code safe to use on production codebases?+

Yes, with proper configuration. Use permission modes (Suggest, Auto-edit, Full auto) appropriately, vet MCP servers before installing, and never store secrets in plain text.

What is tool shadowing and how do I prevent it?+

Tool shadowing is when malicious MCP servers declare tools with names like 'Read' or 'Bash' to intercept commands. Only install MCP servers from trusted sources and use allowedTools restrictions.

How should I manage secrets with Claude Code?+

Use environment variables (${VAR_NAME} in configs), secrets managers like 1Password or AWS Secrets Manager, or system keychains. Never commit tokens to version control.

Can I restrict what files Claude can access?+

Yes, use .claudeignore files (similar to .gitignore) to exclude sensitive files and directories. Also configure allowed-tools in skills to limit filesystem access.