Security Header Best Practices
Security Header Best Practices
1. Progressive Enhancement Strategy
class ProgressiveSecurityHeaders {
constructor() {
this.stages = {
discovery: {
duration: '2 weeks',
headers: {
'Content-Security-Policy-Report-Only': "default-src * 'unsafe-inline' 'unsafe-eval'; report-uri /csp-reports",
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'SAMEORIGIN'
}
},
hardening: {
duration: '2 weeks',
headers: {
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; report-uri /csp-reports",
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'SAMEORIGIN',
'Referrer-Policy': 'strict-origin-when-cross-origin'
}
},
strict: {
duration: 'permanent',
headers: {
'Content-Security-Policy': "default-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'",
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'Referrer-Policy': 'strict-origin',
'Permissions-Policy': 'geolocation=(), camera=(), microphone=()'
}
}
};
this.currentStage = process.env.SECURITY_STAGE || 'discovery';
}
middleware() {
return (req, res, next) => {
const headers = this.stages[this.currentStage].headers;
Object.entries(headers).forEach(([name, value]) => {
res.setHeader(name, value);
});
// Add monitoring header
res.setHeader('X-Security-Stage', this.currentStage);
next();
};
}
advanceStage() {
const stages = Object.keys(this.stages);
const currentIndex = stages.indexOf(this.currentStage);
if (currentIndex < stages.length - 1) {
this.currentStage = stages[currentIndex + 1];
console.log(`Advanced to security stage: ${this.currentStage}`);
}
}
}
2. Environment-Specific Configuration
class EnvironmentAwareHeaders {
constructor() {
this.configs = {
development: {
csp: "default-src * 'unsafe-inline' 'unsafe-eval'; report-uri /dev-csp-reports",
hsts: null, // No HSTS in development
frameOptions: 'SAMEORIGIN',
corsOrigins: ['http://localhost:3000', 'http://localhost:3001']
},
staging: {
csp: "default-src 'self' https:; script-src 'self' 'unsafe-inline' https:; report-uri /csp-reports",
hsts: 'max-age=300', // 5 minutes for testing
frameOptions: 'SAMEORIGIN',
corsOrigins: ['https://staging-app.example.com']
},
production: {
csp: "default-src 'self'; script-src 'self' 'nonce-{nonce}'; style-src 'self' 'nonce-{nonce}'; upgrade-insecure-requests",
hsts: 'max-age=31536000; includeSubDomains; preload',
frameOptions: 'DENY',
corsOrigins: ['https://app.example.com', 'https://mobile.example.com']
}
};
}
getConfig() {
const env = process.env.NODE_ENV || 'development';
return this.configs[env] || this.configs.development;
}
middleware() {
const config = this.getConfig();
return (req, res, next) => {
// Generate nonce for CSP
const nonce = crypto.randomBytes(16).toString('base64');
res.locals.nonce = nonce;
// Apply CSP with nonce substitution
if (config.csp) {
const csp = config.csp.replace(/{nonce}/g, nonce);
res.setHeader('Content-Security-Policy', csp);
}
// Apply HSTS only in appropriate environments
if (config.hsts && req.secure) {
res.setHeader('Strict-Transport-Security', config.hsts);
}
// Frame options
if (config.frameOptions) {
res.setHeader('X-Frame-Options', config.frameOptions);
}
// CORS handling
const origin = req.headers.origin;
if (origin && config.corsOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
next();
};
}
}
3. Documentation and Team Communication
/**
* Security Headers Documentation Generator
* Generates markdown documentation for current security configuration
*/
class SecurityHeadersDocGenerator {
constructor(config) {
this.config = config;
}
generate() {
const doc = `# Security Headers Configuration