security-guardrails

Comprehensive security implementation standards. Use when security guardrails guidance is required.

$ 설치

git clone https://github.com/CsHeng/dot-claude /tmp/dot-claude && cp -r /tmp/dot-claude/skills/security-guardrails ~/.claude/skills/dot-claude

// tip: Run this command in your terminal to install the skill


name: security-guardrails description: Comprehensive security implementation standards. Use when security guardrails guidance is required.

Purpose

Provide comprehensive security implementation standards covering credential management, secret rotation, input validation, and other guardrails that can be reused across services.

IO Semantics

Input: Service configurations, deployment environments, and code paths that handle credentials or security-sensitive operations.

Output: Concrete policies, code templates, and operational procedures for secure storage, rotation, and validation of secrets.

Side Effects: Applying these guardrails may require changes to deployment pipelines, secret management systems, and runtime configuration.

Deterministic Steps

1. Credential Management Security

Implement secure credential handling:

  • Use environment variables for all configuration secrets
  • Apply encrypted storage for sensitive environment variables
  • Implement proper access controls for credential files
  • Use secret management services for production environments

Apply secure credential patterns:

# Secure credential access
import os
from cryptography.fernet import Fernet

class SecureConfig:
    def __init__(self):
        self.cipher_suite = Fernet(self._get_encryption_key())

    def get_database_config(self):
        return {
            'host': os.getenv('DB_HOST', 'localhost'),
            'port': int(os.getenv('DB_PORT', '5432')),
            'username': os.getenv('DB_USER'),
            'password': self._decrypt(os.getenv('DB_PASSWORD')),
            'database': os.getenv('DB_NAME')
        }

    def _get_encryption_key(self):
        key_file = os.getenv('ENCRYPTION_KEY_FILE', '/app/.encryption_key')
        with open(key_file, 'rb') as f:
            return f.read()

    def _decrypt(self, encrypted_value):
        if not encrypted_value:
            return None
        return self.cipher_suite.decrypt(encrypted_value.encode()).decode()

2. Secret Rotation Implementation

Automate secret lifecycle management:

#!/bin/bash
# secret-rotation.sh

rotate_database_credentials() {
    local service_name="$1"
    local max_age_days="${2:-90}"

    # Check credential age
    local credential_age=$(find /etc/secrets/ -name "${service_name}_db_*" -mtime +${max_age_days} | wc -l)

    if [ "$credential_age" -gt 0 ]; then
        echo "Rotating credentials for $service_name"

        # Generate new password
        new_password=$(openssl rand -base64 32)

        # Update database user password
        psql -h "$DB_HOST" -U "$DB_ADMIN" -c "ALTER USER ${service_name}_user WITH PASSWORD '$new_password';"

        # Store encrypted new password
        echo "$new_password" | gpg --encrypt --recipient "$GPG_RECIPIENT" > "/etc/secrets/${service_name}_db_password.gpg"

        # Update environment file
        sed -i "s/${service_name}_DB_PASSWORD=.*/${service_name}_DB_PASSWORD=$(echo "$new_password" | gpg --encrypt --armor --recipient "$GPG_RECIPIENT")/" /etc/environment

        echo "Credentials rotated successfully"
    fi
}

Network Security Implementation

TLS Configuration Standards

Implement comprehensive encryption:

# TLS 1.3 only configuration
server {
    listen 443 ssl http2;
    server_name api.example.com;

    # Modern TLS configuration
    ssl_protocols TLSv1.3;
    ssl_prefer_server_ciphers on;

    # Forward secrecy cipher suites
    ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    # HSTS enforcement
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # Security headers
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header Referrer-Policy "strict-origin-when-cross-origin";

    # Certificate configuration
    ssl_certificate /etc/ssl/certs/api.crt;
    ssl_certificate_key /etc/ssl/private/api.key;
    ssl_trusted_certificate /etc/ssl/certs/chain.crt;
}

CORS and Security Headers

Apply comprehensive web security:

# Flask security configuration
from flask import Flask
from flask_cors import CORS
from flask_talisman import Talisman

app = Flask(__name__)

# Strict CORS configuration
CORS(app,
     resources={
         r"/api/*": {
             "origins": ["https://app.example.com"],
             "methods": ["GET", "POST", "PUT", "DELETE"],
             "allow_headers": ["Content-Type", "Authorization"],
             "max_age": 86400
         }
     })

# Security headers with Talisman
csp = {
    'default-src': "'self'",
    'script-src': [
        "'self'",
        "'nonce-${nonce}'",
        "https://trusted-cdn.example.com"
    ],
    'style-src': [
        "'self'",
        "'unsafe-inline'",
        "https://fonts.googleapis.com"
    ],
    'font-src': ["'self'", "https://fonts.gstatic.com"],
    'img-src': ["'self'", "data:", "https:"],
    'connect-src': ["'self'", "https://api.example.com"]
}

Talisman(app,
         force_https=True,
         strict_transport_security=True,
         content_security_policy=csp,
         referrer_policy='strict-origin-when-cross-origin',
         feature_policy={
             'geolocation': "'none'",
             'camera': "'none'",
             'microphone': "'none'"
         })

Input Validation and Sanitization

Comprehensive Input Security

Implement multi-layer validation:

import re
import bleach
from typing import Any, Dict, List
from pydantic import BaseModel, validator, constr

class UserInputValidator:
    # Security patterns
    SQL_INJECTION_PATTERNS = [
        r"(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION)\b)",
        r"(--|#|\/\*|\*\/)",
        r"(;|\|||\|&)",
        r"(\b(OR|AND)\s+\w+\s*=\s*\w+)"
    ]

    XSS_PATTERNS = [
        r"<script[^>]*>.*?</script>",
        r"javascript:",
        r"on\w+\s*=",
        r"<iframe[^>]*>",
        r"<object[^>]*>",
        r"<embed[^>]*>"
    ]

    @classmethod
    def sanitize_input(cls, user_input: str) -> str:
        # Remove HTML tags
        clean_input = bleach.clean(user_input, tags=[], strip=True)

        # Normalize whitespace
        clean_input = ' '.join(clean_input.split())

        return clean_input

    @classmethod
    def detect_sql_injection(cls, input_string: str) -> bool:
        upper_input = input_string.upper()
        for pattern in cls.SQL_INJECTION_PATTERNS:
            if re.search(pattern, upper_input, re.IGNORECASE):
                return True
        return False

    @classmethod
    def detect_xss(cls, input_string: str) -> bool:
        for pattern in cls.XSS_PATTERNS:
            if re.search(pattern, input_string, re.IGNORECASE | re.DOTALL):
                return True
        return False

# Pydantic model for validation
class SecureUserRegistration(BaseModel):
    username: constr(regex=r'^[a-zA-Z0-9_]{3,30}$')
    email: constr(regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
    password: constr(min_length=12, max_length=128)

    @validator('password')
    def validate_password_strength(cls, v):
        if not re.search(r'[A-Z]', v):
            raise ValueError('Password must contain uppercase letter')
        if not re.search(r'[a-z]', v):
            raise ValueError('Password must contain lowercase letter')
        if not re.search(r'\d', v):
            raise ValueError('Password must contain digit')
        if not re.search(r'[!@#$%^&*(),.?":{}|<>]', v):
            raise ValueError('Password must contain special character')
        return v

    @validator('username')
    def validate_username_safe(cls, v):
        if UserInputValidator.detect_sql_injection(v):
            raise ValueError('Invalid characters in username')
        if UserInputValidator.detect_xss(v):
            raise ValueError('Invalid characters in username')
        return v

File Upload Security

Implement secure file handling:

import magic
import hashlib
from werkzeug.utils import secure_filename

class SecureFileUploader:
    ALLOWED_MIME_TYPES = {
        'image/jpeg', 'image/png', 'image/gif',
        'application/pdf', 'text/plain'
    }

    MAX_FILE_SIZE = 10 * 1024 * 1024  # 10MB
    UPLOAD_FOLDER = '/secure/uploads'

    @classmethod
    def validate_file(cls, file) -> Dict[str, Any]:
        result = {'valid': False, 'errors': []}

        # Check file size
        file.seek(0, 2)  # Seek to end
        file_size = file.tell()
        file.seek(0)  # Reset to beginning

        if file_size > cls.MAX_FILE_SIZE:
            result['errors'].append('File too large')
            return result

        # Check file type
        file_content = file.read(1024)
        file.seek(0)

        mime_type = magic.from_buffer(file_content, mime=True)
        if mime_type not in cls.ALLOWED_MIME_TYPES:
            result['errors'].append(f'File type {mime_type} not allowed')
            return result

        # Generate secure filename
        original_filename = file.filename
        secure_name = secure_filename(original_filename)

        # Add hash to prevent filename collisions
        file_hash = hashlib.sha256(file_content).hexdigest()[:8]
        final_filename = f"{file_hash}_{secure_name}"

        result.update({
            'valid': True,
            'secure_filename': final_filename,
            'mime_type': mime_type,
            'size': file_size
        })

        return result

    @classmethod
    def save_file(cls, file, filename: str) -> str:
        file_path = os.path.join(cls.UPLOAD_FOLDER, filename)

        # Ensure directory exists
        os.makedirs(cls.UPLOAD_FOLDER, mode=0o700, exist_ok=True)

        # Save file with restricted permissions
        with open(file_path, 'wb') as f:
            file.save(file_path)

        # Set file permissions
        os.chmod(file_path, 0o600)

        return file_path

Container and Deployment Security

Container Hardening

Implement secure container practices:

# Multi-stage secure build
FROM alpine:3.18 AS builder

# Create non-root user
RUN addgroup -g 1001 -S appgroup && \
    adduser -u 1001 -S appuser -G appgroup

# Install build dependencies
RUN apk add --no-cache \
    ca-certificates \
    tzdata \
    && rm -rf /var/cache/apk/*

# Build application
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
RUN python -m compileall .

# Production stage
FROM alpine:3.18

# Import user from builder stage
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /etc/group /etc/group

# Install runtime dependencies only
RUN apk add --no-cache \
    ca-certificates \
    tzdata \
    && rm -rf /var/cache/apk/* \
    && rm -rf /root/.cache

# Create app directory with proper permissions
WORKDIR /app
COPY --from=builder /app .

# Set ownership and permissions
RUN chown -R appuser:appgroup /app && \
    chmod -R 755 /app && \
    chmod -R 644 /app/*.py

# Switch to non-root user
USER appuser

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8000/health || exit 1

# Expose port
EXPOSE 8000

# Start application with security flags
CMD ["python", "-u", "app.py"]

Security Scanning Integration

Automated security validation:

# GitHub Actions security workflow
name: Security Scan
on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Run Trivy vulnerability scanner
        run: |
          docker run --rm -v $PWD:/app aquasec/trivy:latest image --exit-code 0 --severity HIGH,CRITICAL myapp:latest

      - name: Run Bandit security linter
        run: |
          pip install bandit[toml]
          bandit -r src/ -f json -o bandit-report.json

      - name: Run Safety dependency check
        run: |
          pip install safety
          safety check --json --output safety-report.json

      - name: Run Semgrep security analysis
        run: |
          docker run --rm -v $PWD:/app returntocorp/semgrep:latest semgrep --config=auto --json --output=semgrep-report.json

      - name: Upload security reports
        uses: actions/upload-artifact@v3
        with:
          name: security-reports
          path: |
            bandit-report.json
            safety-report.json
            semgrep-report.json

Security Monitoring Implementation

Intrusion Detection Setup

Implement comprehensive security monitoring:

# Security event monitoring
import json
import logging
from datetime import datetime
from typing import Dict, List

class SecurityMonitor:
    def __init__(self):
        self.logger = logging.getLogger('security')
        self.security_events = []

    def log_security_event(self, event_type: str, severity: str, details: Dict[str, Any]):
        event = {
            'timestamp': datetime.utcnow().isoformat(),
            'event_type': event_type,
            'severity': severity,
            'details': details,
            'source_ip': details.get('source_ip'),
            'user_agent': details.get('user_agent')
        }

        self.security_events.append(event)

        # Log structured security event
        log_entry = json.dumps(event)

        if severity == 'CRITICAL':
            self.logger.critical(log_entry)
            self._send_alert(event)
        elif severity == 'HIGH':
            self.logger.error(log_entry)
        elif severity == 'MEDIUM':
            self.logger.warning(log_entry)
        else:
            self.logger.info(log_entry)

    def detect_anomalous_login(self, user_id: str, ip_address: str, user_agent: str):
        # Check for login from new location
        recent_logins = [e for e in self.security_events
                        if e['event_type'] == 'login' and e['details']['user_id'] == user_id
                        and (datetime.utcnow() - datetime.fromisoformat(e['timestamp'])).seconds < 3600]

        known_ips = {e['details']['ip_address'] for e in recent_logins}

        if ip_address not in known_ips and len(known_ips) > 0:
            self.log_security_event(
                'suspicious_login_location',
                'HIGH',
                {
                    'user_id': user_id,
                    'new_ip': ip_address,
                    'known_ips': list(known_ips),
                    'source_ip': ip_address,
                    'user_agent': user_agent
                }
            )

    def detect_brute_force(self, ip_address: str):
        failed_attempts = len([e for e in self.security_events
                             if e['event_type'] == 'failed_login'
                             and e['details']['ip_address'] == ip_address
                             and (datetime.utcnow() - datetime.fromisoformat(e['timestamp'])).seconds < 300])

        if failed_attempts >= 5:
            self.log_security_event(
                'brute_force_detected',
                'CRITICAL',
                {
                    'ip_address': ip_address,
                    'failed_attempts': failed_attempts,
                    'timeframe': '5 minutes',
                    'source_ip': ip_address
                }
            )

    def _send_alert(self, event: Dict[str, Any]):
        # Integration with alerting system
        alert_message = f"Security Alert: {event['event_type']} - {event['details']}"

        # Send to monitoring system
        # send_to_slack(alert_message)
        # send_to_pagerduty(alert_message)
        # send_email(alert_message)