Harness-engineering owasp-security-headers

HTTP Security Headers

install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/owasp-security-headers" ~/.claude/skills/intense-visions-harness-engineering-owasp-security-headers-86857b && rm -rf "$T"
manifest: agents/skills/codex/owasp-security-headers/SKILL.md
source content

HTTP Security Headers

Configure HTTP security headers to protect against XSS, clickjacking, MIME sniffing, and information leakage

When to Use

  • Setting up a new web application or API
  • Hardening an existing application's HTTP responses
  • Implementing Content Security Policy (CSP)
  • Preventing clickjacking, MIME sniffing, and protocol downgrade attacks
  • Preparing for a security audit or penetration test

Instructions

  1. Use Helmet.js to set secure defaults. Helmet configures most security headers with sensible defaults in one line.
npm install helmet
import helmet from 'helmet';

app.use(helmet());

This sets:

Content-Security-Policy
,
Cross-Origin-Embedder-Policy
,
Cross-Origin-Opener-Policy
,
Cross-Origin-Resource-Policy
,
X-DNS-Prefetch-Control
,
X-Frame-Options
,
Strict-Transport-Security
,
X-Download-Options
,
X-Content-Type-Options
,
Origin-Agent-Cluster
,
X-Permitted-Cross-Domain-Policies
,
Referrer-Policy
,
X-XSS-Protection
(disabled — see below).

  1. Configure Content-Security-Policy (CSP) explicitly. CSP is the most important security header. It controls which resources the browser is allowed to load, preventing XSS and data injection.
app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", "'strict-dynamic'"],
        styleSrc: ["'self'", "'unsafe-inline'"], // Inline styles needed for most UI frameworks
        imgSrc: ["'self'", 'data:', 'https://cdn.example.com'],
        fontSrc: ["'self'", 'https://fonts.gstatic.com'],
        connectSrc: ["'self'", 'https://api.example.com'],
        frameSrc: ["'none'"],
        objectSrc: ["'none'"],
        baseUri: ["'self'"],
        formAction: ["'self'"],
        upgradeInsecureRequests: [],
      },
    },
  })
);
  1. Enable Strict-Transport-Security (HSTS) to force HTTPS for all future requests.
app.use(
  helmet({
    strictTransportSecurity: {
      maxAge: 63072000, // 2 years in seconds
      includeSubDomains: true,
      preload: true, // Submit to HSTS preload list
    },
  })
);

After enabling, submit your domain to https://hstspreload.org/ for inclusion in browser preload lists.

  1. Prevent clickjacking with
    X-Frame-Options
    and
    frame-ancestors
    CSP directive.
// X-Frame-Options (legacy, but still needed for older browsers)
// Set by Helmet: SAMEORIGIN by default

// CSP frame-ancestors (modern, preferred)
contentSecurityPolicy: {
  directives: {
    frameAncestors: ["'self'"], // Or "'none'" to block all framing
  },
}
  1. Prevent MIME type sniffing with
    X-Content-Type-Options: nosniff
    .
    Without this, browsers may interpret a file as a different MIME type than declared, enabling XSS via uploaded files.
X-Content-Type-Options: nosniff

Set by Helmet by default. Ensure all responses include correct

Content-Type
headers.

  1. Control referrer information with
    Referrer-Policy
    .
    Prevent leaking URLs (which may contain tokens or sensitive data) in the
    Referer
    header.
app.use(
  helmet({
    referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
  })
);

Options from most restrictive to least:

  • no-referrer
    — never send referrer
  • same-origin
    — send referrer only for same-origin requests
  • strict-origin-when-cross-origin
    (recommended) — full URL for same-origin, origin only for cross-origin HTTPS, nothing for downgrades
  • origin
    — send only the origin, not the full URL
  1. Set
    Permissions-Policy
    to disable unnecessary browser features.
app.use((req, res, next) => {
  res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), payment=(self)');
  next();
});

This prevents embedded third-party content from accessing device APIs even if your page includes it.

  1. Set Cross-Origin headers for isolation.
// Prevent your resources from being loaded by other origins
res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');

// Prevent other pages from getting a reference to your window
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
  1. Remove headers that leak server information.
app.disable('x-powered-by'); // Removes "X-Powered-By: Express"
// Also remove Server header at the reverse proxy level
  1. Deploy CSP in report-only mode first to identify violations before enforcing.
app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        // ... your policy
        reportUri: '/api/csp-report',
      },
      reportOnly: true, // Log violations without blocking
    },
  })
);

// Collect CSP violation reports
app.post('/api/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
  logger.warn({ event: 'csp.violation', report: req.body }, 'CSP violation');
  res.sendStatus(204);
});

Details

Essential headers checklist:

HeaderPurposeValue
Content-Security-PolicyXSS prevention, resource control
default-src 'self'; script-src 'self'
Strict-Transport-SecurityForce HTTPS
max-age=63072000; includeSubDomains; preload
X-Content-Type-OptionsPrevent MIME sniffing
nosniff
X-Frame-OptionsPrevent clickjacking
DENY
or
SAMEORIGIN
Referrer-PolicyControl referrer leakage
strict-origin-when-cross-origin
Permissions-PolicyDisable unused browser APIs
camera=(), microphone=()
Cross-Origin-Opener-PolicyWindow isolation
same-origin
Cross-Origin-Resource-PolicyResource isolation
same-origin

CSP

'strict-dynamic'
explained: When
'strict-dynamic'
is present in
script-src
, dynamically created scripts inherit trust from the script that created them. This allows legitimate runtime-generated scripts (bundler chunks, lazy-loaded modules) while still blocking injected scripts. Pair with nonce-based CSP for maximum security.

Nonce-based CSP for inline scripts:

app.use((req, res, next) => {
  const nonce = crypto.randomBytes(16).toString('base64');
  res.locals.cspNonce = nonce;
  res.setHeader('Content-Security-Policy', `script-src 'nonce-${nonce}' 'strict-dynamic'`);
  next();
});
<script nonce="<%= cspNonce %>">
  /* inline script is allowed */
</script>

API-only applications: For APIs that do not serve HTML, a minimal header set suffices:

X-Content-Type-Options: nosniff
,
Strict-Transport-Security
,
Cache-Control: no-store
(for sensitive data), and
Content-Type: application/json
.

Testing headers: Use https://securityheaders.com or https://observatory.mozilla.org to scan your application and get a grade.

Common mistakes:

  • Setting CSP to
    'unsafe-inline' 'unsafe-eval'
    (defeats the purpose of CSP)
  • Not testing CSP before enforcing (breaks legitimate functionality)
  • Missing HSTS on API subdomains (cookie theft via HTTP)
  • Setting
    X-Frame-Options: ALLOW-FROM
    (deprecated, unsupported in modern browsers — use
    frame-ancestors
    )

Source

https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html

Process

  1. Read the instructions and examples in this document.
  2. Apply the patterns to your implementation, adapting to your specific context.
  3. Verify your implementation against the details and edge cases listed above.

Harness Integration

  • Type: knowledge — this skill is a reference document, not a procedural workflow.
  • No tools or state — consumed as context by other skills and agents.

Success Criteria

  • The patterns described in this document are applied correctly in the implementation.
  • Edge cases and anti-patterns listed in this document are avoided.