git clone https://github.com/Intense-Visions/harness-engineering
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"
agents/skills/codex/owasp-security-headers/SKILL.mdHTTP 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
- 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).
- 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: [], }, }, }) );
- 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.
- Prevent clickjacking with
andX-Frame-Options
CSP directive.frame-ancestors
// 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 }, }
- Prevent MIME type sniffing with
. Without this, browsers may interpret a file as a different MIME type than declared, enabling XSS via uploaded files.X-Content-Type-Options: nosniff
X-Content-Type-Options: nosniff
Set by Helmet by default. Ensure all responses include correct
Content-Type headers.
- Control referrer information with
. Prevent leaking URLs (which may contain tokens or sensitive data) in theReferrer-Policy
header.Referer
app.use( helmet({ referrerPolicy: { policy: 'strict-origin-when-cross-origin' }, }) );
Options from most restrictive to least:
— never send referrerno-referrer
— send referrer only for same-origin requestssame-origin
(recommended) — full URL for same-origin, origin only for cross-origin HTTPS, nothing for downgradesstrict-origin-when-cross-origin
— send only the origin, not the full URLorigin
- Set
to disable unnecessary browser features.Permissions-Policy
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.
- 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');
- 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
- 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:
| Header | Purpose | Value |
|---|---|---|
| Content-Security-Policy | XSS prevention, resource control | |
| Strict-Transport-Security | Force HTTPS | |
| X-Content-Type-Options | Prevent MIME sniffing | |
| X-Frame-Options | Prevent clickjacking | or |
| Referrer-Policy | Control referrer leakage | |
| Permissions-Policy | Disable unused browser APIs | |
| Cross-Origin-Opener-Policy | Window isolation | |
| Cross-Origin-Resource-Policy | Resource isolation | |
CSP
explained: When 'strict-dynamic'
'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
(defeats the purpose of CSP)'unsafe-inline' 'unsafe-eval' - Not testing CSP before enforcing (breaks legitimate functionality)
- Missing HSTS on API subdomains (cookie theft via HTTP)
- Setting
(deprecated, unsupported in modern browsers — useX-Frame-Options: ALLOW-FROM
)frame-ancestors
Source
https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- 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.