Skip to main content

CSP unsafe-inline and unsafe-eval risks Why they quietly undo your XSS protection.

Why 'unsafe-inline' and 'unsafe-eval' in a Content Security Policy weaken your defence against XSS, what each keyword permits, and how to move to a strict CSP.

Senior Consultant, Summit
5 min read
Lines of HTML and script on a dark editor screen, representing inline scripts governed by a Content Security Policy
Content Security Policy · script-src 'unsafe-inline' and 'unsafe-eval'
On this page
  1. What the two keywords actually permit
  2. Why “we have a CSP” is not the same as “we are protected”
  3. Why teams end up with these keywords
  4. How to move to a policy that protects
  5. Where this fits in a wider review
  6. How Summit helps
  7. Frequently asked questions

Not sure what your CSP allows? Run the free scan at scan.sumrite.com and see your policy graded in a minute, no account needed.

A Content Security Policy is a browser-enforced allowlist. It tells the browser which sources of script, style, images and other content it is allowed to load and run for your site. Done well, it means that even if an attacker manages to inject script into a page, the browser refuses to run it. Done with 'unsafe-inline' or 'unsafe-eval', the policy is present in the header, passes a casual glance, and still lets that script run.

What the two keywords actually permit

Keyword What it allows Why that matters
'unsafe-inline' Inline <script> blocks, inline event handlers such as onclick, and javascript: URLs These are the usual forms an injected script takes, so the CSP no longer blocks them
'unsafe-eval' Turning strings into code: eval(), the Function constructor, string-based setTimeout/setInterval Untrusted input that reaches these functions is executed as code

Both live in the script-src directive (or default-src when script-src is absent). The word “unsafe” is in the name on purpose. The CSP specification uses it as a deliberate warning that you are opting out of the protection the directive exists to provide.

Why “we have a CSP” is not the same as “we are protected”

Cross-site scripting remains one of the most common serious findings in web application testing. A CSP is meant to be the safety net: the layer that stops an injection from becoming code execution in your users’ browsers. The entire value of that net, for scripts, depends on not allowing inline and eval.

Picture the realistic sequence. An input somewhere on the site is not escaped correctly, so an attacker can get their own markup onto a page another user views. Without a strict CSP, the browser runs whatever script is on the page. The attacker can read the logged-in user’s session, act as them, change what they see, or quietly send their data elsewhere. A strict CSP would have told the browser “only run scripts I explicitly trust”, and the injected script would never have executed. With 'unsafe-inline', the net has a hole exactly where it was supposed to catch the fall.

This is why 'unsafe-inline' is a finding in its own right in a penetration test, even before anyone has found an injection point. It removes a layer of defence in depth that you are otherwise entitled to rely on.

A nonce and 'unsafe-inline' together is a common mistake

If you add a nonce or hash to script-src, modern browsers ignore 'unsafe-inline'. But older browsers do not, so leaving 'unsafe-inline' in “for compatibility” quietly reopens the hole for part of your audience. Use 'strict-dynamic' with a nonce instead of keeping the unsafe keyword as a fallback.

Why teams end up with these keywords

Almost nobody adds 'unsafe-inline' because they want it. It creeps in:

  • Legacy inline scripts and styles. A page built with onclick attributes and <script> blocks throughout will break under a strict policy, and the quickest way to stop the breakage is the unsafe keyword.
  • Third-party tags. Analytics, tag managers, chat widgets and A/B testing tools often inject inline script, and their setup guides sometimes recommend 'unsafe-inline' as the path of least resistance.
  • Frameworks and build tools. Some older setups inject inline bootstrapping code or rely on eval() for templating, which pulls in 'unsafe-eval'.
  • Report-only never enforced. A Content-Security-Policy-Report-Only header was added to gather data, and the enforced policy that would actually block anything never shipped.

How to move to a policy that protects

  1. Inventory your scripts. List every inline script, inline event handler and third-party tag on your key pages. This is usually the bulk of the work.
  2. Externalise or mark trusted scripts. Move inline logic into files, or attach a per-response nonce (a random value regenerated on every page load) or a hash to the scripts you trust.
  3. Add 'strict-dynamic'. This lets a trusted, nonced script load its own dependencies without you listing every domain, which is what makes strict CSP workable on real sites.
  4. Remove 'unsafe-eval'. Find what depends on it, update or replace those libraries, then drop it and test.
  5. Roll out in report-only first. Deploy the strict policy as Content-Security-Policy-Report-Only, collect violation reports, fix what legitimately breaks, then switch to the enforcing header.
  6. Keep the rest tight. Set a sensible default-src, lock down object-src 'none', base-uri 'none' and frame-ancestors to stop clickjacking.

A nonce-based policy ends up looking like script-src 'nonce-RANDOM' 'strict-dynamic'; with no unsafe keywords in sight. That is the version that actually stops an injected script from running.

Where this fits in a wider review

A CSP is one header among several that work together. Our walkthroughs on secure cookie flags and SameSite and CORS misconfiguration cover neighbouring settings that often need the same attention. And a CSP is defence in depth, not a cure: the underlying goal is still to fix the injection points themselves, which is what web application testing and source code review are for.

How Summit helps

Summit’s web application VAPT tests for the injection points a CSP is meant to contain, and reviews whether your policy would actually hold if one were found. We give you a policy written for your site, a migration path off 'unsafe-inline' and 'unsafe-eval', and a retest. Scanner-level checks, including whether these keywords are present, are free at scan.sumrite.com, so spend the budget on the part a scanner cannot do.

See your CSP graded in a minute

Run the free scan at scan.sumrite.com to check your Content Security Policy and other headers, then bring us the findings a scanner cannot confirm. Get a quote.

Related reading: Secure cookie flags and SameSite · CORS misconfiguration: reflecting any origin · What is a VAPT assessment?

Frequently asked questions

What does 'unsafe-inline' do in a Content Security Policy?

Inside script-src, it permits inline script blocks, inline event handler attributes such as onclick, and javascript: URLs to execute. Those are the forms that injected scripts usually take, so the policy stops being an effective barrier against cross-site scripting.

What does 'unsafe-eval' do in a Content Security Policy?

It permits JavaScript that converts strings into executable code, such as eval() and the Function constructor, and setTimeout or setInterval called with a string. If untrusted data reaches one of those, the browser will run it as code.

Is a CSP with 'unsafe-inline' better than no CSP at all?

Only a little. You keep benefits such as restricting where images, frames and connections can go, but the script protection that is the main point of a CSP is effectively switched off. Treat it as a policy that still needs real work.

How do I replace 'unsafe-inline' safely?

Move inline scripts into external files, then allow the ones you trust with a per-response nonce or a hash, and add 'strict-dynamic' so trusted scripts can load their own dependencies. Modern browsers ignore 'unsafe-inline' when a nonce or hash is present, which helps during migration.

Can I just remove 'unsafe-eval'?

Often yes, but some older libraries and template engines rely on it. Find what depends on it first, update or replace those libraries, and then drop the keyword and test thoroughly.

How do I check my current CSP?

Summit's free scan at scan.sumrite.com reports your Content Security Policy and flags weakening keywords such as 'unsafe-inline' and 'unsafe-eval'. It is an automated first check, not a replacement for an expert review of the whole policy.

See all FAQs →

  • CSP
  • Security Headers
  • XSS
  • Web Security
  • Hardening

Niyaz Khan

Niyaz Khan is a Senior Consultant at Summit, working on web application, API and infrastructure security testing and the hardening guidance that follows each engagement.

Talk to a tester

Want us to look at your application?

Scoped quote within a day. Expert-led testing, verified findings, a fix-and-retest cycle and a report your auditors can use as evidence.