PHP and Content Security Policy

Do this first. Don't retrofit later

Secure PHP With Content Security Policy

How to prevent XSS / CSRF attacks via CSP

抬起你的帆一隻腳,你得到十英尺的風 -Raise your sail one foot and you get ten feet of wind.

This tutorial demonstrates how to use PHP and Content Security Policy to dramatically increase the security of your site and the safety of your users.

Cross-site Scipting Attacks(XSS) are one of the most dangerous threat vectors on the web. XSS works by allowing code injected into your web pages to bypass trust and connect to sites controlled by malicious people with very negative results for both the site owner and the sites customers.

Code with no Content Security Policy
No CSP - Not Good Enough

By default, browsers trust any code sent to them and cannot distinguish between a good site and a bad site. This is where Content Security Policy(CSP) comes into play. CSP is a white-listing mechanism that allows only connections that are explicitly set by the developer. This means that the browser stops rogue code from making unauthorized connections. Further, by default, CSP blocks the execution of inline scripts. These two mechanisms, blocking unauthorized connections and preventing rogue code execution, greatly enhance security, and makes CSP one of the strongest security enforcement mechanisms a developer can employ.

It's worth repeating...CSP is one of the strongest architectural enforcement tools you can employ.

Do CSP First - Don't Retrofit Later

A Content Security Policy, once in place, guides your development by forcing you to conform to good security practices.

Start with:

Content-Security-Policy: default-src 'none';

This blocks everythings, and forces you to identify and add each needed connection.

CSP is an architectural mechanism because it forces the developer to make explicit connection decisions and to write code in a more controlled fashion. The best example of this is how CSP blocks inline script execution by default. In order for scripts to execute, the develope has to employ seperation of concerns and properly seperate Javascript and CSS from HTML. For example, inline Javascript such as below are blocked and must be rewritten in order to pass CSP.

Inline JavaScript

function report() {
    console.log('Inline scripting'));

<button onclick='doReport();'>Inline Reporting</button>    


Seperated JavaScript

// report.html 
<script src='report.js'></script>
<button id='report'>Seperated Reporting</button>

// report.js
function doReport() {
  console.log('Seperated Reporting');
document.addEventListener('DOMContentReady', function () {
          .addEventListener('click', doReport);

Purchase on Amazon

This is structurally better code (improved code maintainance, browser caching, minification via gulp workflow, etc..) which is now forced upon us by an architecture constraint, the CSP rules. See Maintainable JavaScript by Zakas for an in depth treament of HTML/Javsascript seperation of concerns.

Inline styles are also prevented from executing, and CSP again forces the better practice of seperating style out from HTML and into a CSS file where it should go, and can be whitelisted via CSP.

Inline CSS

<h1 style="color:blue;">This is Inline</h1> 

Seperated CSS

h1 {
    color: blue;

<link rel="stylesheet" type="text/css" href="style.css">
<h1>This is Seperated</h1>


Starting Out with Content Security Policy

CSP is implemented in one of two ways. Either via an HTTP header, which in PHP looks like this.


header("Content-Security-Policy: default-src 'none';");

or via


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

Make the metatag the first to appear in the HTML header section. The policy will not apply to any content preceding the meta tag. Metatags could be less secure if an attacker able to alter page.

HTTP Headers are recommended because they come before metatags.

Content Security Policy Protects Your Site
Prevent XSS Attacks with CSP

I like using metatags as it makes it a bit easier to write out the rules clearly (You can make better use of space). This is discussed in more detail later with an example. My thoughts are that if an attacker is able to alter your page content being served, you've already lost.

There are basically two conditions in which CSP is employed. Either from the beginning of development on a brand new, Greenfield site, or applied retroactively onto an existing, Brownfield site. On a Greenfield site, start with default-src 'none'; This immediately forces one to pay attention to each script, image, CSS, font, and connection source, and write code accordingly.

On a Brownfield site, because CSP is so strict, it is best to start with Report-Only mode so that the site continues to function. This can only be done via a header and not a metatag. In PHP, it looks like this.

PHP CSP Report Only Header

header("Content-Security-Policy-Report-Only: default-src 'none';");

Adding this to an existing page reports all rule violations to the browser console without actually blocking any existing code. It can be a very illuminating to learn all the sources of connections and scripts in a page. If you were take CSP out of reporting mode and apply the rules, the page would most likely stop working as all of it's unwhitelisted functionality would be blocked.

In order to secure the page, change the header back from Content-Security-Policy-Report-Only to Content-Security-Policy and each violation will need to be either recoded for compliance or whitelisted in a policy rule.

The tools we will be working with:

Content Security Policy Directives

CSP uses several directives for locking down a site.

default-src Start Here. Set to 'none', which sets the default values for all directives ending in -src, and blocks everything unless overridden. default-src is overridden for each specific src directive specified.
script-src Allowed sources for scripts
connect-src Allowed connections for XHR/WebSocket requests
style-src Allowed sources for style sheets
img-src Allowed sources for images
font-src Allowed sources for fonts
object-src Allowed sources for plugins/Flash
media-src Allowed sources for audio/video
child-src Allowed sources for loading frames
base-uri Limits which URLs can be used in a page’s <base> element.
form-action Allowed paths for action attribute of <form> tags.
upgrade-insecure-requests Instructs browser to convert HTTP links into HTTPS.
report-uri A url to which the browser will post client-side policy failures.

CSP Source Rules

Use the following source matching rules to whitelist, allow/disallow, and restrict authorized sources and connections.

Source matching rules are quite straightforward. A domain name without a path, such as, matches all files served from the host. A source ending in a forward slash, such as, matches all files within that path and its sub paths.

Listed in order of preferred implementation - strongest to most open

'none' Matches all directive sources and blocks all source connections Allows strict connection to full domain name over HTTPS only. Most secure because SSL Endpoint is verified making HTTP spoofing very difficult.
'self' Matches to origin of served document, including the same URL scheme and port number. Provides high level of security as long as origin is not compromised. If used in combination with URL rewriting that forces page access over SSL only, (No HTTP access allowed) it is quite strong.
https: Matches any domain and restricts to protocol scheme HTTPS. SSL is enforced, but since any domain can used, some control over source connection is lost.
* Matches any subdomain of Control over source is maintained as long as HTTP source spoofing is not used by attacker. Always prefer HTTPS/SSL over HTTP when possible for this reason. HTTP is preferable over no control, and much better than nothing. This choice does prevent many attacks and threat vectors, forcing an attacker to use a more complex attack.
*:// Matches any protocol scheme. See above notes.* Matches any port (no * matches default port of either 80 or 443)
'unsafe-inline' Allows inline execution of scripts or CSS. Avoid if possible. Sometimes not possible without a great deal of code rewriting and cost. Can be mitigated though by using only trusted sources, and not allowing user input.
'unsafe-eval' Allows execution of eval() function, turning strings into code. Avoid.
* Allows any source connection other than blob:/data:/filesystem:. default-src * equates to no CSP at all.

Content Security Policy Example

We'll cover protecting this article with CSP, and starting from 'sort of' the beginning. Since this blog uses the BootStrap Template, Clean-blog, there are elements already established even with a 'new page'.

The Importance of HTTPS

SSL is about more than just encryption. It verifies that the connection you are making is to company who underwent some checks to prove they are who they say they are. This guarantees you are talking to who you think you are talking to. It is strong protection against HTTP spoofing, and is the reason why it is important to architect a site accessible over HTTPS only. The less reliance on HTTP, the better.

So we'll start with putting a metatag as the first line of our HTML header.

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

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Discusses Techniques for Using Content Security Policy">
    <meta name="author" content="Glaser">

    <!-- Theme CSS -->
    <link href="img/secure-PHP.png" rel="icon" type="image/x-icon">
    <link href="css/bundle.min.css" rel="stylesheet">

    <link href='' rel='stylesheet' type='text/css'>

    <title>PHP and Content Security Policy</title>

This has the following two results. A list of violations in the browser output...

Content Security Policy Rule Violations

And a broken site from blocked code execution...

Content Security Policy Blocks Code


Here we see that we have the following violations to account for:

  1. Refused to load image from
  2. Refused to load stylesheet from
  3. Refused to load stylesheet from
  4. Refused to apply inline style
  5. Refused to load script from

What is nice is that you are given the line number of the each violation so you can examine the cause. Here we see we have an inline CSS issue at line 73.


Here is the solution I arrived at, with an explaination for each decision. This solution is given as a example that may apply to others in a similar situation, but is not a solution for everyone, or everycase. While hopeful that my thought process might prove useful in thinking over the issues, my security decisions are not your decisions, so please take that into account. Use your own criteria.

Updated Policy

<meta http-equiv="Content-Security-Policy" content="default-src 'none' ; 

    font-src 'self' 

    img-src 'self' 'unsafe-inline' 

    style-src 'self' 'unsafe-inline' 


    connect-src 'none' ;
    child-src 'none' ; 
    object-src 'none' ;">                        


My particular site has a few issues related to sources. One I need fonts and images from external sites, Amazon and Google. Google fonts also uses some inline styles. Two, Bootstrap uses both inline images, in this case the background image used in the header, as well as some inline styles.

My server also does not resolve and as the same, so I have to account for both names in the CSP rules.

Fortunately, all my JavaScript is seperated, and I could add a rule restricting it to my site over HTTPS only. However, not all CSS could be seperated without extra time and cost. I decided it was not worth it. One advantage of this page is that it does not utilize user supplied data, so that is a concern I did not have to account for.

For img-src, css-src and font-src, First, I had to add the external Amazon, Google, and GStatic domains needed for sources via HTTPS only. However, this was not enough. Inline CSS is used and I could not rewrite it. Only because the external sources are HTTPS sources, in which the end points are verified, and Amazon and Google have trusted security teams, it is an acceptable risk to use 'unsafe-inline' here in this case. Again, a factor was that I'm not including untrusted user supplied content, only content I control, so the risk was acceptable.

I used 'self' instead of rewriting six times to account for all domain possibilities. I did not want spoofing possiblities via HTTP so I added SSL redirection to each page. The pages on my site are now not accessible via HTTP, which means 'self' will always be over HTTPS, giving me a good degree of security.

For script-src, I did want to be more protective and pedantic, so specifically stated both HTTPS domains that scripts could come from.

Even though this occurs already because default-src 'none' is previously set, connect-src, object-src, and child-src are specically turned off via 'none', mostly as a reminder to myself as I read things over.

Lastly, I'm glad to have all external sources occur over HTTPS, and not have possible HTTP problems.

Mike West has a great article on CSP, and how to include social sources into a set of CSP rules.

Purchase on Amazon

CSP Resources

Content Security Policy Reference

Mozilla Content Security Policy Reference

Google Developers CSP Guide

CSP Cheat Sheet

OWASP Content Security Policy Cheat Sheet

Scott Helme has a very good Content Security Policy Cheat Sheet