ADsafety Type-based Verification of JavaScript Sandboxing
Joe Gibbs Politz Spiridon Aristides Eliopoulos Arjun Guha Shriram Krishnamurthi 1 2 3 third-party ad
third-party ad
4 Who is running code in your browser?
5 Who is running code in your browser?
5 Who is running code in your browser?
5 the host you visit
6 the host you visit
6 the host the ad server you visit
6 the host the ad server you visit
same JavaScript context
6 the host the ad server you visit
6 the host the ad server you visit
top.location.href
6 Microsoft Web Sandbox
Google Facebook JavaScript Caja (FBJS)
Yahoo! ADsafe
All are defining safe sub-languages
7 8 eval
8 eval
8 eval e
8 eval e
wrap(e)
8 eval e
wrap wrap(e)
8 “filters”
“wrappers” eval e
“rewriters” wrap wrap(e)
— Maffeis, Mitchell, and Taly, ESORICS 2009
8 9 eval
9 eval
ADSAFE.get(obj, x) untrusted widget
9 eval
ADSAFE.get ADSAFE.get(obj, x) untrusted widget
9 • 1, 800 LOC adsafe.js library • 50 calls to three kinds of assertions • 40 type-tests • 5 regular-expression based checks • 60 privileged DOM method calls
10 ?
• 1, 800 LOC adsafe.js library • 50 calls to three kinds of assertions • 40 type-tests • 5 regular-expression based checks • 60 privileged DOM method calls
10 Type-based Verification of
11 Definition 1 (ADsafety): If all embedded widgets pass JSLint, then:
12 Definition 1 (ADsafety): If all embedded widgets pass JSLint, then: eval() document.write() document.createElement("script") 1.Widgets cannot load new code ... at runtime, or cause ADsafe to load new code on their behalf;
12 Definition 1 (ADsafety): If all embedded widgets pass JSLint, then: eval() document.write() document.createElement("script") 1.Widgets cannot load new code ... at runtime, or cause ADsafe to load new code on their behalf;
12 Definition 1 (ADsafety): If all embedded widgets pass JSLint, then: 1.Widgets cannot load new code at runtime, or cause ADsafe to load new code on their behalf;
2.Widgets cannot obtain direct
Untrusted
12 Definition 1 (ADsafety): If all embedded widgets pass JSLint, then: 1.Widgets cannot load new code at runtime, or cause ADsafe to load new code on their behalf;
2.Widgets cannot obtain direct
Untrusted
12 Definition 1 (ADsafety): If all embedded widgets pass JSLint, then: 1.Widgets cannot load new code at runtime, or cause ADsafe to load new code on their behalf;
2.Widgets cannot obtain direct
3.Widgets cannot affect the
and Untrusted
12 Definition 1 (ADsafety): If all embedded widgets pass JSLint, then: 1.Widgets cannot load new code at runtime, or cause ADsafe to load new code on their behalf;
2.Widgets cannot obtain direct
3.Widgets cannot affect the
and Untrusted
12 Definition 1 (ADsafety): If all embedded widgets pass JSLint, then: 1.Widgets cannot load new code at runtime, or cause ADsafe to load new code on their behalf; 2.Widgets cannot obtain direct references to DOM nodes; 3.Widgets cannot affect the DOM outside of their subtree; and 4.Multiple widgets on the same page cannot communicate.
Widget A ADsafe Widget B
12 eval Goal: Verify ADsafe
ADSAFE.get ADSAFE.get(obj, x)
13 eval Goal: Verify ADsafe
ADSAFE.get ADSAFE.get(obj, x)
untrusted, but passes JSLint
13 eval Goal: Verify ADsafe Goal: model JSLint
ADSAFE.get ADSAFE.get(obj, x)
untrusted, but passes JSLint
13 JSLint ensures: no DOM node references
Untrusted
“Widgets cannot obtain direct references to DOM nodes.”
14 JSLint ensures: no DOM node references
ADsafe ensures: bunch = { only “safe” __nodes__ : array of nodes, methods on append: function ..., bunches getText: function ..., ... 20 functions }
Untrusted
“Widgets cannot obtain direct references to DOM nodes.”
14 JSLint ensures: no DOM node references
ADsafe ensures: bunch = { only “safe” __nodes__ : array of nodes, methods on append: function ..., bunches getText: function ..., ... 20 functions }
“Widgets cannot obtain direct references to DOM nodes.”
14 JSLint ensures: no DOM node references
ADsafe ensures: bunch = { only “safe” __nodes__ : array of nodes, methods on append: function ..., bunches getText: function ..., ... 20 functions }
“Widgets cannot obtain direct references to DOM nodes.”
14 JSLint ensures: no DOM node references
ADsafe ensures: bunch = { only “safe” __nodes__ : array of nodes, methods on append: function ..., bunches getText: function ..., ... 20 functions }
bunch.append(...) “Widgets cannot obtain direct references to DOM nodes.” Exploit append to return nodes?
14 JSLint ensures: no DOM node references
ADsafe ensures: bunch = { only “safe” __nodes__ : array of nodes, methods on append: function ..., bunches getText: function ..., ... 20 functions }
ADsafe ensures: DOM nodes are bunch.append(...) “Widgets cannot obtain direct not returned references to DOM nodes.”
14 eval Goal 2: Verify ADsafe Goal 1: model JSLint
ADSAFE.get ADSAFE.get(obj, x)
untrusted, but passes JSLint
15 var n = 6 var s = "a string" var b = true
16 var n = 6 var s = "a string" var b = true
Widget := Number + String + Boolean + Undefined + Null +
16 Widget := Number + String + Boolean + Undefined + Null +
17 { x: 6, b: "car" }
Widget := Number + String + Boolean + Undefined + Null +
17 { x: 6, b: "car" } { nested: { y: 10, b: false } }
Widget := Number + String + Boolean + Undefined + Null + ★: Widget __nodes__: Array
17 { x: 6, b: "car" } { nested: { y: 10, b: false } } { __nodes__: 90 } myObj.prototype = { };
Widget := Number + String + Boolean + Undefined + Null + ★: Widget __nodes__: Array
17 { x: 6, b: "car" } { nested: { y: 10, b: false } } { __nodes__: 90 } myObj.prototype = { }; function foo(x) { return x + 1; } foo(900) foo.w = "functions are objects" ["array", "of", "strings"] /regular[ \t]*expressions/ Widget := Number + String + Boolean + Undefined + Null + ★: Widget __nodes__: Array
17 JSLint Widget type-checker
typable widgets widgets that pass JSLint
18 JSLint Widget type-checker
typable widgets
widgets that Claim: pass JSLint evidence: 1,100 LOC of tests
or, passing JSLint ⇒Widget-typable
18 JSLint Widget type-checker
type-based typable arguments about widgets widgets
widgets that Claim: pass JSLint evidence: 1,100 LOC of tests
or, passing JSLint ⇒Widget-typable
18 eval Goal 2: Verify ADsafe Goal 1: model JSLint
ADSAFE.get ADSAFE.get(obj, x)
untrusted, but passes JSLint
19 window.setTimeout(callback, delay);
String eval
window.setTimeout
Widget→Widget
20 Object Widget →Widget
String Number
/*: Widget ⨉ Widget → Widget */ ADSAFE.later = function(callback, delay) { if (typeof callback !== "function") { Widget throw "expected function"; →Widget } window.setTimeout(callback, delay); String } eval
window.setTimeout
Widget→Widget
20 Object window : { Widget →Widget eval: ☠, setTimeout : (Widget ⨉ ... → Widget) ⨉ Widget → Undefined, String ... Number }
/*: Widget ⨉ Widget → Widget */ ADSAFE.later = function(callback, delay) { if (typeof callback !== "function") { Widget throw "expected function"; →Widget } window.setTimeout(callback, delay); String } eval
window.setTimeout
Widget→Widget
20 Object window : { Widget →Widget eval: ☠, setTimeout : (Widget ⨉ ... → Widget) ⨉ Widget → Undefined, String ... Number } Widget
/*: Widget ⨉ Widget → Widget */ ADSAFE.later = function(callback, delay) { if (typeof callback !== "function") { Widget throw "expected function"; →Widget } window.setTimeout(callback, delay); String } eval
window.setTimeout
Widget→Widget
20 Object window : { Widget →Widget eval: ☠, setTimeout : (Widget ⨉ ... → Widget) ⨉ Widget → Undefined, String ... Number } Widget
/*: Widget ⨉ Widget → Widget */ ADSAFE.later = function(callback, delay) { if (typeof callback !== "function") { Widget throw "expected function"; →Widget } window.setTimeout(callback, delay); String } eval Widget ⨉ ... → Widget
window.setTimeout
Widget→Widget
20 Object window : { Widget →Widget eval: ☠, setTimeout : (Widget ⨉ ... → Widget) ⨉ Widget → Undefined, String ... Number } Widget
/*: Widget ⨉ Widget → Widget */ ADSAFE.later = function(callback, delay) { if (typeof callback !== "function") { Widget throw "expected function"; →Widget } window.setTimeout(callback, delay); String } eval Widget ⨉ ... → Widget
window.setTimeout This is just one kind of if-split we handle.
Widget→Widget —Politz et al. USENIX Security 2011 and Guha, Saftoiu, Krishnamurthi. ESOP 2011.
20 JSLinted adsafe.js widget
21 JSLinted adsafe.js widget
21 JSLinted adsafe.js widget
21 Widget
Widget
Widget
Widget
JSLinted adsafe.js widget
21 Widget
Widget
Widget
Widget
JSLinted adsafe.js widget
21 Widget
Widget Widget
Widget
Widget
Widget Widget
Widget
JSLinted adsafe.js widget
21 Widget
Widget Widget
Widget
Widget
Widget Widget
Node Array
JSLinted adsafe.js widget
21 Node Widget
Widget Node Widget
Widget
Widget
Widget Widget
Node Array
JSLinted adsafe.js widget
21 Widget
Widget typable Widget widgets Widget
widgets that Widget pass JSLint Widget ⋯ + Widget =
Node Array
JSLinted adsafe.js widget
JSLint model Type-checked ADsafe
22 typable widgets var fakeNode = { widgets that tagName: "div", pass JSLint appendChild: function(elt) { var win = elt.ownerDocument.defaultView; win.eval("alert('hacked')"); } };
23 typable widgets var fakeNode = { widgets that tagName: "div", pass JSLint appendChild: function(elt) { var win = elt.ownerDocument.defaultView; win.eval("alert('hacked')"); } }; var fakeBunch = { __nodes__: [fakeNode] };
Rejected by JSLint
23 typable widgets var fakeNode = { widgets that tagName: "div", pass JSLint appendChild: function(elt) { var win = elt.ownerDocument.defaultView; win.eval("alert('hacked')"); } }; var fakeBunch = { __nodes__: [fakeNode] }; var fakeBunch = { '__nodes__': [fakeNode] }; Rejected by JSLint
Accepted by JSLint
type error: expected Array, received Array
23 /*: Widget ⨉ Widget → Widget */ WrappedElt.prototype.style = function(name, val) { var regexp = new Regexp("url"); if (regexp.test(val)) { return error(); } ... this.__node__.style[name] = val ... }
24 /*: Widget ⨉ Widget → Widget */ WrappedElt.prototype.style = function(name, val) { var regexp = new Regexp("url"); if (regexp.test(val)) { expected return error(); String, received } Widget ... this.__node__.style[name] = val ... }
24 /*: Widget ⨉ Widget → Widget */ WrappedElt.prototype.style = function(name, val) { var regexp = new Regexp("url"); if (regexp.test(val)) { expected return error(); String, received } Widget ... this.__node__.style[name] = val ... }
var firstCall = true; var badName = { toString: function() { passes safety check if (firstCall) { firstCall = false; return "font"; } else { return "url('/evil.xml')"; } returns bad value } };
24 /*: Widget ⨉ Widget → Widget */ WrappedElt.prototype.style = function(name, val) { var regexp = new Regexp("url"); Fix: if (regexp.test(val)) { check_string expected return error(); assertion inserted String, received here, and in 16 } Widget other places ... this.__node__.style[name] = val ... }
var firstCall = true; var badName = { toString: function() { passes safety check if (firstCall) { firstCall = false; return "font"; } else { return "url('/evil.xml')"; } returns bad value } };
24 Definition 1 (ADsafety): If all eval() document.write() document.createElement("script") embedded widgets pass JSLint, then: ... 1.Widgets cannot load new code at runtime, or cause ADsafe to load new code on their behalf;
Untrusted
references to DOM nodes;
Untrusted
4.Multiple widgets on the same page cannot communicate. Widget A ADsafe Widget B
25 Definition 1 (ADsafety): If all eval() document.write() document.createElement("script") embedded widgets pass JSLint, then: ... 1.Widgets cannot load new code at runtime, or cause ADsafe to load new code on their behalf;
Untrusted
references to DOM nodes;
Untrusted
4.Multiple widgets on the same page cannot communicate. Widget A ADsafe Widget B
25 Definition 1 (ADsafety): If all eval() document.write() document.createElement("script") embedded widgets pass JSLint, then: ... 1.Widgets cannot load new code at runtime, or cause ADsafe to load new code on their behalf;
Untrusted
references to DOM nodes;
Untrusted
4.Multiple widgets on the same page cannot communicate. Widget A ADsafe Widget B
25 Definition 1 (ADsafety): If all eval() document.write() document.createElement("script") embedded widgets pass JSLint, then: ... 1.Widgets cannot load new code at runtime, or cause ADsafe to load new code on their behalf;
Untrusted
references to DOM nodes;
Untrusted
4.Multiple widgets on the same page cannot communicate. Widget A ADsafe Widget B
25 Definition 1 (ADsafety): If all eval() document.write() document.createElement("script") embedded widgets pass JSLint, then: ... 1.Widgets cannot load new code at runtime, or cause ADsafe to load new code on their behalf;
Untrusted
references to DOM nodes;
Untrusted
4.Multiple widgets on the same Retractedpage cannot communicate. Widget A ADsafe Widget B
25 ! Caveats: • 11 LOC unverified • subtree property unverified
26 JavaScript program
Proofs for JavaScript?
27 desugar JavaScript λJS program program
Proofs for Proofs for λJS. JavaScript?
27 desugar JavaScript λJS program program
Proofs for λ SpiderMonkey, 100 LOC Proofs for JS. JavaScript? V8, Rhino interpreter
“their “our answer” identical for answer” Mozilla JS test suite*
— Guha, Saftoiu, Krishnamurthi. ECOOP 2010.
27 banned = { 'arguments' : true, callee : true, caller : true, constructor : true, function reject_global(that) { if (that.window) { if (/url/i.test(string_check(value[i]))) { 'eval' : true, error(); error('ADsafe error.'); prototype : true, } } stack : true, + } + unwatch : true, valueOf : true, watch : true } and other patterns...
28 banned = { 'arguments' : true, callee : true, caller : true, constructor : true, function reject_global(that) { if (that.window) { if (/url/i.test(string_check(value[i]))) { 'eval' : true, error(); error('ADsafe error.'); prototype : true, } } stack : true, + } + unwatch : true, valueOf : true, watch : true } and other patterns...
... can be succinctly expressed with types
Widget := Number + String + Boolean + Undefined + Null + ★: Widget __nodes__: Array
28 Conclusion
untypable eval 1.Model sandbox as a type e system typable wrap wrap(e) typable
Widget := Number + String + Boolean + Undefined + Null + ★: Widget 2.Object types for __proto__: Object + Function + Array + ... code : Widget ⨉ ... → Widget arguments: ★ caller: JavaScript ( and ☠) ...
desugar JavaScript λJS program program
SpiderMonkey, 100 LOC 3.Proofs over tractable V8, Rhino interpreter
JavaScript semantics “their “our answer” identical for answer” Mozilla JS test suite*
29 Spiridon Aristides Eliopoulos
Extra Slides
30 Unverified Code
function F() {};
ADSAFE.create = typeof Object.create === 'function' ? Object.create : function (o) { F.prototype = typeof o === 'object' && o ? o : Object.prototype; return new F(); };
/*: (banned → True) & (not_banned → False) */ function reject_name(name) { return banned[name] || ((typeof name !== 'number' || name < 0) && (typeof name !== 'string' || name.charAt(0) === '_' || name.slice(-1) === '_' || name.charAt(0) === '-')); }
31 Theorems
32 Full Widget Type
33