Content Security Policy

Content Security Policy Directives

CSP directives can be logically divided into eight groups:

  • the base-uri directive declaring a URL that is used to denote the base URL of the protected resource;
  • directives for client scripts allowing the developer to filter out potentially insecure JavaScript code;
  • font and style directives;
  • directives for images and media;
  • the form-action directive controlling the form data upload;
  • directives governing nested browsing contexts;
  • directives restricting embedded content in the protected resource;
  • the sandbox directive declaring an HTML sandbox policy.

Two more directives are special: one of them sets a default source list for a number of aforementioned directives, the other provides a URL for CSP violations reporting.


Document Base URL

base-uri

Relative URLs declared in the protected resource are resolved according to a base URL. By default, the base URL coincides with the URL of the document. The developer can set a base URL explicitly by adding a <base> element to the <head> section of HTML markup. The <base> must precede any other element referring to an external Web asset:

<head>
 . . .
 <base href='http://example.com/base/'>
 <script src='utilities.js'></script>
 . . .
</head>

The location of utilities.js is resolved as http://example.com/base/utilities.js.

Declaring a URL in the base-uri directive will make the browser ignore the href attribute of the <base> if its value does not conform to the CSP prescription:

<?php
 header('Content-Security-Policy: base-uri http://example.com/trusted/js/');
?>
<!DOCTYPE html>
<html>
 <head>
 . . .
  <base href='http://example.com/base/'>
  <script src='utilities.js'></script>
 . . .
 </head>
 . . .
</html>


Client Scripts

connect-src

The connect-src directive deals with client scripts loading external assets or connecting to an endpoint of the remote server. The directive imposes restrictions on the client code using XMLHttpRequest or Fetch APIs, sending beacons, opening a WebSocket, parsing server-sent events or pinging an endpoint.

XMLHttpRequest API

The connect-src is applied by the browser to a client script calling the open() and send() methods of an XMLHttpRequest object:

<?php
 header('Content-Security-Policy: connect-src https:');
?>
<!DOCTYPE html>
<html>
 . . .
 <script>
  var xhr = new XMLHttpRequest();
  xhr.onload = () => {
   if(xhr.status === 200) {
    console.log(xhr.responseText);
   }
  };
  xhr.open('GET', 'http://example.com/docs/document.txt');
  xhr.responseType = 'text';
  xhr.send();
 </script>
 . . .
</html>

The CSP directive above requires that all resources retrieved by client scripts should be sent through the encrypted communication channel, so the text file from http://example.com will not be loaded.

Fetch API

Let's use the same https: scheme directive with the fetch() method. As a result of CSP violation, the returned Promise object will get into the failed state. The state handler will show the message about the network error:

<?php
 header('Content-Security-Policy: connect-src https:');
?>
<!DOCTYPE html>
<html>
 . . .
 <script>
  var request = new Request('http://example.com/docs/document.txt');
  fetch(request).then(response => {
   response.text().then(txt => {
    console.log(txt);
   });
  }, eObj => {
   console.error(eObj.message);
  }
  );
 </script>
 . . .
</html>

The sendBeacon() Method

The sendBeacon() method of the navigator object is also a possible target of the connect-src directive:

<?php
 header('Content-Security-Policy: connect-src https:');
?>
<!DOCTYPE html>
<html>
 . . .
 <script>
  navigator.sendBeacon('http://example.com/analytics.php', 'Request time: '+new Date().toGMTString());
 </script>
 . . .
</html>

WebSockets

Creation of WebSocket objects can be restricted by the security policy, too:

<?php
 header('Content-Security-Policy: connect-src wss:');
?>
<!DOCTYPE html>
<html>
 . . .
 <script>
  var ws = new WebSocket('ws://example.com/server-socket');
  . . .
 </script>
 . . .
</html>

The wss: scheme requires that socket traffic to and from the protected resource should be protected via TLS. As the client script uses the ordinary ws: scheme, the browser will consider it a security violation and refuse to instantiate the socket.

Server-Sent Events

EventSource objects subimit themselves to the same CSP directive:

<?php
 header('Content-Security-Policy: connect-src http://example.com/event-stream-generator.php');
?>
<!DOCTYPE html>
<html>
 . . .
 <script>
  var source = new EventSource('http://example.com/sse-demo.php');
  . . .
 </script>
 . . .
</html>

Pinging an Endpoint

An <a> element can have the ping attribute specifying a URL which is notified when the hyperlink is clicked:

<a href='http://example.com/books/tutorial.html' ping='http://example.com/logs/log.php'>click to read the tutorial</a>

When the user clicks on the hyperlink, the browser creates a POST request with the Ping-From and Ping-To headers:

Ping-From: http://example.com/protected-resource.php.
Ping-To: http://example.com/logs/log.php

The Content-Type of the request is text/ping, its payload is the PING string value.

To prevent the misuse of hyperlink auditing, the connect-src directive can restrict the range of allowed endpoints.

script-src

The script-src directive is applied to sources of <script> elements:

<?php
 header('Content-Security-Policy: script-src https:');
?>
<!DOCTYPE html>
<html>
 . . .
 <script src='http://cdn.example.com/trusted/script.js'></script>
 <script src='http://example.com/js/script.js'></script>
 . . .
</html>

Neither of the scripts above will be loaded by the browser: the https: CSP directive has been violated.

HTML Imports

The script-src directive also makes the browser check the href attribute of a <link> element importing an external resource:

<?php
 header('Content-Security-Policy: script-src https:');
?>
<!DOCTYPE html>
<html>
 . . .
 <link rel='import' href='http://components.example.com/custom-element.html'>
 . . .
</html>

The browser supporting Web components will not import the custom-element.html informing the user about the CSP violation.

XSLT

Extensible Stylesheet Language Transformations specify a language for parsing an input XML source tree and converting it into a result tree of nodes: for example, an XML document can be transformed into an HTML page.

The protected resource in the example below is an XML document containing a list of XML standards. The corresponding XSLT file moulds HTML markup and creates a list of hyperlinks by the client script:

XML document
<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet type='text/xsl' href='xslt-demo.xsl'?>
<standards>
 <doc>http://www.w3.org/TR/xml11/</doc>
 <doc>http://www.w3.org/TR/xmlschema11-1/</doc>
 <doc>http://www.w3.org/TR/xmlschema11-2/</doc>
 <doc>http://www.w3.org/TR/xpath-30/</doc>
 <doc>http://www.w3.org/TR/xslt20</doc>
</standards>

XSL Transformation Rules
<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
 <xsl:template match='/standards'>
  <html>
   <head>
    <title>XSLT Demo<title>
   </head>
   <body>
    <xsl:apply-templates select='doc'/>
    <script>
     var docs = Array.from(document.querySelectorAll('div'));
     docs.forEach(div => {
      var link = document.createElement('a');
      link.href = div.textContent;
      link.textContent = div.textContent;
      div.textContent = '';
      div.appendChild(link);
     });
    </script>
   </body>
  </html>
 </xsl:template>
 <xsl:template match='doc'>
  <div>
   <xsl:value-of select='.'/>
  </div>
 </xsl:template>
</xsl:stylesheet>

As XSLT documents can include JavaScript, their sources are controlled by the script-src security directive:

<?php
 $filename = "xml.xml"; // protected resource referring to XSLT
 $size = filesize($filename);

 header("Content-Type: text/xml; charset=UTF-8");
 header("Content-Length: $size");
 header("Content-Security-Policy: script-src 'self' 'unsafe-inline'");

 $handle = fopen($filename, "r");
 $xml=fread($handle, $size);
 fclose($handle);

 echo $xml;
?>


Fonts and Styles

font-src

The font-src directive checks the src descriptor of the @font-face rule:

<?php
 header('Content-Security-Policy: font-src http://example.com/trusted/Roboto-Regular.ttf');
?>
<!DOCTYPE html>
<html>
 . . .
 <style type='text/css'>
  @font-face {
   font-family: 'Roboto Th';
   src: url(http://example.com/fonts/Roboto-Thin.ttf);
  }
  body {
   font-family: 'Roboto Th';
  }
 </style>
 . . .
</html>

The CSP directive above only permits the use of the regular Roboto font from a trusted location. As a result, the browser will block the load of the Roboto Thin font from another URL.

Font Loading API

The font-src restrictions are also applied to the Font Loading API:

<?php
 header('Content-Security-Policy: font-src https:');
?>
<!DOCTYPE html>
<html>
 . . .
 <script>
  var roboto = new FontFace('Roboto Th', 'url(http://example.com/fonts/Roboto-Thin.ttf)');
  roboto.load().then(function (font) {
   if (font.status === 'loaded') {
    document.body.style.fontFamily = font.family;
   }
  }, function (eObj) {
   console.error(eObj.message);
  }
  );
 </script>
 . . .
</html>

The arguments of the FontFace constructor correspond to the font-family and src descriptors of the @font-face CSS rule. Calling the load() method returns a Promise object. If the Promise is fulfilled, the loaded font can be used to define the style of the document. However, the Promise in the example above will fail: the CSP directive prescribes to the browser that all font files should be obtained from secure sources. The error handler will display the following notice in the browser console:

A network error occurred.

style-src

The style-src directive states a source list of trusted stylesheets:

<?php
 header('Content-Security-Policy: style-src *');
?>
<!DOCTYPE html>
<html>
 . . .
 <link rel='stylesheet' href='http://example.com/style/css.css' type='text/css'>
 . . .
</html>

The http://example.com/style/css.css URL of the external stylesheet satisfies the demands of the security policy. On the other hand, the wildcard directive does not sanction the use of inline CSS: to authorize rules created within the scope of the <style> element, the server should rewrite the CSP header as

Content-Security-Policy: style-src * 'unsafe-inline'

Likewise, the 'unsafe-inline' value is required for CSS rules declared by the style attribute of HTML elements.

XML Styling

A CSS file can be associated with an XML document by using the xml-stylesheet processing instruction:

<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet href='style.css' type='text/css'?>
. . .

Similar to the href attribute of the <link> element in HTML, the href of the xml-stylesheet processing instruction must not violate the style-src directive.

SVG Styling

An SVG image in essence is an XML document describing two-dimensional graphics, so SVG elements can be styled through the use of XSLT or CSS. The inline <style> element with CSS rules is inserted in SVG as a CDATA section:

. . .
<defs>
 <style type='text/css'>
  <![CDATA[
   rect {
    fill: lime;
    stroke: yellow;
    stroke-width: 2
   }
  ]]>
 </style>
</defs>
. . .

The style-src directive affects both external and inline CSS associated with an SVG image:

<?php
 $filename = "svg.svg"; // protected resource
 $size = filesize($filename);

 header("Content-Type: image/svg+xml");
 header("Content-Length: $size");
 header("Content-Security-Policy: style-src 'self' 'unsafe-inline'");

 $handle = fopen($filename, "r");
 $svg=fread($handle, $size);
 fclose($handle);

 echo $svg;
?>


Images and Media

img-src

The img-src directive declares a trusted URL for image loading:

<?php
 header('Content-Security-Policy: img-src http://example.com/images/');
?>
<!DOCTYPE html>
<html>
 . . .
 <img src='http://example.com/res/prohibited.png'>
 <img src='http://example.com/images/allowed.gif'>
 . . .
</html>

The img-src restrictions are applied to both images declared in markup and images created by client scripts:

// this code will not be executed
var prohibited = new Image();
prohibited.src = 'http://example.com/icons/icon.png';
document.body.appendChild(prohibited);

// this code will load the image
var allowed = new Image();
allowed.src = 'http://example.com/images/icon.png';
document.body.appendChild(allowed);

media-src

The media-src directive exercises control over the load of audio and video resources:

<?php
 header('Content-Security-Policy: media-src http://media.example.com/; img-src http://media.example.com/images/');
?>
<!DOCTYPE html>
<html>
 . . .
 <audio src='http://example.net/media/audio.mp3' controls autoplay></audio>
 . . .
 <video src='http://media.example.com/video.webm' controls poster='http://media.example.com/images/poster.png'>
 </video>
 . . .
</html>

The src attribute of the <audio> element in the example above violates the security policy, so the MP3 file will not be loaded, The source of the WebM file, however, conforms to the CSP, and the video will be played. The poster of the <video> has a URL value complying with the img-src directive.

A media element can contain one or more <source> elements enumerating alternative media assets:

<?php
 header('Content-Security-Policy: media-src http://media.example.com/');
?>
<!DOCTYPE html>
<html>
 . . .
 <audio controls autoplay>
  <source src='http://media.example.com/audio.mp3' type='audio/mpeg; codecs=mp3'>
  <source src='http://media.example.com/audio.ogg' type='audio/ogg; codecs=vorbis'>
 </audio>
 . . .
</html>

The <source> elements are processed by the browser according to the media-src directive. Both audio files in the example above can be loaded as their URLs abide by the security policy. The browser will load and play the file with the supported audio format. If the browser supports both codecs, it will play the first file from the source list.

The CSP directive for media affects the <track> element, too:

<?php
 header('Content-Security-Policy: media-src http://media.example.com/');
?>
<!DOCTYPE html>
<html>
 . . .
 <video src='http://media.example.com/video.webm' controls>
  <track kind='subtitles' src='http://media.example.com/webvtt/video.en.vtt' srclang='en'>
 </video>
 . . .
</html>


Forms

form-action

The form-action directive places restrictions upon the action attribute of the <form> element:

<?php
 header('Content-Security-Policy: form-action http://example.com/trusted/form-data-handler.php');
?>
<!DOCTYPE html>
<html>
 . . .
 <form method='post' action='http://example.com/fcgi/form-data-handler.php' enctype='multipart/form-data'>
  <input type='file' name='file-selector'>
  <input type='text' name='file-info'>
  <input type='submit' value='submit'>
 </form>
 . . .
</html>

Neither the selected file nor the accompanying text data will be uploaded to the server: the browser will block the upload and notify the user about the CSP violation.


Nested Browsing Context

frame-ancestors

The frame-ancestors directive defines the range of external Web applications that are allowed to embed the protected resource:

<?php
 header('Content-Security-Policy: frame-ancestors http://example.com/trusted/ancestor.html');
?>
<!DOCTYPE html>
<html>
 . . .
 <div>
  Hello from the protected resource!
 </div>
 . . .
</html>

The protected resource above can only be included in a document located at http://example.com/trusted/ancestor.html. If any other Web application tries to nest the protected page, it will be blocked by the browser.

The frame-ancestors directive is applied to the <iframe> and <frame> elements of the ancestor document:

<iframe src='http://example.com/protected-resource.php'></iframe>

In addition, the directive puts the security restraint upon the <object>, <embed> and <applet> elements:

<object data='http://example.com/protected-resource.php' type='text/html'></object>

frame-src

The frame-src directive monitors the load of Web assets for the <iframe> and <frame> elements in the protected resource. In the example below, the src attribute of the first <iframe> will be ignored by the browser, but the source document of the second <iframe> will be fetched and rendered. Likewise, any document in the example.com/trusted/ directory will be considered conforming to the CSP directive.

<?php
 header('Content-Security-Policy: frame-src http://example.com/trusted/');
?>
<!DOCTYPE html>
<html>
 . . .
 <iframe src='http://example.com/iframe-source-doc.html'></iframe>
 <iframe src='http://example.com/trusted/trusted-doc.html'></iframe>
 . . .
</html>

child-src

The second edition of the Content Security Policy standard marks the frame-src directive as deprecated and proposes the child-src as its replacement. However, the latter directive is not implemented universally, so for the time being it is safer to use the frame-src for nested browsing contexts.


Embedded Content

object-src

The object-src is employed with the <object>, <embed> and <applet> elements of the protected document.

The data attribute of an <object> refers to a URL of an external resource. The type attribute indicates a valid MIME type of the resource:

<object data='http://example.com/docs/document.pdf' type='application/pdf'></object>

The data must conform to the object-src CSP directive: for example, the Content-Security-Policy: object-src https://example.com/ header will make the browser reject the PDF file above.

The <embed> element exposes the URL of an external Web asset as the value of the src attribute:

<embed src='http://example.com/docs/document.pdf' type='application/pdf'>

Applets are considered obsolete and should be avoided in modern Web applications. However, if an <applet> element is created as part of the protected resource, its archive and code attributes are controlled by the object-src directive.

plugin-types

The CSP 2 standard has proposed the plugin-types directive as an additional instrument restricting the load of resources for <object>, <embed> and <applet> elements:

Content-Security-Policy: plugin-types application/pdf application/x-shockwave-flash

The directive has not gained the overall acceptance, so it may be found redundant in the future.


Sandbox Policy

sandbox

The sandbox directive imposes restrictions upon Web content hosted by the protected resource:

<?php
 header('Content-Security-Policy: sandbox');
?>
<!DOCTYPE html>
<html>
 . . .
 <script>document.body.requestPointerLock();</script>
 . . .
</html>

If the browser supports the directive, it will block the inline script and show the console notice informing the user that the document's frame is sandboxed and the 'allow-scripts' permission is not set. The sandbox directive permissions are equivalent to those that are used with the sandbox attribute of the <iframe> element. These are allow-forms, allow-pointer-lock, allow-popups, allow-same-origin, allow-scripts and allow-top-navigation.

To enable the client script requesting the pointer lock, the CSP directive above should be rewritten as

Content-Security-Policy: sandbox allow-scripts allow-pointer-lock


Default Source

default-src

The default-src acts as a "placeholder" for such directives as child-src, connect-src, font-src, img-src, media-src, object-src, script-src and style-src. It should be noted that explicit declaration of any of these directives will override the default settings. In the following example Web assets will be loaded by the browser if they share the same origin with the protected resource. As for client scripts, the default directive is not applied to them: they are to be loaded only from cdn.example.com.

Content-Security-Policy: default-src 'self'; script-src cdn.example.com


CSP Violation Reporting

report-uri

The report-uri directive contains a reference to a URL accepting the browser's reports about CSP violations:

<?php
 header("Content-Security-Policy: script-src 'self'; report-uri http://example.com/report-handler.php");
?>
<!DOCTYPE html>
<html>
 . . .
 <script>console.log('The script will be blocked');</script>
 . . .
</html>

As the script-src directive above does not have the value of 'unsafe-inline', the inline script will be blocked. The browser will generate a POST request with the Content-Type equal to application/json or application/csp-report. The payload of the request is a JSON object with the following structure:

{
 "csp-report": {
  "blocked-uri": "self",
  "document-uri": "http://example.com/protected-resource.php",
  "line-number": 18,
  "original-policy": "script-src http://example.com; report-uri http://example.com/report-handler.php",
  "referrer": "http://example.com/csp-demo.html",
  "script-sample": "console.log('The script will be blocked'...",
  "source-file": "http://example.com/protected-resource.php",
  "violated-directive": "script-src http://example.com"
 }
}

The blocked-uri designates the originally requested URL of the blocked Web asset. If a CSP violation is caused by inline scripts or styles, Chromium-based browsers represent the blocked-uri as the empty string; as for Firefox, it uses the 'self' value - just like in the JSON demo above.

The document-uri specifies the absolute URL of the protected resource.

The source file where the CSP violation has occurred is characterized by the values of the line-number and source-file.

The original-policy shows the CSP directive as it was formulated by the server. Firefox replaces the 'self' value with the origin of the protected resource both in original-policy and violated-directive.

The referrer contains the URL of the document referrer.

The starting strings of the blocked script are represented as the script-sample value.

The violated-directive describes the CSP directive currently violated.

Content-Security-Policy-Report-Only Header

The report-uri directive can be used in combination with the special Content-Security-Policy-Report-Only HTTP header:

Content-Security-Policy-Report-Only: script-src cdn.example.com; report-uri http://example.com/report-handler.php

If the server uses the header instead of Content-Security-Policy, the browser will not block Web resources breaking CSP directives: it will only send a report about CSP violations and display appropriate information in the browser console.