demo · v141

Eval Hashes

Replace blanket 'unsafe-eval' with a hash per allowed string. The eval keyword lets exactly the code you signed off through; anything else throws as if eval were blocked. Type code, get the policy keyword that allows just that string.

Heads up Requires Chrome 141+. 'unsafe-eval' is one of the most common Lighthouse / security audit failures; eval-sha256 is the migration path for the legitimate cases (template engines, expression evaluators, capability probes).
checking support…

paste the eval string

computing…

before — unsafe-eval

Content-Security-Policy: script-src 'self' 'unsafe-eval'

XSS payload reaches eval(attackerControlledString) and runs.

after — eval-sha256 (v2)

only the exact pre-approved string evaluates. anything else throws EvalError.

canonical use cases

how the hash is computed

async function evalHashKeyword(code) {
  const buf = new TextEncoder().encode(code);
  const digest = await crypto.subtle.digest("SHA-256", buf);
  const b64 = btoa(String.fromCharCode(...new Uint8Array(digest)));
  return `'eval-sha256-${b64}'`;
}

// CSP header
// Content-Security-Policy: script-src 'self' 'eval-sha256-Yy…='

why this angle

The explainer calls out that 'unsafe-eval' is binary — once it's there, every eval, new Function, and string-typed setTimeout runs without further checks, including ones an attacker chose. But many legitimate apps need one eval, often inside a library they can't replace. Per-string hashes let security teams enumerate the few real cases and drop the broad escape hatch. The diff between the two CSP headers below is the entire point.

see also