Understanding CSP Performance Overhead
Understanding CSP Performance Overhead
CSP introduces several types of performance overhead that must be understood and managed. While the security benefits typically outweigh performance costs, optimization can minimize any negative impact on user experience.
Comprehensive performance analysis framework:
// CSP Performance Analyzer
class CSPPerformanceAnalyzer {
constructor() {
this.metrics = {
headerParseTime: [],
resourceBlockingTime: [],
policyEvaluationTime: [],
totalOverhead: [],
userPerceivedImpact: []
};
}
measureCSPOverhead() {
// Client-side measurement script
const measurementScript = `
(function() {
const cspMetrics = {
startTime: performance.now(),
violations: [],
blockedResources: [],
parseTime: 0,
evaluationTime: 0
};
// Measure CSP parsing time
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name.includes('CSP')) {
cspMetrics.parseTime += entry.duration;
}
}
});
observer.observe({ entryTypes: ['measure'] });
// Monitor violations for performance impact
document.addEventListener('securitypolicyviolation', (e) => {
const violationTime = performance.now();
cspMetrics.violations.push({
directive: e.violatedDirective,
uri: e.blockedURI,
timestamp: violationTime,
overhead: violationTime - e.timeStamp
});
});
// Measure resource loading impact
const resourceObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name.includes('blocked') || entry.transferSize === 0) {
cspMetrics.blockedResources.push({
url: entry.name,
blockTime: entry.responseEnd - entry.startTime
});
}
}
});
resourceObserver.observe({ entryTypes: ['resource'] });
// Calculate total overhead
window.addEventListener('load', () => {
cspMetrics.totalTime = performance.now() - cspMetrics.startTime;
cspMetrics.overhead = {
parsing: cspMetrics.parseTime,
violations: cspMetrics.violations.length * 2, // Estimated 2ms per violation
blocked: cspMetrics.blockedResources.reduce((sum, r) => sum + r.blockTime, 0),
total: cspMetrics.parseTime + (cspMetrics.violations.length * 2)
};
// Send metrics to analytics
if (window.analytics) {
window.analytics.track('CSP Performance', cspMetrics);
}
});
window.__CSP_METRICS__ = cspMetrics;
})();
`;
return measurementScript;
}
analyzeHeaderSize() {
// Analyze CSP header size impact
const policies = [
{
name: 'minimal',
policy: "default-src 'self'",
size: 19
},
{
name: 'typical',
policy: "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:",
size: 134
},
{
name: 'complex',
policy: this.generateComplexPolicy(),
size: 0 // Will be calculated
}
];
const analysis = policies.map(p => {
p.size = p.size || new Blob([p.policy]).size;
return {
...p,
overhead: {
bytes: p.size,
percentage: (p.size / 1024) * 100, // As percentage of 1KB
requestImpact: this.calculateRequestImpact(p.size),
recommendations: this.getHeaderSizeRecommendations(p.size)
}
};
});
return analysis;
}
calculateRequestImpact(headerSize) {
// Calculate impact on different connection types
const connectionTypes = {
'3G': { bandwidth: 1.6 * 1024 * 1024 / 8, latency: 300 }, // 1.6 Mbps
'4G': { bandwidth: 12 * 1024 * 1024 / 8, latency: 50 }, // 12 Mbps
'WiFi': { bandwidth: 30 * 1024 * 1024 / 8, latency: 10 }, // 30 Mbps
'Fiber': { bandwidth: 100 * 1024 * 1024 / 8, latency: 5 } // 100 Mbps
};
const impact = {};
Object.entries(connectionTypes).forEach(([type, conn]) => {
impact[type] = {
transmissionTime: (headerSize / conn.bandwidth) * 1000, // ms
percentOfLatency: ((headerSize / conn.bandwidth) * 1000 / conn.latency) * 100
};
});
return impact;
}
measurePolicyComplexity() {
// Measure policy parsing complexity
const complexityFactors = {
directiveCount: 0,
sourceCount: 0,
wildcardUsage: 0,
nonceUsage: false,
hashUsage: false,
strictDynamic: false
};
return {
score: this.calculateComplexityScore(complexityFactors),
impact: this.estimateParsingTime(complexityFactors),
optimizations: this.suggestOptimizations(complexityFactors)
};
}
}