# Security headers present (CSP, X-Frame-Options, X-Content-Type, HSTS)

- **Pattern:** `ab-001581` (`gov-fisma-fedramp.system-protection.security-headers`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001581
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001581)

## Why it matters

Missing security headers leave browsers without the instructions needed to block clickjacking, MIME-type sniffing attacks, and content injection — gaps that attackers exploit through iframe embeds, malicious redirects, and injected scripts. NIST 800-53 rev5 SC-8 covers transmission integrity and SI-3 covers malicious code protection; both are satisfied in part by response headers that tell browsers what to trust. FedRAMP rev5 SC-8 applies to all cloud systems serving federal users. OWASP A05 (Security Misconfiguration) and CWE-693 (Protection Mechanism Failure) specifically call out absent security headers. A CSP header alone can neutralize most XSS payloads that slip through input validation.

## Severity rationale

High because absent security headers expose every page visitor to clickjacking, MIME-confusion exploits, and reflected XSS attacks that require no server-side vulnerability to execute.

## Remediation

Configure all four required headers in `next.config.js` at the framework level so they apply to every route including API responses.

```ts
// next.config.js
const securityHeaders = [
  { key: 'Content-Security-Policy',
    value: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:" },
  { key: 'X-Frame-Options', value: 'DENY' },
  { key: 'X-Content-Type-Options', value: 'nosniff' },
  { key: 'Strict-Transport-Security',
    value: 'max-age=31536000; includeSubDomains; preload' }
]

const nextConfig = {
  async headers() {
    return [{ source: '/:path*', headers: securityHeaders }]
  }
}
export default nextConfig
```

Verify the headers are live in production using `curl -I https://yourdomain.com` — not just in dev. If your CSP blocks legitimate inline scripts, migrate to nonce-based CSP rather than weakening to `unsafe-inline` for all sources.

## Detection

- **ID:** `security-headers`
- **Severity:** `high`
- **What to look for:** Count all locations where security headers are configured: `next.config.js`/`next.config.ts`, `middleware.ts`, `vercel.json`, `netlify.toml`, or API configuration. For each header, quote the actual configured value found. Look for Content-Security-Policy (CSP), X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security (HSTS). Count how many of the 4 required headers are present.
- **Pass criteria:** All 4 key headers are present and correctly configured: CSP with `default-src 'self'` or more restrictive, X-Frame-Options set to `DENY` or `SAMEORIGIN`, X-Content-Type-Options set to `nosniff`, HSTS with `max-age` of at least 31536000. Report even on pass: list all 4 headers and their configured values.
- **Fail criteria:** Any of the 4 headers is missing, or headers are misconfigured (e.g., CSP allows all sources with `*`, X-Frame-Options is `ALLOWALL`). Do not pass when headers are configured only in development config but not in production.
- **Skip (N/A) when:** Never — security headers apply to all web projects.
- **Detail on fail:** List which headers are missing or misconfigured. Example: `"CSP header missing. X-Frame-Options set to ALLOWALL (should be DENY or SAMEORIGIN). HSTS header present. 2 of 4 headers configured."`
- **Remediation:** Add security headers in next.config.js or middleware:

  ```ts
  // next.config.js
  const nextConfig = {
    headers: async () => [
      {
        source: '/:path*',
        headers: [
          { key: 'Content-Security-Policy', value: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" },
          { key: 'X-Frame-Options', value: 'DENY' },
          { key: 'X-Content-Type-Options', value: 'nosniff' },
          { key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' }
        ]
      }
    ]
  }
  export default nextConfig
  ```

  Or use middleware.ts:

  ```ts
  // middleware.ts
  import { NextResponse } from 'next/server'
  import type { NextRequest } from 'next/server'

  export function middleware(request: NextRequest) {
    const response = NextResponse.next()
    response.headers.set('Content-Security-Policy', "default-src 'self'")
    response.headers.set('X-Frame-Options', 'DENY')
    response.headers.set('X-Content-Type-Options', 'nosniff')
    response.headers.set('Strict-Transport-Security', 'max-age=31536000')
    return response
  }
  ```

---

## External references

- nist SC-8
- nist SI-3
- fedramp SC-8
- owasp A05
- cwe CWE-693

Taxons: regulatory-conformance, injection-and-input-trust

HTML version: https://auditbuffet.com/patterns/ab-001581
