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.
'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).
paste the eval string
before — 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
- Feature-detection probes:
new Function('return typeof Promise.allSettled === "function"') - Expression evaluators inside spreadsheets / formula bars
- Template engine compiled output (Vue render functions, Lit static templates)
- WASM bootstrap shims that build a glue function from a string
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.