ADsafety
Type-based Verification of JavaScript Sandboxing
Joe Gibbs Politz Spiridon Aristides Eliopoulos Arjun Guha Shriram Krishnamurthi
1
ADsafety Type-based Verification of JavaScript Sandboxing Joe - - PowerPoint PPT Presentation
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
Type-based Verification of JavaScript Sandboxing
1
2
3
4
third-party ad third-party ad
5
5
5
the host you visit
6
the host you visit
6
the host you visit the ad server
6
the host you visit the ad server
6
same JavaScript context
the host you visit the ad server
6
<iframe>
the host you visit the ad server
6
<iframe>
top.location.href
Facebook JavaScript (FBJS) Google Caja Yahoo! ADsafe
All are defining safe sub-languages
7
Microsoft Web Sandbox
8
eval
8
eval
8
eval e
8
eval e wrap(e)
8
wrap eval e wrap(e)
8
wrap eval e wrap(e)
8
9
eval
9
eval
ADSAFE.get(obj, x)
9
ADSAFE.get
eval
ADSAFE.get(obj, x)
9
10
10
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 references to DOM nodes;
<div> <p> <div> <b>
ADsafe Untrusted Widget
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;
<div> <p> <div> <b>
ADsafe Untrusted Widget
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
<div id="WIDGET"> <p> <div> <b>
ADsafe Untrusted Widget
<div>
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
<div id="WIDGET"> <p> <div> <b>
ADsafe Untrusted Widget
<div>
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.
ADsafe Widget A Widget B
12
13
ADSAFE.get
eval
ADSAFE.get(obj, x)
Goal: Verify ADsafe
13
ADSAFE.get
eval
ADSAFE.get(obj, x)
Goal: Verify ADsafe untrusted, but passes JSLint
13
ADSAFE.get
eval
ADSAFE.get(obj, x)
Goal: Verify ADsafe untrusted, but passes JSLint Goal: model JSLint
14
JSLint ensures: no DOM references
node “Widgets cannot obtain direct references to DOM nodes.”
<div> <p> <div> <b>
ADsafe Untrusted Widget
14
bunch = { __nodes__ : array of nodes, append: function ..., getText: function ..., ... 20 functions }
ADsafe ensures:
methods on bunches JSLint ensures: no DOM references
node “Widgets cannot obtain direct references to DOM nodes.”
<div> <p> <div> <b>
ADsafe Untrusted Widget
14
bunch = { __nodes__ : array of nodes, append: function ..., getText: function ..., ... 20 functions }
ADsafe ensures:
methods on bunches
bunch.__nodes__ No private fields in JavaScript!
JSLint ensures: no DOM references
node “Widgets cannot obtain direct references to DOM nodes.”
<div> <p> <div> <b>
ADsafe Untrusted Widget
14
bunch = { __nodes__ : array of nodes, append: function ..., getText: function ..., ... 20 functions }
ADsafe ensures:
methods on bunches JSLint ensures:
__nodes__ is
“private”
bunch.__nodes__
JSLint ensures: no DOM references
node “Widgets cannot obtain direct references to DOM nodes.”
<div> <p> <div> <b>
ADsafe Untrusted Widget
14
bunch = { __nodes__ : array of nodes, append: function ..., getText: function ..., ... 20 functions }
ADsafe ensures:
methods on bunches JSLint ensures:
__nodes__ is
“private”
bunch.append(...) bunch.__nodes__
JSLint ensures: no DOM references
node “Widgets cannot obtain direct references to DOM nodes.”
<div> <p> <div> <b>
ADsafe Untrusted Widget
Exploit append to return nodes?
14
bunch = { __nodes__ : array of nodes, append: function ..., getText: function ..., ... 20 functions }
ADsafe ensures:
methods on bunches JSLint ensures:
__nodes__ is
“private”
bunch.append(...)
ADsafe ensures: DOM nodes are not returned
bunch.__nodes__
JSLint ensures: no DOM references
node “Widgets cannot obtain direct references to DOM nodes.”
<div> <p> <div> <b>
ADsafe Untrusted Widget
15
ADSAFE.get
eval
ADSAFE.get(obj, x)
Goal 2: Verify ADsafe untrusted, but passes JSLint Goal 1: model JSLint
var n = 6 var s = "a string" var b = true
16
var n = 6 var s = "a string" var b = true
16
Widget := Number + String + Boolean + Undefined + Null +
17
Widget := Number + String + Boolean + Undefined + Null +
{ x: 6, b: "car" }
17
Widget := Number + String + Boolean + Undefined + Null +
★: Widget __nodes__: Array<Node> caller: prototype: ... code : Widget ⨉ ... → Widget __proto__: Object + Function + Array + ...
{ x: 6, b: "car" } { nested: { y: 10, b: false } }
17
Widget := Number + String + Boolean + Undefined + Null +
★: Widget __nodes__: Array<Node> caller: prototype: ... code : Widget ⨉ ... → Widget __proto__: Object + Function + Array + ...
{ x: 6, b: "car" } { nested: { y: 10, b: false } } { __nodes__: 90 } myObj.prototype = { };
17
Widget := Number + String + Boolean + Undefined + Null +
★: Widget __nodes__: Array<Node> caller: prototype: ... code : Widget ⨉ ... → Widget __proto__: Object + Function + Array + ...
{ 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/
17
Widget := Number + String + Boolean + Undefined + Null +
typable widgets widgets that pass JSLint
18
typable widgets widgets that pass JSLint
evidence: 1,100 LOC of tests
18
typable widgets widgets that pass JSLint
type-based arguments about widgets
evidence: 1,100 LOC of tests
18
19
ADSAFE.get
eval
ADSAFE.get(obj, x)
untrusted, but passes JSLint Goal 1: model JSLint Goal 2: Verify ADsafe
20
window.setTimeout(callback, delay);
eval
window.setTimeout Widget→Widget String
20
window.setTimeout(callback, delay);
eval
window.setTimeout Widget→Widget String
S t r i n g W i d g e t → W i d g e t W i d g e t → W i d g e t Object Number
/*: Widget ⨉ Widget → Widget */ ADSAFE.later = function(callback, delay) { if (typeof callback !== "function") { throw "expected function"; } }
20
window.setTimeout(callback, delay);
window : { eval: ☠, setTimeout : (Widget ⨉ ... → Widget) ⨉ Widget → Undefined, ... }
eval
window.setTimeout Widget→Widget String
S t r i n g W i d g e t → W i d g e t W i d g e t → W i d g e t Object Number
/*: Widget ⨉ Widget → Widget */ ADSAFE.later = function(callback, delay) { if (typeof callback !== "function") { throw "expected function"; } }
20
window.setTimeout(callback, delay);
window : { eval: ☠, setTimeout : (Widget ⨉ ... → Widget) ⨉ Widget → Undefined, ... }
eval
window.setTimeout Widget→Widget String
S t r i n g W i d g e t → W i d g e t W i d g e t → W i d g e t Object Number
/*: Widget ⨉ Widget → Widget */ ADSAFE.later = function(callback, delay) { if (typeof callback !== "function") { throw "expected function"; } }
Widget
20
window.setTimeout(callback, delay);
window : { eval: ☠, setTimeout : (Widget ⨉ ... → Widget) ⨉ Widget → Undefined, ... }
Widget ⨉ ... → Widget
eval
window.setTimeout Widget→Widget String
S t r i n g W i d g e t → W i d g e t W i d g e t → W i d g e t Object Number
/*: Widget ⨉ Widget → Widget */ ADSAFE.later = function(callback, delay) { if (typeof callback !== "function") { throw "expected function"; } }
Widget
20
window.setTimeout(callback, delay);
window : { eval: ☠, setTimeout : (Widget ⨉ ... → Widget) ⨉ Widget → Undefined, ... }
Widget ⨉ ... → Widget
—Politz et al. USENIX Security 2011 and Guha, Saftoiu, Krishnamurthi. ESOP 2011.
eval
window.setTimeout Widget→Widget String
S t r i n g W i d g e t → W i d g e t W i d g e t → W i d g e t Object Number
/*: Widget ⨉ Widget → Widget */ ADSAFE.later = function(callback, delay) { if (typeof callback !== "function") { throw "expected function"; } }
Widget
This is just one kind of if-split we handle.
JSLinted widget adsafe.js
21
JSLinted widget adsafe.js
21
JSLinted widget adsafe.js
21
JSLinted widget
Widget W i d g e t Widget W i d g e t
adsafe.js
21
JSLinted widget
Widget W i d g e t Widget W i d g e t
adsafe.js
21
JSLinted widget
Widget W i d g e t Widget W i d g e t
adsafe.js
Widget Widget Widget Widget
21
JSLinted widget
Widget W i d g e t Widget W i d g e t
adsafe.js
Widget Widget Widget Widget Array<Node> Node
21
JSLinted widget
Widget W i d g e t Widget W i d g e t
adsafe.js
Widget Widget Widget Widget Array<Node> Node N
e Node
21
22
typable widgets widgets that pass JSLint
JSLinted widget
Widget Widget W i d g e t Widget
adsafe.js
Widget Widget Widget W i d g e t Array<Node> N
e
23
typable widgets widgets that pass JSLint
var fakeNode = { tagName: "div", appendChild: function(elt) { var win = elt.ownerDocument.defaultView; win.eval("alert('hacked')"); } };
23
typable widgets widgets that pass JSLint
var fakeNode = { tagName: "div", appendChild: function(elt) { var win = elt.ownerDocument.defaultView; win.eval("alert('hacked')"); } }; var fakeBunch = { __nodes__: [fakeNode] }; Rejected by JSLint
23
typable widgets widgets that pass JSLint
var fakeNode = { tagName: "div", appendChild: function(elt) { var win = elt.ownerDocument.defaultView; win.eval("alert('hacked')"); } }; var fakeBunch = { __nodes__: [fakeNode] }; Rejected by JSLint var fakeBunch = { '__nodes__': [fakeNode] }; Accepted by JSLint
type error: expected Array<HTML>, received Array<Widget>
/*: 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)) { return error(); } ... this.__node__.style[name] = val ... } expected String, received Widget
24
/*: 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 ... } expected String, received Widget var firstCall = true; var badName = { toString: function() { if (firstCall) { firstCall = false; return "font"; } else { return "url('/evil.xml')"; } } };
passes safety check returns bad value
24
/*: 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 ... } expected String, received Widget var firstCall = true; var badName = { toString: function() { if (firstCall) { firstCall = false; return "font"; } else { return "url('/evil.xml')"; } } };
passes safety check returns bad value
Fix: check_string assertion inserted here, and in 16
24
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; 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.
<div> <p> <div> <b>
ADsafe Untrusted Widget
<div id="WIDGET"> <p> <div> <b>
ADsafe Untrusted Widget
<div>
ADsafe Widget A Widget B
25
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; 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.
<div> <p> <div> <b>
ADsafe Untrusted Widget
<div id="WIDGET"> <p> <div> <b>
ADsafe Untrusted Widget
<div>
ADsafe Widget A Widget B
25
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; 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.
<div> <p> <div> <b>
ADsafe Untrusted Widget
<div id="WIDGET"> <p> <div> <b>
ADsafe Untrusted Widget
<div>
ADsafe Widget A Widget B
25
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; 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.
<div> <p> <div> <b>
ADsafe Untrusted Widget
<div id="WIDGET"> <p> <div> <b>
ADsafe Untrusted Widget
<div>
ADsafe Widget A Widget B
25
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; 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.
<div> <p> <div> <b>
ADsafe Untrusted Widget
<div id="WIDGET"> <p> <div> <b>
ADsafe Untrusted Widget
<div>
ADsafe Widget A Widget B
Retracted
25
Caveats:
26
JavaScript program
Proofs for JavaScript?
27
JavaScript program λJS program
desugar
Proofs for JavaScript? Proofs for λJS.
27
JavaScript program λJS program “their answer” “our answer”
desugar
SpiderMonkey, V8, Rhino 100 LOC interpreter
identical for Mozilla JS test suite*
Proofs for JavaScript? Proofs for λJS.
27
banned = { 'arguments' : true, callee : true, caller : true, constructor : true, 'eval' : true, prototype : true, stack : true, unwatch : true, valueOf : true, watch : true }
function reject_global(that) { if (that.window) { error(); } }
if (/url/i.test(string_check(value[i]))) { error('ADsafe error.'); }
28
banned = { 'arguments' : true, callee : true, caller : true, constructor : true, 'eval' : true, prototype : true, stack : true, unwatch : true, valueOf : true, watch : true }
function reject_global(that) { if (that.window) { error(); } }
if (/url/i.test(string_check(value[i]))) { error('ADsafe error.'); }
28
Widget := Number + String + Boolean + Undefined + Null + ★: Widget __nodes__: Array<Node> caller: prototype: ... code : Widget ⨉ ... → Widget __proto__: Object + Function + Array + ...
★: Widget __proto__: Object + Function + Array + ... code : Widget ⨉ ... → Widget arguments: caller: ... Widget := Number + String + Boolean + Undefined + Null +
JavaScript program λJS program “their answer” “our answer”
SpiderMonkey, V8, Rhino 100 LOC interpreterdesugar identical for Mozilla JS test suite*
wrap wrap(e) e eval
u n t y p a b l e typable typable
29
30 Spiridon Aristides Eliopoulos
31 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) === '-')); }
32
33