The Safest Content Security Policy

Surprisingly, defaults-src 'self' is not the safest CSP policy. That's mostly because there are directives that don't fall back to default-src by default and are permissive when not specified.

Then what CSP policy is the most secure?

TL;DR: The most secure Content-Security-Policy which should work for most websites is:

default-src 'self'; media-src 'none'; child-src 'none'; frame-src 'none'; object-src: 'none'; connect-src 'none'; manifest-src 'none'; frame-ancestors: 'none'; base-uri 'none'; form-action 'self'

Now let's see why.

Getting started

We start with the most straightforward policy we can think of, and that is:

default-src 'self';

This policy is already powerful enough.

  1. It restricts all the resources (scripts, styles, images, etc.) only to allow being loaded from the current domain. Even subdomains are not allowed. So if our main domain is mydomain.org, it won't be able to load scripts from cdn.mydomain.org. This subdomain must be specifically included in the policy (e.g., default-src 'self' https://cdn.mydomain.org).

  2. Out of the box, it disallows inline javascript and eval. This, by itself, is a huge security booster because it protects us from XSS injections.

Note that technically speaking, we can make the policy even stronger by switching default-src 'none'. But practically, it makes little sense since. We probably want to load at least some resources like styles and fonts, even if our page doesn't use JavaScript at all.

Directives that fall back to default-src

Since we know what kind of resources we use on the website, typically, of course, scripts, styles, images, and fonts are all required to load, but sometimes some of them don't.

For example, if you don't use custom fonts and images (a text-only blog?), you should go ahead and disable those.

default-src 'self'; img-src 'none'; font-src 'none';

For the sake of this article, we won't disallow those resources since 99% of websites need those.

Disallowing less-popular resources

The media-src directive is used to restrict the loading of resources used in <audio>, <video>, and <track>. Chances are, you don't have those on your website. Let's go ahead and disable them.

default-src 'self'; media-src 'none';

The same goes for child-src (web workers and iframes) and frame-src (iframes, falls back to child-src).

default-src 'self'; media-src 'none'; child-src 'none'; frame-src 'none';

The worker-src directive governs web workers and falls back first to child-src, then to script-src, and finally to default-src. Since we already included child-src 'none', we don't need to add it specifically. But if you use web workers, you can add it.

Does anyone still uses Flash or Java Applets. Probably not, so let's disable object-src.

default-src 'self'; media-src 'none'; child-src 'none'; frame-src 'none'; object-src: 'none'

In case you don't use ajax (but also fetch, websockets, and some other) loading, you might want also to turn off connect-src. It does fall back to default-src, which is 'self' in our case. Let's assume we don't.

default-src 'self'; media-src 'none'; child-src 'none'; frame-src 'none'; object-src: 'none'; connect-src 'none'

Finally, assuming you don't need a manifest, you should probably disable manifest-src as well.

default-src 'self'; media-src 'none'; child-src 'none'; frame-src 'none'; object-src: 'none'; connect-src 'none'; manifest-src 'none'

Plenty of websites include a manifest, so instead of manifest-src 'none', we can set it to manifest-src 'self'.

Directives that don't fallback to default-src

Some of the directives don't fall back to default-src, and if we don't specify them, the browser will allow anything.

For example, did you know that anyone can include your website inside an iframe for whatever malicious reason on their website? We can disallow it with frame-ancestors.

default-src 'self'; media-src 'none'; child-src 'none'; frame-src 'none'; object-src: 'none'; connect-src 'none'; manifest-src 'none'; frame-ancestors: 'none'

The base-url directive restricts what can be set in base tag. If an attacker adds such a tag on your page, he can practically redirect all links to a malicious website. It's a good idea to set it to either 'self' or 'none'`.

default-src 'self'; media-src 'none'; child-src 'none'; frame-src 'none'; object-src: 'none'; connect-src 'none'; manifest-src 'none'; frame-ancestors: 'none'; base-uri 'none';

Finally, with form-action, we can control URLs that can be used as the target of form submissions. Similarly, it doesn't fall back to default-src, allowing any URLs by default. That's why it's important to restrict it to 'self' (or 'none' in case you don't have any form submissions on your website).

default-src 'self'; media-src 'none'; child-src 'none'; frame-src 'none'; object-src: 'none'; connect-src 'none'; manifest-src 'none'; frame-ancestors: 'none'; base-uri 'none'; form-action 'self'

Reporting

With Content-Security-Policy, we can set a reporting URL, which will serve as a collector for all violation reports. If something tries to violate any of the rules, the browser will send a JSON report to the specified URL. It's crucial to collect such reports and analyze them for possible vulnerabilities or mistakes.

For example, it might look like this with CSP Hero.

default-src 'self'; media-src 'none'; child-src 'none'; frame-src 'none'; object-src: 'none'; connect-src 'none'; manifest-src 'none'; frame-ancestors: 'none'; base-uri 'none'; form-action 'self'; report-uri https://app.csphero.com/report/Wab8kRBH

Wrap up

Turns out, default-src 'self' doesn't necessarily get us the most secure CSP. Disallowing resources like media-src, child-src, frame-src, object-src, connect-src, and manifest-src can significantly elevate security, especially if these resources aren't required.

Furthermore, directives like frame-ancestors, base-uri, and form-action can provide a strong line of defense against iframe misuse, base tag hijacking, and unauthorized form submissions, respectively. The important part is that they are allowing and don't fall back to default-src. That's why they need to be included and restricted specifically.

Collect And Analyze
CSP Reports in Real-Time ✨✨✨