Deploying CSP: a 5-step approach

Spread the love

You may have seen recommendations from Dareboost to put in place a security policy regarding the origin of your resources. In case you haven’t dived in yet, I’d like to offer some advice on its implementation, from my experience.

The idea, already explained here, is simple: you define security guidelines for the browser to apply to the content of your pages. In practice, this header is very powerful and offers a lot of possibilities.

However, implementing a CSP can be frightening, as it is quite a complex matter. As a convinced practitioner who has been interested in the subject for years, I wanted to share with you an implementation methodology based on my own experience. But first, let’s put things into context.

CSP is not a binary switch

It is customary to consider security as a switch. A website is either secure or not.
This binary view of security is deeply wrong.: security is a process and, as such, can have different gradations dictated by your context, history, needs, budget, etc. CSP is no exception. You will often hear that you need to “do some belt-tightening” on the Front-End because of your CSP: like the boxer who adapts his weight according to the category in which he wants to compete, your CSP implementation will be more or less restrictive according to your security needs. A highly sensitive site will not require the same type of security as a showcase site.

Another important point to evaluate when considering the deployment of a CSP: how well do you master your Front-End?
When we talk about mastering the front-end, we don’t ask to prove that the team is gifted in CSS or if it is composed of JavaScript gods… But to understand what happens on the client side.

In other words, do you have an exhaustive view of your content sources (scripts, CSS, web fonts, third-party services, etc.)?

Do you know the exact impact of each script, resource or CSS? Their roles during the page rendering? What does all this need to work properly? What does it do exactly?

Do you know which portions of code are intended and controlled by your Front-End teams and which portions are inherent to the Back-End technologies being used – and therefore difficult to modify?

Please note that I don’t judge your answers. : in some contexts, I would have this knowledge myself (led by expertise, tools, documentation, etc.), and in others, I would not have it at all.

This fine knowledge is preferable but sometimes delicate or impossible to obtain.

Different levels of implementation

If you were able to answer yes to all of the above questions, then you will surely know what directives you can deploy. Start your deployment with Content-Security-Policy-Report-Only to prevent directives from improperly jamming elements loading. And use report-uri to retrieve violations. This should logically go well. At the very most, you might have a few simple adjustments to make before you can deploy. Mapping what is happening on your Front-End was the hardest part of the work.

If you lack this knowledge, you’ll have to fill this gap and deploy CSP gradually, tightening your belt one notch at a time.

During the 2017 issue of Paris Web (sponsored by Dareboost], I animated a workshop proposing a 5-step method to implement CSP.
Once again, these steps should not be considered judgments of your work! In some cases, I myself remain at first stage because I don’t have the opportunity to go any further.

Instead, think of it as your optimal weight (the upper-level restrictions include those of the previous levels). Another important point, If you can borrow elements from different levels without necessarily achieving them fully, don’t hesitate to do so.

A reminder: during the implementation, be sure to stick with Content-Security-Report-Onlyand specify, in addition to the directives I am going to indicate, a value for the directive report-uri. In the CSP-useful repository, I list several retrieval methods (sending an email, a JSON object, database storage…) for violations detected by the browser that will help you to adapt your control strategy.

One-Star implementation

Badge CSP ★

default-src * data: ;
script-src * 'unsafe-inline' 'unsafe-eval'  ;
style-src * 'unsafe-inline' data: ;
frame-ancestors 'none' ; # none or what you authorize

Since it is necessary to start with something, this first level allows everything that happens on the site (online scripts/CSS, eval function, data-uri, all origins, etc.). This policy only restricts the possibility of embedding the site in frames. This is an equivalent of the X-Frame-Options header (apart from this you can precisely define the sources for frame-ancestors). This should be your bare minimum.

Two-Star implementation

Badge CSP ★★

To put it bluntly: the previous level does not protect you from anything but clickjacking attacks. We will now specify your authorized sources for your various elements in order to be more restrictive.

default-src 'self' data: ;
script-src 'self' 'unsafe-inline' 'unsafe-eval' ;
style-src 'self' 'unsafe-inline' data: ;
frame-ancestors 'none' ;
base-uri 'none' ; # none or what you authorize

Here, you specify your sources (which prevents the use of the wildcard “*” selector) and set base-uri (either to none or what you need depending on your use of the base element: self, etc.).

Three-Star implementation

Badge CSP ★★★

The previous level restricts origins, but many things are still authorized. Let’s look at the web fonts and CSS.

default-src 'self' data: ;
script-src 'self' 'unsafe-inline' 'unsafe-eval' ;
style-src 'self' data: ;
font-src 'self' ;
frame-ancestors 'none' ;
base-uri 'none' ;

Compared to the 2-star level, the difference is that we decide to prohibit unsafe-inline for styles (so no more inline styles unprotected by hash/nonce, and no more style attributes on HTML elements), and we also remove the dangerous possibility of data-uri for the JS (strongly discouraged, because a strong vector for XSS attacks).

This is probably the first difficult level to achieve since it requires rethinking the way certain styles are set on the page. For example, scripts that have an unfortunate tendency to set inline styles will either have to be cleaned or replaced. While this may seem like a constraint at first, your sites will also benefit from no longer being subjected to these potentially annoying styles.

If you think that styles can only be harmless, please read this: CSS Exfil Vulnerability.

As for the web fonts, look at how it is possible to use CSS and webfonts to create a keylogger.

Four-Star implementation

Badge CSP ★★★★

A first step – and not the slightest – has already been taken by mastering CSS. But this is not the main vector of XSS attacks, as you can imagine. It’s time to step into the ring by tackling JavaScript permissions!

default-src 'self' ;
script-src 'self'  sha256-abcdef ;
style-src 'self' data: ;
font-src 'self' ;
frame-ancestors 'none' ;
base-uri 'none' ;

Here, we add strong restrictions on JavaScript scripts: no more unsafe-inline. In other words, either your inline scripts are protected by hashes/nonces, or you store them in external files served by approved origins.

And would you please restrict this eval function that is far too dangerous? :) Forbid yourself the use of unsafe-eval, to cut short a whole range of attack vectors. It’ll be for the best.

This is the second big step, which can be very difficult to reach. If your Back-End generates scripts (e.g. SharePoint), you will either have to learn to do without them or make sure that your Back-End also generates the nonces/hashes to authorize without authorizing inline scripts. Otherwise, you will also need to “enumerate” the inline scripts to authorize (by nonce or hash).

This level will force your Back-End and Front-End teams to work in concert to fully control your site.

Five-Star implementation

After two tough levels that ensure that you master your CSS and JS imports, this “last” level is likely to seem… easy to reach. It involves not authorizing anything implicitly.

default-src 'none' ;
script-src 'self'  ;
style-src 'self' data: ;
font-src 'self' ;
frame-ancestors 'none' ;
base-uri 'none' ;

You can detail further to refine your needs but with default-src to none, everything will be blocked by default, for these directives :

  • connect-src
  • font-src
  • frame-src
  • img-src
  • media-src
  • object-src
  • script-src
  • style-src
  • worker-src (CSP 3)
  • manifest-src (CSP 3)

Unlocking resources directive by directive will give you a clear understanding of what is happening on your Front-End so you can react and adapt.

If you don’t need plug-ins, make sure that object-src is set to none. Don’t forget the form-action directive for your forms either.

Badge CSP ★★★★★

To infinity and beyond!

Do not consider this last step as an absolute achievement. You will certainly be happy to get there, but it is still possible to fine-tune your directives.
CSP 2 makes possible to restrict the origins with paths (e.g. you can only authorize files from a specific path like

If you can, prefer integrity checks (hashes) to origins. They will protect you from unintentional and ill-advised changes by your own teams.

CSP 3 will be even stricter : you will be able to only authorize scripts and/or styles that will be protected by SubResource Integrity (SRI), or also to check external files via hashes (for the moment, only inline scripts/styles can benefit from it), or even to combine these two possibilities by requiring a script to be protected by SRI and that the hash belongs to an explicit list of values.

To say it in other words: if you have reached those levels and that is not enough, your problem is no longer CSP… but most likely other parts of the security chain. This may even involve hiring highly skilled and specialized people who will be dedicated to managing your needs about security! :)


CSP has this unique feature: it is a security technology that involves Back-End teams as well as Front-End teams (and even very strongly the latter).

Always be pragmatic with CSP (well, not just CSP): aim at directives that are relevant to your needs and contexts. If you have the opportunity to plan ahead, it’s always easier. Do not hesitate to involve and explain the functioning and the interest of the Marketing teams who are very concerned by the limitations and do not always understand the pursued objective. A redesign is a great time to clean up your Front-End code and impose good practices and a little of sobriety.

Also, do not hesitate to set up a continuous integration capable of reporting the infractions identified by your CSP. This can vary from an internal warning system that can prevent repeated violations of your directives to a system that can scan a version of the candidate site for production and verify that no critical dependencies are blocked.

You can also build CSP-friendly components one after another. It may take a long time, but it’s worth it.

If you use Dareboost, you must have already noticed that many third-party services are not as concerned about quality as you can be. CSP is one more reason to be challenging about them… or to be more frugal in their use !

I sincerely thank Boris Schapira and Philippe Guilbert for all the fine-tuning and reformulation work.

Additional Resources

Spread the love

About Nicolas Hoffmann

Front-end dev in ❤ with CSS, CSP/security, web mobile, #a11y, webperfs & Web quality. Father of Röcssti, @Van11y & some #a11y plugins. Staff member @parisweb and Openweb.