Saturday, June 3, 2017

Content Security Policy (CSP) in Spring

The same origin policy is an important concept in the web application security. The data of https://myweb.com should only be accessed by code from https://myweb.com. And should never be allowed to access by http://evilweb.com.

Cross-site scripting can bypass the same origin policy by injecting malicious script into trusted web sites. If an attacker injects any script which successfully executed, bad things happen like user session could be compromised or sensitive information could be sent to the attacker.

Content Security Policy (CSP) which is supported by modern browsers can reduce the risk of Cross-site scripting significantly.

How it works

So how CSP works? For example, the Google+ follow button (next to my profile picture) on my blog loads and executes code from https://apis.google.com. We know the code is trusted. But browser doesn't know which sources are trusted and which are not.

CSP introduces the Content-Security-Policy HTTP header that allows you to tell the browser which sources are trusted, like a whitelist. Back to the Google+ button example, we can define a policy that accept scripts from https://apis.google.com.

Content-Security-Policy: script-src 'self' https://apis.google.com

As you can tell, script-src is a directive that controls a whitelist of scripts sources. We tell the browser, 'self' which is current page's origin and https://apis.google.com are trusted scripts sources. Scripts from current page's origin and https://apis.google.com are allowed to be executed. Scripts from all other origins should be blocked.

So if unfortunately, an attacker successfully injects a script from http://evilweb.com into your site. Because http://evilweb.com is not in the list of script-src. Instead of loading and executing the script, a modern browser will block the script with an error saying something like "Refused to load the script from 'http://evilweb.com' because it violates the following Content Security Policy directive: script-src 'self' https://apis.google.com".

Directives

Policy applies to a wide variety of resources. A full list of valid directives can be found on W3C Recommendation. And apart from domains, four keywords can be used in the source list.

'self' - matches the current origin, but not its subdomains
'none' - matches nothing, even current origin is not allowed
'unsafe-inline' - allows inline JavaScript and CSS
'unsafe-eval' - allows text-to-JavaScript mechanism

All the above keywords require single quotes.

In order to protect against Cross-site scripting, a web application should include:
  • Both script-src and object-src directives, or
  • A default-src directive
In either case, 'unsafe-inline' or data: should not be included as a valid sources. As both of them enable Cross-site scripting attacks.

By default, directives are accepts everything. So if you don't define any directives, any resources can be loaded and executed by the Browser. You can change the default by define the default-src. For example, let's define the default-src as below.

Content-Security-Policy: default-src 'self' https://google.com

If we don't define a script-src, the browser knows to allow scripts from current page origin and https://google.com only, and blocks scripts from any other origins.

Note the following directives don't use default-src as a fallback. So if they are not defined, it means allowing everything.

  • base-uri
  • form-action
  • frame-ancestor
  • plugin-types
  • report-uri
  • sandbox
For more details about directives, please read W3C Recommendation.

Configuring CSP in Spring

Spring framework provides an easy way to configure CSP by using Spring Security module. Please note Spring Security module doesn't add CSP by default.

To enable CSP header using XML configuration:

<http>
 <!-- ... -->

 <headers>
  <content-security-policy policy-directives="script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com;">
         </content-security-policy>
        </headers>
</http>

To enable CSP header using Java configuration:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
 http
 // ...
 .headers()
  .contentSecurityPolicy("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/");
}
}

Additional Resources