security-headers-configuration

Category: Security compliance
Tags:
security
For: Claude Code

Configure HTTP security headers including CSP, HSTS, X-Frame-Options, and XSS protection. Use when hardening web applications against common attacks.

Installation

Copy to your project
cp -r skills/security-headers-configuration/ /your-project/.claude/skills/security-headers-configuration/

Security Headers Configuration

Overview

Implement comprehensive HTTP security headers to protect web applications from XSS, clickjacking, MIME sniffing, and other browser-based attacks.

When to Use

  • New web application deployment
  • Security audit remediation
  • Compliance requirements
  • Browser security hardening
  • API security
  • Static site protection

Implementation Examples

1. Node.js/Express Security Headers

// security-headers.js
const helmet = require("helmet");

function configureSecurityHeaders(app) {
  // Comprehensive Helmet configuration
  app.use(
    helmet({
      // Content Security Policy
      contentSecurityPolicy: {
        directives: {
          defaultSrc: ["'self'"],
          scriptSrc: [
            "'self'",
            "'unsafe-inline'", // Remove in production
            "https://cdn.example.com",
            "https://www.google-analytics.com",
          ],
          styleSrc: [
            "'self'",
            "'unsafe-inline'",
            "https://fonts.googleapis.com",
          ],
          fontSrc: ["'self'", "https://fonts.gstatic.com"],
          imgSrc: ["'self'", "data:", "https:", "blob:"],
          connectSrc: ["'self'", "https://api.example.com"],
          frameSrc: ["'none'"],
          objectSrc: ["'none'"],
          upgradeInsecureRequests: [],
        },
      },

      // Strict Transport Security
      hsts: {
        maxAge: 31536000, // 1 year
        includeSubDomains: true,
        preload: true,
      },

      // X-Frame-Options
      frameguard: {
        action: "deny",
      },

      // X-Content-Type-Options
      noSniff: true,

      // X-XSS-Protection
      xssFilter: true,

      // Referrer-Policy
      referrerPolicy: {
        policy: "strict-origin-when-cross-origin",
      },

      // Permissions-Policy (formerly Feature-Policy)
      permittedCrossDomainPolicies: {
        permittedPolicies: "none",
      },
    }),
  );

  // Additional custom headers
  app.use((req, res, next) => {
    // Permissions Policy
    res.setHeader(
      "Permissions-Policy",
      "geolocation=(), microphone=(), camera=(), payment=(), usb=()",
    );

    // Expect-CT
    res.setHeader("Expect-CT", "max-age=86400, enforce");

    // Cross-Origin policies
    res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
    res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
    res.setHeader("Cross-Origin-Resource-Policy", "same-origin");

    // Remove powered-by header
    res.removeHeader("X-Powered-By");

    next();
  });
}

// CSP Violation Reporter
app.post(
  "/api/csp-report",
  express.json({ type: "application/csp-report" }),
  (req, res) => {
    const report = req.body["csp-report"];

    console.error("CSP Violation:", {
      documentUri: report["document-uri"],
      violatedDirective: report["violated-directive"],
      blockedUri: report["blocked-uri"],
      sourceFile: report["source-file"],
      lineNumber: report["line-number"],
    });

    // Store in database or send to monitoring service
    // monitoringService.logCSPViolation(report);

    res.status(204).end();
  },
);

module.exports = { configureSecurityHeaders };

2. Nginx Security Headers Configuration

# nginx-security-headers.conf

server {
    listen 443 ssl http2;
    server_name example.com;

    # SSL Configuration
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # Security Headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

    # Content Security Policy
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://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; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; report-uri /api/csp-report" always;

    # Cross-Origin Policies
    add_header Cross-Origin-Embedder-Policy "require-corp" always;
    add_header Cross-Origin-Opener-Policy "same-origin" always;
    add_header Cross-Origin-Resource-Policy "same-origin" always;

    # Expect-CT
    add_header Expect-CT "max-age=86400, enforce" always;

    # Hide server version
    server_tokens off;

    location / {
        root /var/www/html;
        index index.html;
    }

    # API endpoints
    location /api/ {
        proxy_pass http://backend:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Remove backend headers that might leak info
        proxy_hide_header X-Powered-By;
        proxy_hide_header Server;
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

3. Python Flask Security Headers

# security_headers.py
from flask import Flask, make_response
from functools import wraps

app = Flask(__name__)

def add_security_headers(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        resp = make_response(f(*args, **kwargs))

        # Strict Transport Security
        resp.headers['Strict-Transport-Security'] = \
            'max-age=31536000; includeSubDomains; preload'

        # X-Frame-Options
        resp.headers['X-Frame-Options'] = 'DENY'

        # X-Content-Type-Options
        resp.headers['X-Content-Type-Options'] = 'nosniff'

        # X-XSS-Protection
        resp.headers['X-XSS-Protection'] = '1; mode=block'

        # Referrer-Policy
        resp.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'

        # Permissions-Policy
        resp.headers['Permissions-Policy'] = \
            'geolocation=(), microphone=(), camera=(), payment=()'

        # Content Security Policy
        csp = {
            "default-src": ["'self'"],
            "script-src": ["'self'", "https://cdn.example.com"],
            "style-src": ["'self'", "'unsafe-inline'"],
            "img-src": ["'self'", "data:", "https:"],
            "font-src": ["'self'"],
            "connect-src": ["'self'", "https://api.example.com"],
            "frame-ancestors": ["'none'"],
            "base-uri": ["'self'"],
            "form-action": ["'self'"],
            "report-uri": ["/api/csp-report"]
        }

        csp_string = "; ".join([
            f"{key} {' '.join(values)}"
            for key, values in csp.items()
        ])

        resp.headers['Content-Security-Policy'] = csp_string

        # Cross-Origin Policies
        resp.headers['Cross-Origin-Embedder-Policy'] = 'require-corp'
        resp.headers['Cross-Origin-Opener-Policy'] = 'same-origin'
        resp.headers['Cross-Origin-Resource-Policy'] = 'same-origin'

        # Expect-CT
        resp.headers['Expect-CT'] = 'max-age=86400, enforce'

        # Remove server header
        resp.headers.pop('Server', None)

        return resp

    return decorated_function

# Apply to all routes
@app.after_request
def apply_security_headers(response):
    # Same headers as above
    response.headers['Strict-Transport-Security'] = \
        'max-age=31536000; includeSubDomains; preload'
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-XSS-Protection'] = '1; mode=block'

    return response

# CSP Violation endpoint
@app.route('/api/csp-report', methods=['POST'])
def csp_report():
    report = request.get_json()

    print(f"CSP Violation: {report}")

    # Log to monitoring service
    # monitoring.log_csp_violation(report)

    return '', 204

if __name__ == '__main__':
    # Run with HTTPS only
    app.run(ssl_context='adhoc', port=443)

4. Apache .htaccess Configuration

# .htaccess - Apache security headers

# Strict Transport Security
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

# X-Frame-Options
Header always set X-Frame-Options "DENY"

# X-Content-Type-Options
Header always set X-Content-Type-Options "nosniff"

# X-XSS-Protection
Header always set X-XSS-Protection "1; mode=block"

# Referrer-Policy
Header always set Referrer-Policy "strict-origin-when-cross-origin"

# Permissions-Policy
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"

# Content Security Policy
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'none'"

# Cross-Origin Policies
Header always set Cross-Origin-Embedder-Policy "require-corp"
Header always set Cross-Origin-Opener-Policy "same-origin"
Header always set Cross-Origin-Resource-Policy "same-origin"

# Remove server signature
ServerSignature Off
Header unset Server
Header unset X-Powered-By

# Force HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

5. Security Headers Testing Script

// test-security-headers.js
const axios = require("axios");

async function testSecurityHeaders(url) {
  console.log(`\n=== Testing Security Headers for ${url} ===\n`);

  try {
    const response = await axios.get(url, {
      validateStatus: () => true,
    });

    const headers = response.headers;

    const tests = {
      "Strict-Transport-Security": {
        present: !!headers["strict-transport-security"],
        value: headers["strict-transport-security"],
        recommended: "max-age=31536000; includeSubDomains; preload",
      },
      "X-Frame-Options": {
        present: !!headers["x-frame-options"],
        value: headers["x-frame-options"],
        recommended: "DENY or SAMEORIGIN",
      },
      "X-Content-Type-Options": {
        present: !!headers["x-content-type-options"],
        value: headers["x-content-type-options"],
        recommended: "nosniff",
      },
      "X-XSS-Protection": {
        present: !!headers["x-xss-protection"],
        value: headers["x-xss-protection"],
        recommended: "1; mode=block",
      },
      "Content-Security-Policy": {
        present: !!headers["content-security-policy"],
        value: headers["content-security-policy"],
        recommended: "Define strict CSP",
      },
      "Referrer-Policy": {
        present: !!headers["referrer-policy"],
        value: headers["referrer-policy"],
        recommended: "strict-origin-when-cross-origin",
      },
      "Permissions-Policy": {
        present: !!headers["permissions-policy"],
        value: headers["permissions-policy"],
        recommended: "Restrict dangerous features",
      },
    };

    let passed = 0;
    let failed = 0;

    for (const [header, test] of Object.entries(tests)) {
      if (test.present) {
        console.log(`✓ ${header}: ${test.value}`);
        passed++;
      } else {
        console.log(`✗ ${header}: MISSING`);
        console.log(`  Recommended: ${test.recommended}`);
        failed++;
      }
    }

    console.log(`\n=== Summary ===`);
    console.log(`Passed: ${passed}/${Object.keys(tests).length}`);
    console.log(`Failed: ${failed}/${Object.keys(tests).length}`);

    const score = (passed / Object.keys(tests).length) * 100;
    console.log(`Security Score: ${score.toFixed(0)}%`);
  } catch (error) {
    console.error("Error testing headers:", error.message);
  }
}

// Usage
testSecurityHeaders("https://example.com");

Best Practices

✅ DO

  • Use HTTPS everywhere
  • Implement strict CSP
  • Enable HSTS with preload
  • Block framing with X-Frame-Options
  • Prevent MIME sniffing
  • Report CSP violations
  • Test headers regularly
  • Use security scanners

❌ DON’T

  • Allow unsafe-inline in CSP
  • Skip HSTS on subdomains
  • Ignore CSP violations
  • Use overly permissive policies
  • Forget to test changes

Security Headers Checklist

  • Strict-Transport-Security
  • Content-Security-Policy
  • X-Frame-Options
  • X-Content-Type-Options
  • X-XSS-Protection
  • Referrer-Policy
  • Permissions-Policy
  • Cross-Origin policies
  • Expect-CT
  • Remove server signatures

Testing Tools

Resources