Skip to main content
Security Best Practices Enterprise Hooks Agents

Security-First Development with Claude Code

How to ensure your Claude Code workflow produces secure code. From security-auditor agent to PreToolUse hooks and automated auditing.

January 10, 2026 16 min read By ClaudeWorld

AI-assisted development raises an important question: How do you ensure the code Claude writes is secure?

The answer isn’t to avoid AI assistance—it’s to build security into your workflow using Claude Code’s official tools: the security-auditor agent, PreToolUse hooks, and permission settings.

This guide covers how to configure Claude Code for security-conscious development using official features.

Official Security Tools

The security-auditor Agent

According to the Claude Code documentation, the security-auditor is a specialized agent for vulnerability detection:

Task({
  subagent_type: "security-auditor",
  model: "sonnet",  // or "opus" for critical code
  prompt: `
    Audit the authentication module for security vulnerabilities.
    Check for:
    - OWASP Top 10 vulnerabilities
    - Hardcoded secrets
    - SQL injection patterns
    - XSS vulnerabilities
    - Authentication bypass risks
  `
})

When to use security-auditor:

ScenarioModelWhy
Auth/payment changesSonnet/OpusCritical code
New API endpointsSonnetInput validation
File upload handlingSonnetSanitization
External API integrationSonnetData exposure

Hooks for Automatic Security Enforcement

From the official hooks documentation, hooks automatically enforce security policies without manual intervention.

PreToolUse Hook - Block Dangerous Operations

Configure in .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "prompt",
        "prompt": "If the command contains destructive operations (rm -rf, DROP TABLE, git push --force), return 'ask' for user confirmation. Otherwise return 'approve'."
      }]
    }]
  }
}

Available hook events:

EventWhenSecurity Use Case
PreToolUseBefore tool executionBlock dangerous commands
PostToolUseAfter tool executionAudit file changes
StopAgent considers stoppingVerify security review
SessionStartSession beginsLoad security context

Stop Hook - Verify Security Review

{
  "hooks": {
    "Stop": [{
      "matcher": "*",
      "hooks": [{
        "type": "prompt",
        "prompt": "If code in auth/, payment/, or admin/ was modified, verify security-auditor was run. If not, block and explain."
      }]
    }]
  }
}

Permission Settings

From the Claude Code documentation, use settings.json for access control:

{
  "permissions": {
    "allow": [
      "Read",
      "Write",
      "Edit",
      "Bash(npm run:*)",
      "Bash(git:*)"
    ],
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Read(secrets/**)",
      "Read(**/*credentials*)",
      "Bash(*rm -rf*)",
      "Bash(*DROP*)"
    ]
  }
}

Permission pattern syntax:

  • Bash(npm run:*) - Allow all npm run commands
  • Read(.env) - Deny reading .env files
  • Read(secrets/**) - Deny reading anything in secrets/
  • Bash(*DROP*) - Deny commands containing DROP

The Security Challenge

When Claude writes code, several risks exist:

  • Insecure patterns copied from training data
  • Missing input validation
  • Improper error handling that leaks information
  • Hardcoded credentials or secrets
  • SQL injection, XSS, and other vulnerabilities

The solution: codify your security requirements using agents and hooks so Claude follows them automatically.

Building a Security-First CLAUDE.md

Security Section Template

# Security Requirements

## Non-Negotiables
These rules cannot be overridden for any reason:

1. **No Secrets in Code**
   - Never hardcode credentials, API keys, or tokens
   - Use environment variables for all secrets
   - Reference secrets through secure config management

2. **Input Validation**
   - All user inputs must be validated
   - Use allowlist validation over blocklist
   - Validate on both client and server

3. **Output Encoding**
   - Encode all outputs for the context (HTML, URL, JS)
   - Use framework-provided escaping mechanisms
   - Never concatenate raw user input into responses

4. **Authentication & Authorization**
   - All endpoints must verify authentication
   - Check authorization before every action
   - Use principle of least privilege

5. **Data Protection**
   - Encrypt sensitive data at rest and in transit
   - Minimize data collection and retention
   - Log access to sensitive data

## Security Review Requirements
These types of changes require explicit security review:
- Authentication/authorization logic
- Payment processing
- Personal data handling
- API endpoint creation
- Database queries
- File uploads
- Email sending
- External API calls

Input Validation Standards

# Input Validation Standards

## Validation Library
Use Zod for all runtime validation:

\`\`\`typescript
import { z } from 'zod';

// Define schema
const UserSchema = z.object({
  email: z.string().email(),
  age: z.number().min(0).max(150),
  name: z.string().min(1).max(100),
});

// Validate
const result = UserSchema.safeParse(input);
if (!result.success) {
  throw new ValidationError(result.error);
}
\`\`\`

## Validation Rules
- String inputs: Define max length
- Numeric inputs: Define valid range
- Email: Use proper email validation
- URLs: Validate protocol and domain
- IDs: Validate format (UUID, numeric, etc.)
- Arrays: Validate length and item types
- Objects: Validate required fields

Secure Coding Patterns

# Secure Coding Patterns

## Database Queries
ALWAYS use parameterized queries:

\`\`\`typescript
// NEVER do this
const query = \`SELECT * FROM users WHERE email = '\${email}'\`;

// ALWAYS do this
const user = await db.user.findUnique({
  where: { email }, // Prisma handles parameterization
});
\`\`\`

## Error Handling
Never expose internal details:

\`\`\`typescript
// NEVER do this
catch (error) {
  return res.status(500).json({ error: error.message });
}

// ALWAYS do this
catch (error) {
  console.error('Internal error:', error); // Log full error
  return res.status(500).json({ error: 'An error occurred' }); // Generic message
}
\`\`\`

## Authentication Checks
Always verify auth at the start:

\`\`\`typescript
export async function handler(req, res) {
  // First: Authenticate
  const user = await getAuthenticatedUser(req);
  if (!user) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  // Second: Authorize
  if (!user.canAccessResource(resourceId)) {
    return res.status(403).json({ error: 'Forbidden' });
  }

  // Then: Proceed with logic
  // ...
}
\`\`\`

Agent Delegation for Security

Configure Claude to automatically involve security review for sensitive changes:

# Agent Delegation Rules

## Automatic Security Review
These changes automatically trigger security-auditor agent:

- Files in: auth/, payment/, admin/
- Functions containing: password, token, secret, key, credential
- Operations: user creation, permission changes, data deletion
- External calls: API integrations, webhooks, email

## Security Agent Configuration
When security-auditor runs, it checks for:
- OWASP Top 10 vulnerabilities
- Hardcoded secrets
- Missing authentication
- SQL injection patterns
- XSS vulnerabilities
- Insecure direct object references
- Missing rate limiting
- Insufficient logging

Implementing with Task Tool

// Automatic security review for auth changes
Task({
  subagent_type: "security-auditor",
  model: "sonnet",
  prompt: `
    Review changes to auth/ directory.
    Check for OWASP Top 10 vulnerabilities:
    1. Injection
    2. Broken authentication
    3. Sensitive data exposure
    4. XML external entities
    5. Broken access control
    6. Security misconfiguration
    7. Cross-site scripting
    8. Insecure deserialization
    9. Using components with known vulnerabilities
    10. Insufficient logging

    Report findings with severity levels.
  `
})

Security Workflow Patterns

Pattern 1: Pre-Implementation Review

Before writing security-sensitive code:

Task({
  subagent_type: "security-auditor",
  model: "sonnet",
  prompt: `
    Planning to implement password reset functionality.
    Review security considerations:
    1. What vulnerabilities should I address?
    2. What OWASP vulnerabilities are relevant?
    3. What's the secure implementation pattern?
    4. What tests should I write?
  `
})

Pattern 2: Implementation with Guard Rails

While implementing with automatic hooks:

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write",
      "hooks": [{
        "type": "prompt",
        "prompt": "If the written file is in auth/, payment/, or admin/ directories, trigger a security scan and report any issues found."
      }]
    }]
  }
}

Pattern 3: Post-Implementation Audit

After implementing:

Task({
  subagent_type: "security-auditor",
  model: "opus",  // Use Opus for critical security review
  prompt: `
    Comprehensive security audit of password reset feature.

    Check for:
    - Token generation entropy
    - Timing attacks in token comparison
    - Rate limiting bypass
    - User enumeration
    - Session fixation after reset
    - Proper invalidation of old sessions

    Provide findings categorized by severity:
    - Critical
    - High
    - Medium
    - Low
  `
})

Common Vulnerabilities and Prevention

SQL Injection

Vulnerable:

const query = `SELECT * FROM users WHERE id = ${userId}`;

Secure:

const user = await prisma.user.findUnique({ where: { id: userId } });

Hook protection:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Write",
      "hooks": [{
        "type": "prompt",
        "prompt": "If the code contains string concatenation with SQL keywords (SELECT, INSERT, UPDATE, DELETE), return 'block' with warning about SQL injection. Otherwise 'approve'."
      }]
    }]
  }
}

Cross-Site Scripting (XSS)

Vulnerable:

<div dangerouslySetInnerHTML={{ __html: userContent }} />

Secure:

<div>{userContent}</div> // React auto-escapes

CLAUDE.md rule:

## XSS Prevention
- Never use dangerouslySetInnerHTML without sanitization
- Use DOMPurify for HTML content that must be rendered
- Escape all dynamic content in non-React contexts

Authentication Bypass

Vulnerable:

if (user.role === 'admin') {
  // Allow action
}

Secure:

if (await authService.hasPermission(user.id, 'admin:action')) {
  // Allow action
}

Sensitive Data Exposure

Vulnerable:

return res.json({ user }); // Includes password hash!

Secure:

return res.json({ user: sanitizeUser(user) });

Hooks for Continuous Security

Pre-Commit Security Check

{
  "hooks": {
    "Stop": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "npm run security-scan",
        "onFailure": "block"
      }]
    }]
  }
}

Automatic Secret Detection

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Write",
      "hooks": [{
        "type": "prompt",
        "prompt": "Scan the code for potential secrets: API keys, passwords, tokens, private keys. If found, return 'block' with the line numbers. Otherwise 'approve'."
      }]
    }]
  }
}

File Access Control

{
  "permissions": {
    "deny": [
      "Read(.env*)",
      "Read(**/secrets/**)",
      "Read(**/*credential*)",
      "Read(**/*.pem)",
      "Read(**/*.key)",
      "Write(.env*)",
      "Edit(.env*)"
    ]
  }
}

Security Testing with test-runner

Automated Security Test Suite

// Launch parallel security tests
Task({
  subagent_type: "test-runner",
  model: "haiku",
  prompt: "Run all security-related tests in tests/security/"
})

Task({
  subagent_type: "security-auditor",
  model: "sonnet",
  prompt: "Verify test coverage for authentication edge cases"
})

Security Test Example

describe('Password Reset Security', () => {
  it('rejects invalid tokens', async () => {
    const response = await request(app)
      .post('/api/auth/reset-password')
      .send({ token: 'invalid', password: 'newpassword' });

    expect(response.status).toBe(400);
    expect(response.body).not.toContain('token');
  });

  it('prevents timing attacks on token validation', async () => {
    // Use constant-time comparison
    const times = [];
    for (let i = 0; i < 100; i++) {
      const start = performance.now();
      await validateToken(`token${i}`);
      times.push(performance.now() - start);
    }
    // Variance should be minimal
    expect(standardDeviation(times)).toBeLessThan(5);
  });

  it('rate limits reset requests', async () => {
    const email = '[email protected]';

    // First 3 requests succeed
    for (let i = 0; i < 3; i++) {
      const response = await request(app)
        .post('/api/auth/forgot-password')
        .send({ email });
      expect(response.status).toBe(200);
    }

    // 4th request is rate limited
    const response = await request(app)
      .post('/api/auth/forgot-password')
      .send({ email });
    expect(response.status).toBe(429);
  });
});

Security Audit Checklist

Before any deployment:

## Pre-Deployment Security Checklist

### Authentication
- [ ] All endpoints require authentication (except public ones)
- [ ] Tokens expire appropriately
- [ ] Password requirements enforced
- [ ] Account lockout after failed attempts

### Authorization
- [ ] Permission checks on all protected actions
- [ ] No privilege escalation paths
- [ ] Admin functions properly protected

### Data Protection
- [ ] Sensitive data encrypted at rest
- [ ] HTTPS enforced
- [ ] No secrets in code or logs
- [ ] PII handling compliant

### Input/Output
- [ ] All inputs validated
- [ ] All outputs properly encoded
- [ ] File uploads validated and sanitized
- [ ] No SQL injection vulnerabilities

### Logging & Monitoring
- [ ] Security events logged
- [ ] Logs don't contain sensitive data
- [ ] Alerting configured for anomalies

Enterprise Security Features

Claude Code supports enterprise security requirements through the four-layer configuration hierarchy:

Enterprise Layer (Managed Settings)

// /etc/claude-code/settings.json (IT managed)
{
  "permissions": {
    "deny": [
      "Bash(*curl*)",
      "Bash(*wget*)",
      "Read(/etc/**)",
      "Write(/etc/**)"
    ]
  },
  "mcpServers": {
    "allowlist": ["filesystem", "memory"]
  }
}

Audit Logging Configuration

## Audit Requirements
Log these events to secure audit system:
- Authentication attempts (success/failure)
- Authorization decisions
- Data access (especially sensitive data)
- Configuration changes
- API calls to external services

Compliance Integration

## Compliance Requirements
- PCI DSS: No card data in logs, encrypt at rest
- GDPR: Data minimization, right to deletion
- HIPAA: PHI access controls and audit trails
- SOC 2: Change management documentation

Model Selection for Security

From the Claude Code CHANGELOG:

TaskRecommended ModelWhy
Quick security scanHaiku 4.5Fast pattern matching
Code reviewSonnet 4.5Balanced analysis
Security auditSonnet 4.5Thorough review
Critical securityOpus 4.5Highest intelligence

Quick model switching (v2.0.65+): Press Option+P (macOS) or Alt+P (Windows/Linux) during prompting.

Getting Started

Today:

  1. Add security-auditor delegation rules to CLAUDE.md
  2. Configure permission denials for sensitive files
  3. Add one PreToolUse hook for dangerous commands

This week:

  1. Implement Stop hook for security verification
  2. Add security test suite
  3. Run security-auditor on critical modules

This month:

  1. Deploy pre-commit security hooks
  2. Integrate security scanning in CI/CD
  3. Train team on security-first development

Security is a journey, not a destination. With Claude Code’s agents and hooks, you can automate security best practices.

Sources: Claude Code Documentation, Claude Code GitHub, Hooks Documentation