348

I'm getting a bunch of errors in the developer console:

Refused to evaluate a string

Refused to execute inline script because it violates the following Content Security Policy directive

Refused to load the script

Refused to load the stylesheet

What's this all about? How does Content Security Policy (CSP) work? How do I use the Content-Security-Policy HTTP header?

Specifically, how to...

  1. ...allow multiple sources?
  2. ...use different directives?
  3. ...use multiple directives?
  4. ...handle ports?
  5. ...handle different protocols?
  6. ...allow file:// protocol?
  7. ...use inline styles, scripts, and tags <style> and <script>?
  8. ...allow eval()?

And finally:

  1. What exactly does 'self' mean?
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Schlaus
  • 18,144
  • 10
  • 36
  • 64
  • 4
    https://content-security-policy.com – sites Feb 01 '17 at 20:40
  • Should be helpful - [Learn what is content-security-policy and bypass it in your local dev & QA](https://requestly.io/blog/learn-and-bypass-content-security-policy-http-response-header/) – Sachin Jain Nov 16 '22 at 07:34

2 Answers2

806

The Content-Security-Policy meta-tag allows you to reduce the risk of XSS attacks by allowing you to define where resources can be loaded from, preventing browsers from loading data from any other locations. This makes it harder for an attacker to inject malicious code into your site.

I banged my head against a brick wall trying to figure out why I was getting CSP errors one after another, and there didn't seem to be any concise, clear instructions on just how does it work. So here's my attempt at explaining some points of CSP briefly, mostly concentrating on the things I found hard to solve.

For brevity I won’t write the full tag in each sample. Instead I'll only show the content property, so a sample that says content="default-src 'self'" means this:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'">

1. How can I allow multiple sources?

You can simply list your sources after a directive as a space-separated list:

content="default-src 'self' https://example.com/js/"

Note that there are no quotes around parameters other than the special ones, like 'self'. Also, there's no colon (:) after the directive. Just the directive, then a space-separated list of parameters.

Everything below the specified parameters is implicitly allowed. That means that in the example above these would be valid sources:

https://example.com/js/file.js
https://example.com/js/subdir/anotherfile.js

These, however, would not be valid:

http://example.com/js/file.js
^^^^ wrong protocol

https://example.com/file.js
                   ^^ above the specified path

2. How can I use different directives? What do they each do?

The most common directives are:

  • default-src the default policy for loading javascript, images, CSS, fonts, AJAX requests, etc
  • script-src defines valid sources for javascript files
  • style-src defines valid sources for css files
  • img-src defines valid sources for images
  • connect-src defines valid targets for to XMLHttpRequest (AJAX), WebSockets or EventSource. If a connection attempt is made to a host that's not allowed here, the browser will emulate a 400 error

There are others, but these are the ones you're most likely to need.

3. How can I use multiple directives?

You define all your directives inside one meta-tag by terminating them with a semicolon (;):

content="default-src 'self' https://example.com/js/; style-src 'self'"

4. How can I handle ports?

Everything but the default ports needs to be allowed explicitly by adding the port number or an asterisk after the allowed domain:

content="default-src 'self' https://ajax.googleapis.com http://example.com:123/free/stuff/"

The above would result in:

https://ajax.googleapis.com:123
                           ^^^^ Not ok, wrong port

https://ajax.googleapis.com - OK

http://example.com/free/stuff/file.js
                 ^^ Not ok, only the port 123 is allowed

http://example.com:123/free/stuff/file.js - OK

As I mentioned, you can also use an asterisk to explicitly allow all ports:

content="default-src example.com:*"

5. How can I handle different protocols?

By default, only standard protocols are allowed. For example to allow WebSockets ws:// you will have to allow it explicitly:

content="default-src 'self'; connect-src ws:; style-src 'self'"
                                         ^^^ web Sockets are now allowed on all domains and ports.

6. How can I allow the file protocol file://?

If you'll try to define it as such it won’t work. Instead, you'll allow it with the filesystem parameter:

content="default-src filesystem"

7. How can I use inline scripts and style definitions?

Unless explicitly allowed, you can't use inline style definitions, code inside <script> tags or in tag properties like onclick. You allow them like so:

content="script-src 'unsafe-inline'; style-src 'unsafe-inline'"

You'll also have to explicitly allow inline, base64 encoded images:

content="img-src data:"

8. How can I allow eval()?

I'm sure many people would say that you don't, since 'eval is evil' and the most likely cause for the impending end of the world. Those people would be wrong. Sure, you can definitely punch major holes into your site's security with eval, but it has perfectly valid use cases. You just have to be smart about using it. You allow it like so:

content="script-src 'unsafe-eval'"

9. What exactly does 'self' mean?

You might take 'self' to mean localhost, local filesystem, or anything on the same host. It doesn't mean any of those. It means sources that have the same scheme (protocol), same host, and same port as the file the content policy is defined in. Serving your site over HTTP? No https for you then, unless you define it explicitly.

I've used 'self' in most examples as it usually makes sense to include it, but it's by no means mandatory. Leave it out if you don't need it.

But hang on a minute! Can't I just use content="default-src *" and be done with it?

No. In addition to the obvious security vulnerabilities, this also won’t work as you'd expect. Even though some docs claim it allows anything, that's not true. It doesn't allow inlining or evals, so to really, really make your site extra vulnerable, you would use this:

content="default-src * 'unsafe-inline' 'unsafe-eval'"

... but I trust you won’t.

Further reading:

http://content-security-policy.com

http://en.wikipedia.org/wiki/Content_Security_Policy

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Schlaus
  • 18,144
  • 10
  • 36
  • 64
  • 6
    Great post. One thing: it's not obvious what happens when multiple directives are specified; do the style-src settings in example 3 take precedence over default-src? etc... – track0 Jun 03 '15 at 21:36
  • 53
    So, to allow *everything everything everything* the content would be `default-src *; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'; img-src * data: 'unsafe-inline'; connect-src * 'unsafe-inline'; frame-src *;` – Arnold Roa Dec 22 '15 at 01:25
  • 12
    It's important to know that `content="default-src * 'unsafe-inline' 'unsafe-eval'"` is necessary to make some Angular applications work. – flanger001 Dec 29 '15 at 17:47
  • when i try to do `router.navigate()` in durandal it says navigation cancelled, what should i do it to allow every thing nothing works for me from your answers and comments .. – Touqeer Shafi Jan 07 '16 at 07:26
  • Thanks Arnold! My app couldn't load images properly using the permissive policy in Schlaus' post. With your policy, they load correctly. – Reubend Jan 25 '16 at 09:47
  • 1
    Great answer, but I think that instead of HTML `meta` tag, you should use a HTTP header. Please update your answer. I already edited the question; I hope you don't mind. You can also add a link to this great article on [HTML5 Rocks](http://www.html5rocks.com/en/tutorials/security/content-security-policy/). – Michał Perłakowski Jan 29 '16 at 21:22
  • @ArnoldRoa it does not accepts https src. Can anyone help please – Ashok Shah Jan 31 '16 at 14:28
  • @AshokShah did you resolve this? I'm having the same issue right now. I go out fine in Ripple, but Google emulator or on phone - nothing. – Aaron Jun 20 '16 at 19:09
  • @ArnoldRoa yes use this code. – Ashok Shah Jun 21 '16 at 23:13
  • Is there some obscure reason why, even after adding all possible script-src values you can possible imagine, all my dynamically created onclick events are ignored? This is the case in WP8 compiled with Cordova. – andreszs Aug 14 '16 at 23:25
  • @flanger001 For AngularJS apps, you can also use the `csp` directive, as described here: https://docs.angularjs.org/api/ng/directive/ngCsp – Matty J Sep 13 '16 at 01:47
  • Worth also noting that you can use `*` as a wildcard for (sub)domains - as documented with some good further information here: https://developer.mozilla.org/en/docs/Web/Security/CSP/CSP_policy_directives – Matty J Sep 13 '16 at 01:48
  • 1
    I found that `file:` was required and that `filesystem` didn't work for supporting `file:///` resources – andrewb Apr 07 '17 at 06:35
  • `'font-src': "'self' https://fonts.gstatic.com",` What about **font src** i didn't get anything about it .. I have still error for font . – MD Ashik Jul 28 '17 at 10:58
  • * also doesn't include "protocol" whitelists either, so `data:` `blob:` and the rest also need to be added along with * and the unsafe directives. Found that out when moving my scripts to blobs for local caching. – Ben Brocka Jan 12 '18 at 19:06
  • Someone should credit the original poster: https://girellinigirellini.blogspot.com/2010/03/javascript-how-does-content-security.html – Mahesh Jul 13 '18 at 18:26
  • 5
    @Mahesh That "blog" is full of posts copied from SO. Seems unlikely so many SO users would copy content from an unknown blogger - I know I didn't. – Schlaus Jul 17 '18 at 20:47
  • Yep. I think so too. But based on the dates posted, the blog seems to have been written in 2010. – Mahesh Jul 17 '18 at 21:04
  • 7
    Short note regarding `connect-src` and paths: trailing slashes are mandatory if you want to include a whole subpath. E.g.: the file `http://foo.com/files/bar.txt` will be blocked if the source is `http://foo.com/files`, but served when it's `http://foo.com/files/` – Gerrit-K Jul 26 '18 at 14:50
  • Even with allowing everything I am continuing to get the error (below). I had tried to be specific about sites but whatever I did i got the same error, so I thought I would try to allow everything but i continue to get the error. default-src *; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'; img-src * data: 'unsafe-inline'; connect-src * 'unsafe-inline'; frame-src *; Refused to load the script 'https://ssl.google-analytics.com/ga.js' because it violates the following CSP directive: "script-src 'unsafe-inline' 'unsafe-eval' 'self' – David Sep 30 '18 at 19:47
  • 2
    and if you don't want to take risks you can use " Content-Security-Policy-Report-Only". "The HTTP Content-Security-Policy-Report-Only response header allows web developers to experiment with policies by monitoring (but not enforcing) their effects. " for more information : https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only – mst Nov 01 '18 at 06:26
  • In order to get my create-react-app working on Heroku following this demo (https://github.com/fullstackreact/food-lookup-demo) I simply had to add this line in the `head` of the `index.html`. `` – HaulinOats Jul 08 '19 at 21:37
  • Is there a way to disable external script access while allowing inline scripts? I tried `content="script-src 'unsafe-inline'"` but it allows external scripts to be loaded. – Guillaume F. Jan 15 '20 at 03:28
  • Took me a while to find how to correctly add hashed inline styles (chrome gives you the hashes in the log). It's like this: `style-src 'self' 'unsafe-hashes' 'sha256-aqNNdDLnnrDOnTNdkJpYlAxKVJtLt9CtFLklmInuUAE=';` – Scott Apr 29 '22 at 11:43
  • I am using blobs from Azure. I had to add another directive : object-src 'self' blob: – leguminator Sep 26 '22 at 15:00
  • Till this day, i don't see a way to reformat event handlers like document.onload => what is a suitable replacement in order to meet the strict csp policy? – Kosem Mar 05 '23 at 13:26
  • How does it work, if a my app enforces CSP through Jboss, but communicate with an app that does not set CSP header, is my app vulnerable because of third party app? and how can I mitigate that? – Bionix1441 Jun 07 '23 at 18:17
  • How to specify script-src when my files are in webpack://prj_nam/node_modules/script.js? – weis_ss Aug 22 '23 at 19:27
21

Apache 2 mod_headers

You could also enable Apache 2 mod_headers. On Fedora it's already enabled by default. If you use Ubuntu/Debian, enable it like this:

# First enable headers module for Apache 2,
# and then restart the Apache2 service
a2enmod headers
apache2 -k graceful

On Ubuntu/Debian you can configure headers in the file /etc/apache2/conf-enabled/security.conf

#
# Setting this header will prevent MSIE from interpreting files as something
# else than declared by the content type in the HTTP headers.
# Requires mod_headers to be enabled.
#
#Header set X-Content-Type-Options: "nosniff"

#
# Setting this header will prevent other sites from embedding pages from this
# site as frames. This defends against clickjacking attacks.
# Requires mod_headers to be enabled.
#
Header always set X-Frame-Options: "sameorigin"
Header always set X-Content-Type-Options nosniff
Header always set X-XSS-Protection "1; mode=block"
Header always set X-Permitted-Cross-Domain-Policies "master-only"
Header always set Cache-Control "no-cache, no-store, must-revalidate"
Header always set Pragma "no-cache"
Header always set Expires "-1"
Header always set Content-Security-Policy: "default-src 'none';"
Header always set Content-Security-Policy: "script-src 'self' www.google-analytics.com adserver.example.com www.example.com;"
Header always set Content-Security-Policy: "style-src 'self' www.example.com;"

Note: This is the bottom part of the file. Only the last three entries are CSP settings.

The first parameter is the directive, the second is the sources to be white-listed. I've added Google analytics and an adserver, which you might have. Furthermore, I found that if you have aliases, e.g, www.example.com and example.com configured in Apache 2 you should add them to the white-list as well.

Inline code is considered harmful, and you should avoid it. Copy all the JavaScript code and CSS to separate files and add them to the white-list.

While you're at it you could take a look at the other header settings and install mod_security

Further reading:

https://developers.google.com/web/fundamentals/security/csp/

https://www.w3.org/TR/CSP/

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Erik Hendriks
  • 406
  • 4
  • 9
  • 2
    I was able to add those same directives to my .htaccess file, since I don't have the ability to edit Apache configurations on my shared host. I found excellent tools for adjusting these settings at https://report-uri.io/home/tools. – Michael McGinnis Oct 13 '17 at 02:26
  • Are there any way to resolve this one with tomcat 7. I have tried adding filters and didn't work. – Elshan Apr 11 '19 at 00:46
  • How does it work, if a my app enforces CSP through Jboss, but communicate with an app that does not set CSP header, is my app vulnerable because of third party app? and how can I mitigate that? – Bionix1441 Jun 07 '23 at 18:18