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)
    };
  }
}