Retrofitting Code for Content Security Policy

Update: Interested in getting your hands on the un-minified CSP-friendly port of reCAPTCHA JavaScript API code? Download it now from our GitHub account. https://github.com/SendSafely/reCaptcha_CSP/

In a previous blog post we shared how SendSafely uses Content Security Policy to minimize the risk of Cross-Site Scripting, commonly referred to as XSS (if you didn’t catch this post, you can check it out here).  While it would have been easiest to design our site to use CSP from the beginning, the initial version of our website grew out of an internal research project and was not so fortunate.  As a result, we needed to refactor a lot of our UI code to comply with a strict CSP.  Specifically, we needed to get rid of the following two patterns that were fairly pervasive in our code:  

  • Inline scripting.  A sound CSP does not allow HTML and JavaScript to co-exist in the same document.  Prior to CSP, we had a lot of in-line scripts.
  • Script code served from the same host.  CSP best practices dictate that scripts should only run from a dedicated sub-domain that serves static content.   This means that JavaScript not only needs to be in separate files, but also served from a completely different host.  

Given the above requirements, we needed to figure out an efficient way to convert our existing UI code.  As it turned out, our code followed a few simple patterns.  Once we came up with a methodical way to convert each pattern, we had a game plan for moving forward with the site-wide conversion.

Common Code Patterns
When we analyzed HTML our code to see how we were using Javascript, we were broadly able to categorize about 90% of our use cases into two buckets:

  • Links or tag events that called no-arg functions:
    <a href=”javascript:doSomething()”>Do something</a>
  • Links or tag events that called functions with one or more arguments:
    <type=”text” name=”email” onkeyup=”doSomethingElse(arg1, arg2)”>

The first case was simple.  For elements that previously called a function on a specific event or on a click, we started by giving them a unique element id.  Then, in the JavaScript code that loads from our static domain, we have a routine that always fires and looks specifically for each relevant id and programmatically registers the event on that element.  So, for example, the first sample we showed you above would get converted to the following HTML (on our dynamic domain) and JavaScript (loaded from the static domain).

HTML:

<a id=”my-link”>Do something</a>

JavaScript:

var link = document.getElementById(“my-link”);
link.addEventListener(“click”, doSomething, false);

If you use JQuery (like we do) it can be done in a slightly more elegant fashion:

$(“#my-link”).click(function() {
  doSomething();
});

The second case is not quite as simple, but still relatively straightforward. The main difference between the first and second case is that we need to pass arguments into the JavaScript function.  One of the most widely supported (and earliest adopted) parts of the HTML5 spec across all browsers is the data-* element.  It’s supported by all major browsers and has been for some time (http://caniuse.com/#feat=dataset).  This allows us to declare data attributes on a given HTML element that can be referenced elsewhere by JavaScript, so they are perfect for holding the values we were previously passing in as function arguments.   We use the same technique as before to register the click event, but also include references to the data-* attributes in the function call.  So, going back to our example, the second sample we showed you would get converted to the following HTML (on our dynamic domain) and JavaScript (loaded from the static domain).

HTML:

<type=”text” id=”my-email-field” name=”email” data-arg-one=”arg1” data-arg-two=”arg2”>

JavaScript (JQuery):

$(“#my-email-field”).keyup(function() {
  doSomethingElse(this.getAttribute(“data-arg-one”), this.getAttribute(“data-arg-two”));
});

Web Workers
Unfortunately not all of our JavaScript was covered by the above two examples.  One of the more notable exceptions to this was how to incorporate HTML5 Web Workers into our policy. We use web workers when we encrypt and decrypt files using JavaScript since CPU intensive operations like that would cause the entire browser UI to freeze-up during the process (which can take anywhere from a few seconds to several minutes).  As it currently stands, most browsers require that web workers execute from JavaScript on the same domain that the page is loaded from.  So, in the case of our website, pages loaded from www.sendsafely.com cannot run a web worker loaded from static.sendsafely.com. This is less than ideal from a security perspective since it requires an exception to our otherwise tight CSP.  

In order to minimize the places where this exception is allowed, we defined a slightly looser policy for the two URLS that we use for sending (encrypting) and receiving (decrypting) files.  Unlike other URLs on our site, these two pages allow scripts originating from the dynamic server to execute.  We still don’t allow in-line scripting, so the exposure on these pages is still somewhat minimal since a separate file still needs to be loaded from the same server.  For now it seems we will need to live with this approach until a solution for loading web workers from a separate domain is possible.

Third Party Scripts (reCAPTCHA)
Like many sites, SendSafely uses reCAPTCHA to prevent bots and other automated processes from interacting with certain parts of our application.  The reCAPTCHA AJAX API requires us to load certain scripts and images from Google servers (specifically from www.google.com/recaptcha/), which forced us to include www.google.com in our CSP (refer to the previous post to see how we’ve done that).  In an ideal world, that would be the only change needed, but life is rarely that simple.

Unfortunately, it doesn’t look like the reCAPTCHA AJAX API plays nicely with CSP since it doesn’t run without the inline-scripts and unsafe-eval directives.  Out of all the CSP directives to allow, these two create a huge increase in attack surface since they expose a wide variety of XSS attack variants. To better understand why the reCAPTCHA AJAX API requires these directives, let’s take a closer look at the two steps needed to implement the API (taken from https://developers.google.com/recaptcha/docs/display)

Step 1: Load the API JavaScript from Google

<script type=”text/javascript”
src=”http://www.google.com/recaptcha/api/js/recaptcha_ajax.js”
</script>

Step 2:  Display the CAPTCHA using the following code

Recaptcha.create(“your_public_key”,
  “element_id”,
  {
    theme: “red”,
    callback: Recaptcha.focus_response_field
  }
);

At their surface, both steps seem easy to run with CSP.  The problem, however, lies in the contents of recaptcha_ajax.js.  Specifically, the following three code patterns are present in this file and unless re-factored require inline-scripts and unsafe-eval permissions:

  • Inline Event Handler Definitions
  • Inline Script within HREF Attributes
  • Use of String-to-Code in Function Calls 

After some research and initial attempts to (unsuccessfully) contact the reCAPTCHA team at Google, we decided to take a stab at re-factoring some of the code to make it CSP friendly.  Refactoring third party code is never ideal, but if we could restrict our changes to just presentation-level code and not touch the code that invokes the server API, we minimize the risk of introducing any breaking changes going forward.  

As it turns out, the changes to recaptcha_ajax.js required are very minimal and self-contained in that single JS file.  Once updated, all we needed to do was load the  re-factored JS file from our server instead of remotely from the Google servers.  Let’s take a close look at what was changed.  

Inline Event Handler Definitions
Many of the reCAPTCHA HTML elements use in-line handler definitions for the onclick event.  In order to comply with CSP, the handler definition must be rewritten in terms of addEventListener as shown below (the “a” function is used to dynamically generate an HTML “a” tag with the specified ID).  Very easy.

Before:

a(“recaptcha_whatsthis_btn”).onclick = function () {
Recaptcha.showhelp(); return !1 };

After:

document.getElementById(“recaptcha_whatsthis_btn”).addEventListener(‘click’, function () { Recaptcha.showhelp(); return !1 });

Inline Script within HREF Attributes
reCAPTCHA uses a custom function to dynamically build certain document elements.  The last argument for one of these functions (named c) is assigned to the HREF attribute of the element, which in some cases includes JavaScript.  For these cases, the function call was modified to remove the last argument, and instead bind the argument value programmatically to the onclick event (using addEventListener as in the previous example).  

Before:

c(“recaptcha_reload”, “refresh”, “refresh_btn”, “javascript:Recaptcha.reload();”);

After:

c(“recaptcha_reload”, “refresh”, “refresh_btn”);
document.getElementById(“recaptcha_reload_btn”).addEventListener(‘click’, function () {Recaptcha.reload()});

Use of String-to-Code in Function Calls 
Some JavaScript functions, like eval() for example, allow you to specify a function as input or alternatively let you pass string content that will get treated and executed as code (often referred to as string-to-code).  Passing a string argument to any of these functions (eval, setinterval, etc) requires the unsafe-eval directive, which is definitely something we do not want to allow.  In this case, as shown below, the code is relatively painless to convert since the string value is not dynamic in nature.   This was the simplest change of all.  

Before:

Recaptcha.timer_id = setInterval(‘Recaptcha.reload(“t”);’, a);

After:

Recaptcha.timer_id = setInterval(function(){Recaptcha.reload(“t”)}, a);

By changing those three subtle patterns, we were able to safely run the reCaptcha AJAX API without loosening our CSP.   We welcome anyone in the same boat to leverage our re-factored JS code to run reCAPTCHA with CSP on your own site.  As mentioned, we attempted to contact the reCAPTCHA team at Google during this effort with no success. Hopefully our changes will one day get reflected in the ReCaptcha AJAX API code.



Introducing the SendSafely Enterprise Console

We’re very proud to announce the official release of the SendSafely Enterprise Console.  Enterprise Administrators now have the ability to manage users and view enterprise-wide package activity through a centralized interface within their custom branded SendSafely site. 

The new Enterprise Console can be accessed from the Account menu when logged in as an Enterprise Administrator.  From here, you can add, remove and promote other administrative users, search for activity within your organization, and view package details for users within the enterprise.  Check out the screenshot below for a glimpse inside.


By popular demand, we’ve also made our HTML5 API the default API for Chrome and Firefox users. We continue to receive great feedback from our users, which helps us drive the process of making SendSafely better and better.  Please keep it coming and thanks so much for the support!

If you, or someone you might know, are interested in an Enterprise Account, please reach out to us directly at info@sendsafely.com.  We’d love to hear about your secure file transfer needs and tell you how we can help.  And don’t forget, the free beta for individual users is still going, which includes all the features of the Premium account.  If you haven’t already, give SendSafely a test spin today for free!



Using Content Security Policy to Prevent Cross-Site Scripting (XSS)

On SendSafely.com we make heavy use of many new JavaScript APIs introduced with HTML5. We encrypt files, calculate checksums and upload data using pure JavaScript.  Moving logic like this down to the browser, however, makes the threat of Cross-Site Scripting (XSS) even greater than before.  In order to prevent XSS vulnerabilities, our site makes liberal use of pretty aggressive client-side and server-side encoding APIs.  These APIs are based on the OWASP ESAPI library, so we have context-specific encoding methods for pretty much every scenario.   Even so, we recognize that it is very difficult to rule out all possible ways to inject code, including a human error on our part. For this reason we chose to also implement Content Security Policy (CSP) on SendSafely.com.

CSP is a new security mechanism supported by modern browsers. It aims to prevent XSS by white-listing URLs the browser can load and execute JavaScript from. The server can, by specifying specific CSP directives, prevent the browser from executing things like in-line JavaScript, eval(), setTimeout() or any JavaScript that comes from an untrusted URL. The policy works as a white list, only domains listed are allowed to execute, everything else will be blocked.

The Content Security Policy in SendSafely
In SendSafely.com, our Javascript files are all loaded from a dedicated host that doesn’t run any dynamic content (static.sendsafely.com). The exceptions to this are for certain third-party JavaScript APIs that we still load from an external domain, specifically Google Analytics and reCAPTCHA.  Text-to-JavaScript functions like eval() and setTimeout() are blocked across the board, even if the script is loaded from one of our white-listed hosts, as is any in-line JavaScript

Use of a strict CSP makes it significantly harder to inject executable JavaScript into application pages since the code must come from a trusted server.  The typical XSS attack using un-encoded output on one of our pages won’t work when the CSP is enforced.  In fact, any JavaScript embedded on our content pages (even JavaScript we put there) get blocked by the policy.  Pretty cool stuff.  

So you may be asking yourself, does this mean XSS is nothing but a memory? Sadly, this is not the case.  For starters, CSP is still fairly new and only supported by recent versions of Firefox, Safari and Chrome. Internet Explorer 10 (IE10) supports a subset of CSP options, but the ability to white list domains is unfortunately not one of them.  Aside from limited browser support, data dynamically loaded into the page from JavaScript is still potentially vulnerable. A strict content security policy should therefore not be considered the end-all solution to XSS . Think of CSP more like a safety belt, which is nice to have when your car crashes.

Dissecting our Policy
Now let’s take a look at the CSP policy we use on www.sendsafely.com and dissect it a bit.  One of the first things to note is that if you are going to implement CSP, you must realize that there are some browser compatibility nuances to deal with.   The main thing to note is that Safari uses ‘X-WebKit-CSP’ as the header name for implementing CSP, while other browsers have standardized on the ‘X-Content-Security-Policy’.  Another glitch that affects Safari is that a sever bug in the CSP implementation on Version 5.1 essentially blocks authorized content when a valid CSP is specified.  As a result, you’ll want to specifically detect when Safari is used and send either the ‘X-WebKit-CSP’ header or no header at all (if Version 5.1 is used).

To keep our policy as strict as possible, we use two different policies depending on what the page needs to do.  The stricter policy is used for all pages except the ones that handle encryption and decryption (the reason for this will be discussed in a separate follow up post).  For simplicity, the more strict policy will be explained here.

X-Content-Security-Policy: default-src ‘none’; connect-src ‘self’; script-src https://static.sendsafely.com https://www.google.com https://ssl.google-analytics.com; style-src ‘self’ ‘unsafe-inline’ http: https:; img-src ‘self’ https://www.google.com https://ssl.google-analytics.com; report-uri /csp-reports;

The header is divided into different sections that are each separated by a semi-colon. The “default-src” directive defines the security policy for all types of content which are not expressly called out by more specific directives.  We opted to set the default-src value to ‘none’, meaning that by default we allow nothing to load.  If we stopped defining directives here, the site would be completely broken, so now we need to open up the policy and allow specifically what we want to load.  

Now that we explicitly denied everything as the default, we need to add back the specific content policy options our site needs.  On SendSafely, we have a hand full of resource categories that we need to add policy settings for.  Each of these are outlined below, along with the CSP directives for each.

  • Ajax Requests - Several pages within our site use the browser’s XMLHttpRequest (XHR) object to make HTTP requests from within our JavaScript code.  In order for us to make these request’s we set the “connect-src” attribute to “self”, so scripts on our site can make XHR requests back the server but nowhere else. This attribute is another place where we run into compatibility issues across different browsers.  Specifically, FireFox decided to name this directive “xhr-src” instead of “connect-src”.  To account for this, our CSP code does some basic browser detection and if we detect that FireFox is being used, we change the directive name accordingly.  

  • JavaScript - As mentioned previously, we load all of our internal static JavaScript from a dedicated host (static.sendsafely.com). Additionally, we’ve chosen to load the Google Analytics and ReCaptcha JavaScript files from their origin domains on google.com.  Unfortunately the ability to allow just a sub-path of a host (like /scripts/) is not supported. Since ReCaptcha script files get loaded directly off of the main www.google.com site, our “script-src” directive includes https://static.sendsafely.com, https://www.google.com and https://ssl.google-analytics.com

    Having such a large site like www.google.com in our CSP whitelist is understandably something we are not thrilled about.  The ability to allow sub-paths of a host is slated to be introduced in CSP 1.1, however, but until then we’ll have to live with it.  The good news is that Google takes security very seriously, and they take great care to avoid script injection bugs on their website.
      
  • CSS - Our site design makes heavy use of in-line CSS for styling various UI attributes.  As such, the style-src directive includes a value of “self” (that allows us to load CSS files from the same host) and a value of “unsafe-inline”, meaning that we can use in-line CSS from within our HTML pages.  We recognize that by allowing in-line CSS within our pages, there is a minimal increased security risk since someone could potentially be mischievous if they found a way to inject markup into one of our pages.  Given the cost/benefit of refactoring the UI to completely avoid any in-line CSS, however, we decided this is a tolerable risk that we can live with.  

  • Images - Our img-src directive specifies both “self” and the two previously mentioned google hosts (https://www.google.com and https://ssl.google-analytics.com)  as the authorized origin hosts for all image content  For the most part, our site only loads images from the same host.  The exception to this is reCaptcha, however, since reCaptcha loads various images from www.google.com domain.  

  • The final part of our CSP header is the ‘report-uri’ directive.  This directive tells the browser to send us a report of pages that violate the Content Security Policy. The  violation reports consist of JSON documents sent via an HTTP POST request to the specified URI.  Using this option, we can monitor for events that trigger CSP exceptions and quickly take action if we think there may be a problem with our site.  The reports are also great to use during testing and development in order to debug CSP issues you might encounter.

Final Notes
A few final notes; CSP is a great tool to add an additional layer of protection against Cross-Site Scripting. If you’re building a new application, CSP should be considered as a solid defense in depth security control against the never-ending battle against cross-site scripting. Writing client-side code which is designed to use CSP will save precious developer cycles in the future, if code must be migrated to work with CSP.

Implementing CSP on our site proved to be a very interesting exercize.  We’ll provide more details on some other aspects of our Content Security Policy implementation in a follow up post here on our blog.  



“Hello World”

Hi Everyone! Today marks the official launch of the SendSafely Blog. I’m Brian, one of the co-founders of Gotham Digital Science (also known as GDS Security) and the SendSafely team lead. Our new service, SendSafely, grew out of our need at Gotham Digital Science to securely communicate with our clients. Like many information security teams, we were used to juggling a combination of PGP keys, password-protected zip files and SFTP logins. This was often a cumbersome and laboring task, which detracted from our service and client relationships. We searched for a way to easily send and receive files securely without the requirement of keys or installing software. We wanted a well-designed browser-based application that took both security and user experience seriously. We wanted a service that worked for our clients and us. We couldn’t find one… so we developed the platform that we’ve now released to the public as SendSafely.

If you haven’t tried us out, I encourage you to sign up for free and take us for a test drive. Our goal is to become the standard for secure file transfers. We aren’t a Dropbox replacement, nor do we aim to be. Think of them as the mini-storage unit you rent every month to hold onto all of that extra stuff you own. Instead, think of us as the FedEx or UPS of the digital world. If you want to send something important to someone else, and you want to get it there fast and securely, then come to us.  

All right, enough about our new service, what I really want to talk about is our new blog. We’ve learned a lot while building SendSafely and we want to share our insights with the rest of the security and development community. Our team will be using this blog to post about the challenges we’ve faced while building and running a secure and scalable cloud application platform. Our goal is to not only talk about what we’re doing, but hopefully find out what others are doing to solve the same challenges that we face. In case you didn’t know, our platform uses a lot of new technologies…from Amazon Web Services and Gluster FS on the back-end, to HTML5 and Content-Security Policy on the front-end.

In addition to sharing our learning experiences, we’ll occasionally be posting about new features that we roll out within our platform. But we promise to try and leave our used car salesman suit in the closet when we do (no offense to any used car salesman who may be reading this, we love you!). Stay tuned for more updates, and tell us what you think of SendSafely…we’re all ears! You can reach us at info@sendsafely.com or feel free to post your comments right here on our blog.





Get started in 60 seconds.
Sign Up Today!