Improving security of your web applications with the Content Security Policy
Hello my dear friends.
Today we will talk about Content Security Policy and how it can help your to improve security of your web applications.
What is Content Security Policy?
Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware.
Usage example
In many cases many web applications do not need CSP, if they do not store and show some HTML/CSS data that user inputs. But in case when your web application should show some custom user content (html pages with css, some files, etc), you need to have good filter engine, which will remove any inline JavaScript code (<script>
tags, onclick
from <a>
links, etc). For example, if you build web mail client, you cannot remove all HTML from email tags, because email will be broken and customer will not be happy to read broken email. So you need to prevent JavaScript inline code injection by this HTML email for hackers with any posibility.
How to use it
Instead of blindly trust to everything that a server delivers, CSP defines the Content-Security-Policy
HTTP header that allows you to create a whitelist of sources of trusted content, and instructs the browser to execute or render only resources from those sources. Even if an attacker can find a hole through which to inject script, the script won’t match the whitelist, and therefore won’t be executed.
For example, if we trust cdn.example.com
to deliver valid code, and we trust ourselves to do the same, let’s define a policy that only allows script to execute when it comes from one of those two sources:
This head can contain such directives:
Directive | Example Value | Description |
---|---|---|
default-src | 'self' cdn.example.com | The "default-src" is the default policy for loading content such as JavaScript, Images, CSS, Font's, AJAX requests, Frames, HTML5 Media. See the Source List for possible values |
script-src | 'self' js.example.com | Defines valid sources of JavaScript |
style-src | 'self' css.example.com | Defines valid sources of stylesheets |
img-src | 'self' img.example.com | Defines valid sources of images |
connect-src | 'self' | Applies to XMLHttpRequest (AJAX), WebSocket or EventSource. If it is not allowed, the browser emulates a 400 HTTP status code |
font-src | font.example.com | Defines valid sources of fonts |
object-src | 'self' | Defines valid sources of plugins, eg <object>, <embed> or <applet> |
media-src | media.example.com | Defines valid sources of audio and video, eg HTML5 <audio>, <video> elements |
child-src (old version frame-src) | 'self' | Defines valid sources for loading frames |
sandbox | allow-forms allow-scripts | Enables a sandbox for the requested resource similar to the iframe sandbox attribute. The sandbox applies a same origin policy, prevents popups, plugins and script execution is blocked. You can keep the sandbox value empty to keep all restrictions in place, or add values: allow-forms allow-same-origin allow-scripts, and allow-top-navigation |
report-uri | /report | Instructs the browser to POST a reports of policy failures to this URI. You can also append "-Report-Only" to the HTTP header name to instruct the browser to only send reports (does not block anything) |
Source List
All of the directives that end with “-src” support similar values known as a source list. Multiple source list values can be space seperated with the exception of “*” and “none” which should be the only value.
Source Value | Example | Description |
---|---|---|
* | img-src * | Wildcard, allows anything |
'none' | object-src 'none' | Prevents loading resources from any source |
'self' | script-src 'self' | Allows loading resources from the same origin (same scheme, host and port) |
data: | img-src 'self' data: | Allows loading resources via the data scheme (eg Base64 encoded images) |
domain.example.com | img-src img.example.com | Allows loading resources via the data scheme (eg Base64 encoded images) |
*.example.com | img-src *.example.com | Allows loading resources from the any subdomain under example.com |
https: | img-src https: | Allows loading resources only over HTTPS on any domain |
'unsafe-inline' | script-src 'unsafe-inline' | Allows use of inline source elements such as style attribute, onclick, or script tag bodies (depends on the context of the source it is applied to) |
'unsafe-eval' | script-src 'unsafe-eval' | Allows unsafe dynamic code evaluation such as JavaScript eval() |
Usage example
As you can see we can combine all this values for “Content Security Policy” header and create most flexible rule for our app. Let’s create a little example. I am using Rails app with global gem to make it work with “Content Security Policy”. First I create global yml file with configuration (config/global/content_security_policy.yml
):
And user default_headers
inside rails app (config/application.rb
):
After restarting of the Rails app you should see “Content Security Policy” header in any HTTP response from your app. If someone will try to inject JS code in your app (onclick
in link), it will get such JS error:
Subresource Integrity
Many sites uses a content delivery network (CDN) to serve static assets such as JavaScript, CSS, and images to our users. The CDN makes web browsing faster by delivering assets from data centers that are geographically close to the end user and by using hardware and software that is optimized for quickly serving static assets. The compromise of a major CDN could be devastating to the security of the hundreds of thousands of sites that depends on it. If our CDN were to be compromised, it could be used to serve malicious JavaScript to all our users, rendering our many XSS mitigations and transport security useless. Content Security Policy is invaluable for protecting against traditional XSS attacks, but it provides no defense against an attacker who can control assets served from whitelisted sources.
To prevent this type of attack, you can use Subresource Integrity browser technology. The website author includes an integrity
attribute on JavaScript and CSS tags, specifying the cryptographic digest of the resource being loaded from the third party. When the browser fetches the resource, it computes the file’s digest and compares it with the value from the integrity
attribute. If the values match, the resource is loaded. Otherwise, the browser refuses to load the resource. Example:
If you are using Rails with sprockets-rails gem (version >= 3), you can add integrity
key to your javascript_include_tag
helper to activate this feature:
More info about Subresource Integrity you can read in this article.
Browser Support
CSP is designed to be fully backward compatible; browsers that don’t support it still work with servers that implement it, and vice-versa. Browsers that don’t support CSP simply ignore it, functioning as usual, defaulting to the standard same-origin policy for web content. If the site doesn’t offer the CSP header, browsers likewise use the standard same-origin policy.
Header | Chrome | FireFox | Safari | Internet Explorer/Edge |
---|---|---|---|---|
Content-Security-Policy (1.0) | 25+ | 23+ | 7+ | - |
X-Content-Security-Policy | - | 4.0+ | - | 10+ (limited) |
X-Webkit-CSP | 14+ | - | 6+ | - |
As you can see in this table, CSP have good support for major browsers. Internet Explorer 10-11 and Edge have partial support for CSP via the X-Content-Security-Policy
header, but even then they only appear to support the optional “sandbox” directive. More info on caniuse.
Summary
Content Security Policy can provide the additional security layer for your apps against XSS and data injection attacks (XSS is in third place in the ranking of the key risks of Web-based applications under the 2013 OWASP).
That’s all folks! Thank you for reading till the end.