Dynamic CSP Generation
Dynamic CSP Generation
Modern applications often require dynamic CSP policies based on user context, feature flags, or runtime configuration. Implementing dynamic CSP generation provides flexibility while maintaining security.
// Dynamic CSP Generator
class DynamicCSPGenerator {
constructor(basePolicy) {
this.basePolicy = basePolicy;
this.contextualPolicies = new Map();
}
registerContextualPolicy(context, modifications) {
this.contextualPolicies.set(context, modifications);
}
generatePolicy(context = {}) {
// Start with base policy
let policy = JSON.parse(JSON.stringify(this.basePolicy));
// Apply contextual modifications
if (context.userRole === 'admin') {
// Admins might need additional sources for admin panels
policy['script-src'].push('https://admin-assets.example.com');
}
if (context.features?.analytics) {
// Add analytics endpoints
policy['script-src'].push('https://www.google-analytics.com');
policy['connect-src'].push('https://www.google-analytics.com');
policy['img-src'].push('https://www.google-analytics.com');
}
if (context.features?.chat) {
// Add chat widget sources
policy['frame-src'] = policy['frame-src'] || ["'self'"];
policy['frame-src'].push('https://chat.example.com');
policy['connect-src'].push('wss://chat.example.com');
}
if (context.environment === 'development') {
// Relax policies for development
policy['script-src'].push("'unsafe-eval'"); // For webpack dev server
policy['connect-src'].push('ws://localhost:*'); // For hot reload
}
return this.formatPolicy(policy);
}
formatPolicy(policy) {
return Object.entries(policy)
.filter(([_, values]) => values && values.length > 0)
.map(([directive, values]) => `${directive} ${values.join(' ')}`)
.join('; ');
}
}
// Usage example
const cspGenerator = new DynamicCSPGenerator({
'default-src': ["'self'"],
'script-src': ["'self'"],
'style-src': ["'self'"],
'img-src': ["'self'", 'data:'],
'connect-src': ["'self'"]
});
// Express middleware using dynamic CSP
app.use((req, res, next) => {
const context = {
userRole: req.user?.role,
features: {
analytics: req.cookies.analyticsConsent === 'true',
chat: req.user?.premiumFeatures?.includes('chat')
},
environment: process.env.NODE_ENV
};
const policy = cspGenerator.generatePolicy(context);
res.setHeader('Content-Security-Policy', policy);
next();
});