Inspetor CSP

Analise diretivas CSP para fraquezas de segurança e gere recomendações reforçadas

What is Content Security Policy Inspection?

Content Security Policy (CSP) inspection is the systematic analysis of deployed CSP headers to identify security weaknesses, overly permissive directives, and misconfigurations that undermine the policy's protective intent. While a CSP generator builds new policies from scratch, an inspector evaluates existing policies — detecting gaps that allow cross-site scripting (XSS), data exfiltration, clickjacking, and other injection attacks to bypass what appears to be a secure configuration.

A CSP header that exists but is poorly configured provides a false sense of security. Many production deployments contain directives with 'unsafe-inline', 'unsafe-eval', wildcard sources (*), or overly broad domain allowlists that effectively neutralize the policy's protection. The CSP Inspector parses raw headers, scores each directive against established security criteria, and produces actionable findings that prioritize remediation by attack surface impact.

Security Scoring Methodology

The CSP Inspector assigns a security score from 0 to 100 based on a weighted evaluation of each directive present in the policy. The scoring model considers three dimensions: directive coverage (are all critical directives defined?), source restrictiveness (how narrowly scoped is each directive?), and bypass resistance (can known attack techniques circumvent the policy?).

  • Critical deductions (−20 to −30 points): Presence of 'unsafe-inline' in script-src (negates XSS protection entirely), 'unsafe-eval' allowing eval()/Function() execution, wildcard (*) in default-src or script-src, and missing script-src directive when default-src is also permissive.
  • High deductions (−10 to −15 points): Use of data: URIs in script or object sources (enables inline payload injection), overly broad CDN domains that host user-uploaded content (e.g., *.googleapis.com without path restriction), missing object-src directive (defaults to default-src which may be permissive), and absent base-uri allowing base tag injection attacks.
  • Medium deductions (−5 to −8 points): Missing frame-ancestors (clickjacking vulnerability), absent form-action (form hijacking), no upgrade-insecure-requests on HTTPS sites, and use of report-uri (deprecated) instead of report-to.
  • Positive scoring (+5 to +10 points): Use of nonce-based or hash-based script allowlisting ('nonce-...', 'sha256-...'), presence of 'strict-dynamic' for trust propagation, inclusion of require-trusted-types-for 'script', and complete directive coverage across all resource types.

A score of 80+ indicates a well-configured policy suitable for production enforcement. Scores between 50-79 suggest the policy provides partial protection but has exploitable gaps. Below 50, the policy is effectively security theater — present but not protective.

Common CSP Misconfigurations and Bypass Techniques

The inspector detects misconfigurations that create exploitable attack surfaces, including bypass techniques documented by security researchers and used in real-world penetration testing:

  • JSONP endpoint bypass: When a CSP allows a domain that hosts JSONP endpoints (e.g., accounts.google.com), attackers can execute arbitrary JavaScript via <script src="https://accounts.google.com/o/oauth2/revoke?callback=alert(1)">. The inspector maintains a database of known JSONP-capable domains and flags their inclusion in script-src.
  • CDN-hosted attacker scripts: Allowing broad CDN domains like *.cloudflare.com, *.amazonaws.com, or *.jsdelivr.net enables attackers to host malicious payloads on the same CDN and load them within the CSP allowlist. The inspector identifies CDN wildcards and recommends path-specific restrictions or hash-based integrity checks.
  • Open redirect via allowed origins: If a CSP-allowed domain has an open redirect vulnerability, attackers can chain it to load scripts from arbitrary origins while appearing to originate from the trusted domain. The inspector flags domains known to have redirect endpoints and warns about transitive trust implications.
  • Angular/Vue template injection: Policies that allow 'unsafe-eval' combined with framework CDN sources enable template injection attacks. Attackers inject expressions like {{constructor.constructor('alert(1)')()}} that frameworks evaluate without triggering traditional XSS filters. The inspector detects framework CDN + unsafe-eval combinations.
  • Base URI hijacking: Without base-uri 'self' or base-uri 'none', attackers can inject a <base href="https://evil.com"> tag that redirects all relative URL script loads to an attacker-controlled server — bypassing script-src restrictions entirely.

Each finding includes the specific directive responsible, the attack vector it enables, and a concrete remediation step that preserves application functionality while closing the security gap.

Directive Evaluation: Permissive vs Restrictive

The inspector classifies each directive source on a restrictiveness spectrum. Understanding this classification helps developers interpret audit results and prioritize fixes:

  • Maximally restrictive: 'none' — blocks all resources of that type. Ideal for object-src, base-uri, and any resource type your application does not use.
  • Highly restrictive: 'self' combined with nonce-based or hash-based script allowlisting. Also includes 'strict-dynamic' which propagates trust only to scripts explicitly loaded by already-trusted scripts, eliminating the need for domain allowlists.
  • Moderately restrictive: Specific domain allowlists with exact origins (e.g., https://cdn.example.com) — acceptable when nonce/hash approaches are impractical, but each domain increases the attack surface proportionally to that domain's security posture.
  • Weakly restrictive: Wildcard subdomains (*.example.com), protocol schemes (https:), and data: URIs — each provides minimal restriction. https: allows any HTTPS origin globally. data: enables inline payload injection in base64-encoded form.
  • Non-restrictive (nullifying): 'unsafe-inline', 'unsafe-eval', and bare * — these effectively disable CSP protection for the directive they appear in. 'unsafe-inline' in script-src allows any injected <script> tag to execute, which is precisely the attack vector CSP was designed to prevent.

The inspector computes a per-directive restrictiveness grade (A through F) and highlights directives where upgrading to a more restrictive source expression would meaningfully reduce attack surface without breaking application functionality.

Report-Only Mode Analysis and Enforcement Migration

The Content-Security-Policy-Report-Only header allows deploying a CSP in observation mode — violations generate reports but resources are not blocked. The inspector analyzes report-only policies differently from enforcing ones, providing migration guidance for transitioning to full enforcement:

  • Report-only adequacy check: Verifies that the report-only policy includes a report-to or report-uri directive. A report-only policy without reporting endpoints generates no violation data and serves no purpose — it is effectively dead code in your HTTP response headers.
  • Enforcement readiness assessment: Compares the report-only policy against the page's actual resource loading patterns (if provided) to estimate how many legitimate resources would be blocked upon enforcement. A policy that would block critical application scripts is not ready for enforcement regardless of its security score.
  • Incremental tightening recommendations: Suggests a phased migration path from the current report-only policy to a strict enforcing policy. For example: start by enforcing object-src 'none' and base-uri 'self' (low breakage risk), then migrate script-src from domain allowlist to nonce-based (medium effort), and finally add 'strict-dynamic' to eliminate the allowlist entirely (requires build pipeline integration for nonce injection).
  • Dual-header detection: Identifies when both Content-Security-Policy and Content-Security-Policy-Report-Only are present simultaneously. When both exist, the enforcing header takes precedence for blocking while the report-only header can test a stricter future policy. The inspector validates that the report-only policy is strictly equal to or more restrictive than the enforcing one — if the report-only policy is weaker, it provides no useful migration data.

Code Examples

CSP Header Parsing and Security Evaluation

// CSP Inspector — parse header and evaluate security posture
function inspectCsp(headerValue) {
  const directives = {};
  const findings = [];
  let score = 100;

  // Parse CSP header into directive map
  headerValue.split(';').forEach(part => {
    const trimmed = part.trim();
    if (!trimmed) return;
    const [name, ...sources] = trimmed.split(/\s+/);
    directives[name] = sources;
  });

  // Check: missing critical directives
  const critical = ['script-src', 'object-src', 'base-uri'];
  for (const dir of critical) {
    if (!directives[dir] && !directives['default-src']) {
      findings.push({
        severity: 'high',
        directive: dir,
        issue: `Missing "${dir}" directive with no default-src fallback`,
        fix: `Add ${dir} with restrictive sources (e.g., 'self' or 'none')`
      });
      score -= 15;
    }
  }

  // Check: unsafe-inline in script-src (nullifies XSS protection)
  const scriptSrc = directives['script-src'] || directives['default-src'] || [];
  if (scriptSrc.includes("'unsafe-inline'")) {
    findings.push({
      severity: 'critical',
      directive: 'script-src',
      issue: "'unsafe-inline' allows any injected script to execute",
      fix: "Replace with nonce-based ('nonce-<random>') or hash-based allowlisting"
    });
    score -= 30;
  }

  // Check: unsafe-eval allows eval()/Function() execution
  if (scriptSrc.includes("'unsafe-eval'")) {
    findings.push({
      severity: 'critical',
      directive: 'script-src',
      issue: "'unsafe-eval' enables eval()-based XSS attacks",
      fix: "Remove 'unsafe-eval' and refactor code to avoid eval/Function/setTimeout(string)"
    });
    score -= 25;
  }

  // Check: wildcard source in script-src
  if (scriptSrc.includes('*')) {
    findings.push({
      severity: 'critical',
      directive: 'script-src',
      issue: "Wildcard '*' allows scripts from any origin",
      fix: "Replace with specific domain allowlist or 'strict-dynamic' with nonces"
    });
    score -= 30;
  }

  // Check: data: URI in script sources (inline payload injection)
  if (scriptSrc.includes('data:')) {
    findings.push({
      severity: 'high',
      directive: 'script-src',
      issue: "'data:' allows base64-encoded script injection",
      fix: "Remove 'data:' from script-src; use nonces for inline scripts"
    });
    score -= 15;
  }

  // Check: missing frame-ancestors (clickjacking)
  if (!directives['frame-ancestors']) {
    findings.push({
      severity: 'medium',
      directive: 'frame-ancestors',
      issue: "Missing frame-ancestors — page can be framed (clickjacking)",
      fix: "Add frame-ancestors 'self' or 'none' to prevent framing"
    });
    score -= 8;
  }

  // Classify directive restrictiveness
  const grades = {};
  for (const [dir, sources] of Object.entries(directives)) {
    if (sources.includes("'none'")) grades[dir] = 'A';
    else if (sources.includes("'strict-dynamic'")) grades[dir] = 'A';
    else if (sources.every(s => s === "'self'" || s.startsWith("'nonce-")
      || s.startsWith("'sha256-"))) grades[dir] = 'B';
    else if (sources.some(s => s === '*' || s === "'unsafe-inline'")) grades[dir] = 'F';
    else grades[dir] = 'C';
  }

  return {
    score: Math.max(0, score),
    directives,
    findings,
    grades,
    hasReportOnly: false,
    directiveCount: Object.keys(directives).length
  };
}

// Example: inspect a weak CSP header
const result = inspectCsp(
  "default-src 'self'; script-src 'self' 'unsafe-inline' https:; style-src * 'unsafe-inline'"
);
// → { score: 35, findings: [{severity: 'critical', issue: "'unsafe-inline' allows..."}], ... }

Standards & Specifications