Content Security Policy and Ruby and Clojure

(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at: lawrence@krubner.com, or follow me on Twitter.

Although I love Clojure, I must admit that Ruby and Rails have an impressive depth of gems to help with every aspect of web development, including security. John P Hackworth recently wrote of the weakness of the Clojure eco-system, although his criticism is also an attack on the whole of idea of “small libraries that compose well” which amounts to an attack on the idea of “small pieces, loosely joined”. Clearly, good security can be achieved with small libraries that are composed, though it is not as automatic as when using a monolithic framework like Rails.

The SecureHeaders gem is a wonder to behold. The ReadMe is, by itself, an education in security. I wanted to read more, so I discovered an article by Mike West regarding the Content Security Policy.

The core issue exploited by XSS attacks is the browser’s inability to distinguish between script that’s intended to be part of your application, and script that’s been maliciously injected by a third-party. For example, the Google +1 button at the top of this article loads and executes code from https://apis.google.com/js/plusone.js in the context of this page’s origin. We trust that code, but we can’t expect the browser to figure out on it’s own that code from apis.google.com is awesome, while code from apis.evil.example.com probably isn’t. The browser happily downloads and executes any code a page requests, regardless of source.

Instead of blindly trusting 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 only execute or render 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.

Since we trust apis.google.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:

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

Simple, right? As you probably guessed, script-src is a directive that controls a set of script-related privileges for a specific page. We’ve specified ‘self’ as one valid source of script, and https://apis.google.com as another. The browser will dutifully download and execute JavaScript from apis.google.com over HTTPS, as well as from the current page’s origin.

With this policy defined, the browser will simply throw an error instead of loading script from any other source. When a clever attacker does manage to inject code into your site, she’ll run headlong into an error message, rather than the success she was expecting

Policy applies to a wide variety of resources

While script resources are the most obvious security risks, CSP provides a rich set of policy directives that enable fairly granular control over the resources that a page is allowed to load. You’ve already seen script-src, so the concept should be clear. Let’s quickly walk through the rest of the resource directives:

connect-src limits the origins to which you can connect (via XHR, WebSockets, and EventSource).
font-src specifies the origins that can serve web fonts. Google’s Web Fonts could be enabled via font-src https://themes.googleusercontent.com
frame-src lists the origins that can be embedded as frames. For example: frame-src https://youtube.com would enable embedding YouTube videos, but no other origins.
img-src defines the origins from which images can be loaded.
media-src restricts the origins allowed to deliver video and audio.
object-src allows control over Flash and other plugins.
style-src is script-src’s counterpart for stylesheets.
By default, directives are wide open. If you don’t set a specific policy for a directive, let’s say font-src, then that directive behaves by default as though you’d specified * as the valid source (e.g. you could load fonts from everywhere, without restriction).

You can override this default behavior by specifying a default-src directive. This directive, as you might suspect, will define the defaults for any directive you leave unspecified. If default-src is set to https://example.com, and you fail to specify a font-src directive, then you can load fonts from https://example.com, and nowhere else. We specified only script-src in our earlier examples, which means that images, fonts, and so on can be loaded from any origin.

You can use as many or as few of these directives as makes sense for your specific application, simply listing each in the HTTP header, separating directives with semicolons. You’ll want to make sure that you list all required resources of a specific type in a single directive. If wrote something like script-src https://host1.com; script-src https://host2.com the second directive would simply be ignored. script-src https://host1.com https://host2.com would correctly specify both origins as valid.

Post external references

  1. 1
    https://hackworth.be/2014/03/26/clojure-web-security-is-worse-than-you-think/
  2. 2
    https://github.com/twitter/secureheaders
  3. 3
    http://www.html5rocks.com/en/tutorials/security/content-security-policy/
Source