The Web Application Security Working Group of the W3C has created a standard for web security called Content Security Policy (CSP). Currently on version two with a version three in the works, CSP has been implemented by the majority of browsers and offers a strong defensive layer against XSS. Unfortunately, web developers seem reluctant to use the policy. Based on recent research, only about two percent of the most popular million websites include CSP. This is a shame as CSP not only offers easy implementation and powerful protection but its use promotes best practices in web development.
Cross site scripting (XSS) is a particularly thorny problem. Any site that displays data input by users opens itself to this vulnerability, making it possible for an attacker to run malicious code within the context of the site, code that can steal session tokens, carry out transactions, and cause all sorts of damage. It is possible to filter malicious user input to avoid XSS, but attackers have bypassed filters, leading to an arms race of ever more clever filters and bypasses. CSP provides significant protection against XSS and other threats, but not a panacea, at least not until all browsers implement the standard.
To mitigate XSS, CSP enables site owners to restrict browser behavior, enforcing a white list of content sources that the browser will trust, making it extremely difficult for an attacker to get their malicious scripts to execute.
Configuring CSP
Site owners configure this white list of trusted sources through a CSP policy defined in an HTTP header. The CSP HTTP header value is a plain-text, semicolon delimited list of directives. Each directive includes the directive name followed by a space-delimited list of paths. The header applies the policy to an individual page. Think of the page as a security context that encompasses all content within it. Depending on need, a developer can craft custom policies per page or use the same policy on every page.
Content-Security-Policy: script-src mycdn.org/scripts/ myserver.org/scripts/
This simple policy tells the browser to only execute scripts downloaded from mycdn.org/scripts/ and myserver.org/scripts/, but not from any other sources. Even if an attacker managed to insert a script tag pointing to evilempire.com/maliciousscript.js, the browser would refuse to execute it.
CSP also supports several useful keywords. For example, adding the keyword ‘self’ to the script-src directive enables browsers to execute scripts from the site itself.
Eliminating Inline Scripts and eval()
Most importantly, the browser will not execute inline scripts, which prevents the vast majority of XSS attacks. The notion of inline here covers both script blocks and event handlers within HTML element attributes. While this restricts developers, it really just promotes the best practice of unobtrusive JavaScript, meaning keeping a clean separation between HTML and JavaScript by moving all JavaScript into script files. Event handlers can be easily added in code rather than through HTML attributes.
While the standard provides a way to override this restriction by adding the ‘unsafe-inline’ keyword to the script-src policy, this really should be avoided as it effectively eliminates almost all of the value of CSP.
CSP version 2 offers an additional alternate for inline script. It supports inline script blocks with a nonce tag that matches a nonce value in the script-src directive. Several such blocks could be added to a page. Again, this is less secure than avoiding inline scripts all together.
Content-Security-Policy: script-src nonce-WFNQcIGJSOCnmt00THCK
< script nonce="WFNQcIGJSOCnmt00THCK">
/* inline script that for some reason cannot be moved to script file */
</script>
CSP eliminates yet another vector for XSS attacks by preventing browsers from executing the eval function even when called within permitted content sources. The eval function executes strings, which poses a risk if the string contains any user input. Again, CSP does allow bypassing this protection by adding the ‘unsafe-eval’ keyword to the script-src directive, but this likewise is discouraged. (The limitation on eval applies as well to other JavaScript functions that can execute strings such as setTimeout, setInterval, and Function.)
The CSP Directives
CSP supports several other directives in addition to script-scr that can further lock down a site to prevent potential attacks.
style-src | Allowed sources for style sheets. |
img-src | Allowed sources for images. |
connect-src | Allowed paths for XHR and WebSocket requests. |
font-src | Allowed sources for fonts. |
object-src | Allowed sources for plugins such as Flash. |
media-src | Allowed sources for audio and video. |
frame-src | Allowed sources for loading frames. Deprecated in version 2 of the standard. Use child-src. |
child-src | Allowed sources for loading frames. New in version 2. |
frame-ancestors | The sites allowed to load the page as a frame. Applies to <frame>, <iframe>, , and <applet> tags. Useful to prevent clickjacking. |
default-src | Default values for all directives ending in -src. The default is overridden whenever a specific src directive is specified. |
base-uri | Limits which URLs can be used in a page’s <base> element. |
form-action | Allowed paths for action attribute of <form> tags. |
plugin-types | The types of plugins that the browser may load for the page. |
sandbox | Imposes iframe-like sandbox on the page, preventing popups, plugins, scripts, and forms. |
upgrade-insecure-requests | Instructs browser to convert HTTP links into HTTPS. |
report-uri | A url to which the browser will post client-side policy failures. |
Meta Tag
In addition to specifying CSP policies via HTTP headers, the standard supports defining policy in meta tags as in the example below. However, this approach is less secure since an attacker who manages to alter page content might find a way to alter the meta tag to enable their own exploit. Moreover, the policy will not apply to any content preceding the meta tag.
<meta http-equiv="Content-Security-Policy" content="script-src mycdn.org">
Path Matching and Wildcards
Path matching rules are quite straightforward. A domain name without a path, such as mydomain.com, matches all files served from the host. A path ending in a forward slash, such as mydomain.com/scripts/, matches all files within that path and its sub paths. Paths that do not end with a slash, such as mydomain.com/scripts/script.js, match only the one specific file.
To make policy definitions more flexible, the CSP standard allows for wildcards in paths.
* | Matches all content sources other than blob:, data: and filesystem: |
*.mysite.com | Matches any subdomain of mysite.com |
*://example.com | Matches any protocol scheme |
example.com:* | Matches any port (without the * the path by default matches the standard port of the protocol, meaning either 80 or 443) |
https: | Matches any domain but only with protocol scheme HTTPS |
Defense in Depth
CSP should be considered an important new layer in a defense in depth strategy rather than a single solution for web security. XSS remains a threat for users of browsers that do not implement the standard. Moreover, you can never be sure that user input saved through one web application is not resurfaced through another one of your organization’s sites. Continue to apply XSS filters to all user input both when stored and displayed.
Implementation
CSP is easy to implement for new sites. Begin with the strictest policy and ease up only as needed.
For existing sites, particularly larger, more complex sites, gaining the full benefit of CSP might prove difficult, especially if there are inline script blocks, attribute-based event handlers, and resources pulled from many sources. CSP supports an additional HTTP header, Content-Security-Policy-Report-Only, that eases the pain of finding policy violations within your site. Add this header rather than the standard CSP header so the browser will post each violation to the URL specified in the report-uri directive, enabling you can track and resolve. (See https://cspbuilder.info and https://report-uri.io for CSP violation tracking tools.) For guidance, read about the experiences of Twitter and Dropbox.
Supporting Tools and Libraries
Adding CSP headers to pages is straight-forward as all popular web servers and server-side web frameworks provide mechanisms for adding HTTP headers to responses. Here is a simple example in Asp.Net:
HttpContext.Response.AddHeader("Content-Security-Policy", "scipt-src ‘self’");
In addition, there are libraries available focused on HTTP security-related headers that include support for CSP.
Asp.Net | NWebsec | https://www.nuget.org/packages/NWebsec |
Django | Django-CSP | https://github.com/mozilla/django-csp |
Java | headlines | https://github.com/sourceclear/headlines (mutliple security headers) |
Salvation | https://github.com/shapesecurity/salvation (specific to CSP) | |
Node.js | helmut | https://www.npmjs.com/package/helmet |
PHP | php-csp | https://github.com/Martijnc/php-csp |
Ruby on Rails | secureheaders | https://github.com/twitter/secureheaders |
Making it even easier, the site http://cspisawesome.com provides an online tool for generating CSP headers. And my colleagues at Shape Security have created an online validator at https://cspvalidator.org to assure you’ve gotten your policy correct. So what are we waiting for?
Resources:
Supported Browsers http://caniuse.com/#feat=contentsecuritypolicy
Presentation by Adam Barth, https://youtu.be/pocsv39pNXA
Reference Guide http://content-security-policy.com/
Mozilla Reference Guide https://developer.mozilla.org/en-US/docs/Web/Security/CSP/CSP_policy_directives
OWASP Summary https://www.owasp.org/index.php/Content_Security_Policy
Tutorial by Mike West http://www.html5rocks.com/en/tutorials/security/content-security-policy/
Dropbox’s Experience https://blogs.dropbox.com/tech/2015/09/on-csp-reporting-and-filtering/
Twitter’s Experience https://blog.twitter.com/2011/improving-browser-security-with-csp
Leave a Reply