Content Security Policy (CSP) is a computer security standard introduced by the World Wide Web Consortium (W3C) to prevent cross-site scripting (XSS) and clickjacking attacks. Explained simply, CSP is a whitelist of origins of content that is allowed to load or execute on a webpage. We’ll look at the three versions of CSP and the relevant features of each, though it’s important to note CSP Level 3 is not yet ratified as a W3C recommendation and is still a working draft in progress. It is still subject to change from time to time before its standardization. As we go along, the differences between these versions will be pointed out to you.
What is Cross-Site Scripting?
Cross-Site Scripting (XSS) attacks are a type of code injection, in which malicious scripts are injected into trusted websites. A good example could occur on an ecommerce site: a buyer posts a product review with malicious code that is saved on the server. For every customer who views the product review, malicious code gets executed.
CSP in Action
CSP can be specified in an HTTP response header. When a web client, like a web browser, requests a resource from web server, it sends an HTTP request with a bunch of information in a request header for the server. If the request is successful, the web server then replies back with the resource together with a response header telling the web browser how to handle the response. In the case of CSP, it is specifying what those trusted sources are to fetch the web page content from. On CSP 2 capable browsers, we have an additional option of specifying the CSP in an HTML meta tag. For our examples, this is exactly what we are going to use; we take a web framework agnostic approach to keep things simple. All you need to follow the examples is a text editor and modern web browser.
Anatomy of CSP
CSP begins with
Content-Security-Policy text, which is followed by one or more directives. Each directive ends with a semicolon, which can be the beginning of the next directive. Each directive could have zero or multiple values. The values are separated by whitespace. More often than not, the value is simply a trusted source URI.
Content-Security-Policy [directive] <value>;
This is an example of a one-directive CSP. The
default-src directive with a ‘
self’ value instructs the web browser to only trust content from the same origin as the webpage.
Content-Security-Policy default-src 'self';
The equivalent CSP in a
meta tag is shown below:
<meta http-equiv="Content-Security-Policy" content="default-src 'self';>
Take note that the
meta tag has to be specified within the
head section, not the
body section of the HTML. One big downside a developer has to be cautious of: with the
meta tag approach, CSP rules are not enforced until the
meta tag is read and processed.
This is the HTML that loads the image from CodeProject without CSP. You can copy and paste the code in an empty HTML file and save it locally.
<html> <head> <title>CSP in Action</title> <head> <body> <p><img src="https://www.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif" /> </p> </body> </html>
View the HTML on the browser by double-clicking the file on the File Explorer, the image is downloaded and displayed from CodeProject.
Let’s add a CSP meta tag.
<html> <head> <meta http-equiv="Content-Security-Policy" content="default-src 'self';"> <title>CSP in Action</title> <head> <body> <p><img src="https://www.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif" /> </p> </body> </html>
Now try viewing the page in a browser:
Bam! Now the broken image is shown to indicate the image is not fetched because www.codeproject.com is not the same origin domain. Hit F12 on the web browser to open developer tool and navigate to console tab. On Chrome, it shows this error in red.
Refused to load the image ‘https://www.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif’ because it violates the following Content Security Policy directive: “default-src ‘self'”. Note that ‘img-src’ was not explicitly set, so ‘default-src’ is used as a fallback.
What we have effectively done with the
default-src directive is to restrict all the content to the same origin with the ‘
self’ keyword, as explained previously.
default-src with a whitespace and followed by the CodeProject URI. For simplicity, I just show the updated
meta tag as the rest of HTML remains unchanged.
<meta http-equiv="Content-Security-Policy" content="default-src 'self' https://www.codeproject.com;">
View the page again. Now the CodeProject image is shown. Note: The
self keyword has to be enclosed in single quotes while the URI is not required to be.
View the HTML on browser. Now the image is back. Since the gif is an image resource, let’s do some refactoring and put CodeProject URI under the
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://www.codeproject.com;">
View the page on browser again. The image still appears. Prior to that,
img-src is not specified. What is its value then? The answer is, when not specified, it inherits from
default-src. Note: If your URI redirects to a URI on another domain, that domain has to be in the CSP as well.
CSP directives mostly cover the content type whose source(s) can be specified. This article covers most of the directives. All the directives that fall back to the
default-src are shown on the hierarchy below.
default-src: Is a main fallback for the other fetch directives when they are not explicitly specified
child-src: Lists the trusted sources for web workers and nested browsing contexts loaded using elements such as
<iframe>. This directive is deprecated in CSP 3. Instead of
child-src, to list trusted source for nested browsing contexts and workers, the
worker-srcdirectives should be used respectively.
object-src: Lists trusted sources for the
style-src: Lists trusted sources for stylesheets (CSS)
img-src: Lists trusted sources of images and favicons
media-src: Lists trusted sources for loading media using the
frame-src: Lists trusted sources for nested browsing contexts loading using elements such as
font-src: Lists trusted sources for fonts loaded using
connect-src: Limits the URLs which can be loaded using script interfaces. Script interfaces include
worker-src: Lists trusted sources for
base-uri: Limits the URLs which can be used in a document’s
plugin-types: Limits the set of plugins that can be embedded into a document by limiting the types of resources which can be loaded. For example, to allow Flash, specify its mime type:
application/x-shockwave-flashin this directive
sandbox: Put the resource under a sandbox similar to the
form-action: Limits the URLs which can be used as the target of a form submissions from a given context
frame-ancestors:Limits valid parents that may embed a page using
report-uri: List URL for the web browser to report the Content Security Policy violation. These violation reports consist of JSON documents sent via an HTTP POST request to the specified URI. Deprecated in CSP 3, but still widely supported
report-uri(mentioned above) has been renamed to
report-uriis deprecated in CSP 3. However, at the time of article writing, not a single browser supports
report-to. It is perfectly fine to specify both
report-toto future-proof CSP
block-all-mixed-content: Forbids loading any assets using HTTP when the page is loaded using HTTPS
upgrade-insecure-requests: Instructs web browser to treat all of a site’s insecure URLs (those served over HTTP) as though they have been replaced with secure URLs (those served over HTTPS). This directive is intended for web sites with large numbers of insecure legacy URLs that need to be rewritten.
require-sri-for: Requires the use of Subresource Integrity (SRI) for external scripts or styles on the page
Each directive follows by one or more values separated by whitespace. The acceptable value types are in two main categories: keywords and URI.
All keyword, except wildcard, must be enclosed in single quotes:
self’: Restrict source to same origin
none’: No source is allowed
One very common reason to specify external trusted source URI other than the same origin is the need to support loading resource from a Content Delivery Network (CDN), a geographically distributed network of proxy servers that store commonly downloaded content.
URIs must not be enclosed in single quotes!
In CSP 1, only the scheme (http or https), domain and port number are allowed in the URI.
Whereas in CSP 2, subdomains and paths are allowed. This URI allows all files in the js folder:
This URI treats js as file, not a folder, as it is not ended with a forward slash:
To allow all subdomains, use an asterisk as a wildcard.
In the article, we have seen CSP put to good use in restricting the source of resource to eliminate the possibility of running/displaying untrusted contents. As a word of caution, utmost care must be undertaken by the developer to ensure that all sources needed by the webpage are not overlooked and omitted in the CSP, causing functionality to break, denying service to the user.