ADsafety - USENIX

Preview:

Citation preview

ADsafetyType-based Verification of JavaScript Sandboxing

Joe Gibbs PolitzSpiridon Aristides Eliopoulos

Arjun GuhaShriram Krishnamurthi

1

2

3

4

third-party ad

third-party ad

5

Who is running code in your

browser?

5

Who is running code in your

browser?

5

Who is running code in your

browser?

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)

GoogleCaja

Yahoo!ADsafe

All are defining safe sub-languages

7

Microsoft Web Sandbox

8

eval

8

eval

8

evale

8

evale

wrap(e)

8

wrap

evale

wrap(e)

8

wrap

evale

wrap(e)

“filters”

“wrappers”

— Maffeis, Mitchell, and Taly, ESORICS 2009

“rew

riter

s”8

9

eval

9

eval

ADSAFE.get(obj, x)

9

untrusted widget

ADSAFE.get

eval

ADSAFE.get(obj, x)

9

untrusted widget

• 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 references to DOM nodes;

<div>

<p> <div>

<b>

ADsafeUntrusted

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>

ADsafeUntrusted

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>

ADsafeUntrusted

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>

ADsafeUntrusted

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.

ADsafeWidget 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>

ADsafeUntrusted

Widget

14

bunch = { __nodes__ : array of nodes, append: function ..., getText: function ..., ... 20 functions}

ADsafe ensures: only “safe”

methods on bunches

JSLint ensures: no DOM references

node

“Widgets cannot obtain direct references to DOM nodes.”

<div>

<p> <div>

<b>

ADsafeUntrusted

Widget

14

bunch = { __nodes__ : array of nodes, append: function ..., getText: function ..., ... 20 functions}

ADsafe ensures: only “safe”

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>

ADsafeUntrusted

Widget

14

bunch = { __nodes__ : array of nodes, append: function ..., getText: function ..., ... 20 functions}

ADsafe ensures: only “safe”

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>

ADsafeUntrusted

Widget

14

bunch = { __nodes__ : array of nodes, append: function ..., getText: function ..., ... 20 functions}

ADsafe ensures: only “safe”

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>

ADsafeUntrusted

Widget

Exploit append to return nodes?

14

bunch = { __nodes__ : array of nodes, append: function ..., getText: function ..., ... 20 functions}

ADsafe ensures: only “safe”

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>

ADsafeUntrusted

Widget

15

ADSAFE.get

eval

ADSAFE.get(obj, x)

Goal 2: Verify ADsafe

untrusted, but passes JSLint

Goal 1: model JSLint

var n = 6var s = "a string"var b = true

16

var n = 6var 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 +

JSLint Widget type-checker

typable widgets

widgets that pass JSLint

18

JSLint Widget type-checker

typable widgets

widgets that pass JSLint

or, passing JSLint ⇒Widget-typable

Claim:evidence:1,100 LOC of tests

18

JSLint Widget type-checker

typable widgets

widgets that pass JSLint

or, passing JSLint ⇒Widget-typable

type-based arguments about

widgets

Claim: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

String

Widget→Widget

Widget→Widget

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

String

Widget→Widget

Widget→Widget

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

String

Widget→Widget

Widget→Widget

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 ⨉ ... → Widgeteval

window.setTimeout

Widget→Widget

String

String

Widget→Widget

Widget→Widget

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

String

Widget→Widget

Widget→Widget

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 widgetadsafe.js

21

JSLinted widgetadsafe.js

21

JSLinted widgetadsafe.js

21

JSLinted widget

WidgetW

idget

Wid

get

Wid

get

adsafe.js21

JSLinted widget

WidgetW

idget

Wid

get

Wid

get

adsafe.js21

JSLinted widget

WidgetW

idget

Wid

get

Wid

get

adsafe.js

Widget

Widget

Widget

Widget

21

JSLinted widget

WidgetW

idget

Wid

get

Wid

get

adsafe.js

Widget

Widget

Widget

Widget

Arr

ay<N

ode>

Nod

e

21

JSLinted widget

WidgetW

idget

Wid

get

Wid

get

adsafe.js

Widget

Widget

Widget

Widget

Arr

ay<N

ode>

Nod

e

Node

Node

21

22

typable widgets

widgets that pass JSLint

JSLinted widget

WidgetW

idget

Wid

get

Wid

get

adsafe.js

Widget

Widget

Widget

Widget

Arr

ay<N

ode>

Nod

e

JSLint model Type-checked ADsafe

+ = ⋯

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 other places

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>

ADsafeUntrusted

Widget

<div id="WIDGET">

<p> <div>

<b>

ADsafeUntrusted

Widget

<div>

ADsafeWidget 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>

ADsafeUntrusted

Widget

<div id="WIDGET">

<p> <div>

<b>

ADsafeUntrusted

Widget

<div>

ADsafeWidget 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>

ADsafeUntrusted

Widget

<div id="WIDGET">

<p> <div>

<b>

ADsafeUntrusted

Widget

<div>

ADsafeWidget 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>

ADsafeUntrusted

Widget

<div id="WIDGET">

<p> <div>

<b>

ADsafeUntrusted

Widget

<div>

ADsafeWidget 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>

ADsafeUntrusted

Widget

<div id="WIDGET">

<p> <div>

<b>

ADsafeUntrusted

Widget

<div>

ADsafeWidget A Widget BRetracte

d

25

Caveats:

• 11 LOC unverified

• subtree property unverified

26

!

JavaScriptprogram

Proofs for JavaScript?

27

JavaScriptprogram

λJS

programdesugar

Proofs for JavaScript?

Proofs for λJS.

27

JavaScriptprogram

λJS

program

“theiranswer”

“ouranswer”

desugar

SpiderMonkey, V8, Rhino

100 LOC interpreter

identical forMozilla JS test suite*

Proofs for JavaScript?

Proofs for λJS.

— Guha, Saftoiu, Krishnamurthi. ECOOP 2010.

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.');}+

and other patterns...

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.');}+

and other patterns...

28

... can be succinctly expressed with typesWidget := 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 ⨉ ... → Widgetarguments: �caller: �...

Widget := Number + String + Boolean + Undefined + Null +

JavaScriptprogram

λJS

program

“theiranswer”

“ouranswer”

SpiderMonkey, V8, Rhino

100 LOC interpreter

desugar

identical forMozilla JS test suite*

wrap wrap(e)

eeval

untypable

typable

typable

1.Model sandbox as a type system

2.Object types for JavaScript (★ and ☠)

3.Proofs over tractable JavaScript semantics

Conclusion

29

Extra Slides30

Spiridon Aristides Eliopoulos

Unverified Code

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) === '-'));}

Theorems

32

Full Widget Type

33