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.