Introduction
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.
Let’s append 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 img-src
directive.
<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
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 <frame>
and <iframe>
. This directive is deprecated in CSP 3. Instead of child-src
, to list trusted source for nested browsing contexts and workers, the frame-src
and worker-src
directives should be used respectively. script-src
: Lists trusted sources for JavaScript object-src
: Lists trusted sources for the <object>
, <embed>
, and <applet>
elements 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 <audio>
, <video>
and <track>
elements frame-src
: Lists trusted sources for nested browsing contexts loading using elements such as <frame>
and <iframe>
font-src
: Lists trusted sources for fonts loaded using @font-face
connect-src
: Limits the URLs which can be loaded using script interfaces. Script interfaces include <a> ping
, Fetch
, XMLHttpRequest
, WebSocket
and EventSource
worker-src
: Lists trusted sources for Worker
, SharedWorker
, or ServiceWorker
scripts base-uri
: Limits the URLs which can be used in a document's <base>
element 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-flash
in this directive sandbox
: Put the resource under a sandbox similar to the <iframe>
sandbox
attribute. 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 <frame>
, <iframe>
, <object>
, <embed>
, or <applet>
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-to
: report-uri
(mentioned above) has been renamed to report-to
and report-uri
is 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-uri
and report-to
to 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
CSP Values
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 *
: wildcard - ‘
unsafe-inline
’: Allows the inline JavaScript code and stylesheet - ‘
unsafe-eval
’: Allows dynamic JavaScript through eval()
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.
https://example.com:80/
Whereas in CSP 2, subdomains and paths are allowed. This URI allows all files in the js folder:
https://example.com:80/js/
This URI treats js as file, not a folder, as it is not ended with a forward slash:
https://example.com:80/js
To allow all subdomains, use an asterisk as a wildcard.
https://*. example.com:80/
Conclusion
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.
What Web Developers Need to Know About Content Security Policy - Part 2