adsafety - usenix

95
ADsafety Type-based Verification of JavaScript Sandboxing Joe Gibbs Politz Spiridon Aristides Eliopoulos Arjun Guha Shriram Krishnamurthi 1

Upload: others

Post on 08-Apr-2022

15 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: ADsafety - USENIX

ADsafetyType-based Verification of JavaScript Sandboxing

Joe Gibbs PolitzSpiridon Aristides Eliopoulos

Arjun GuhaShriram Krishnamurthi

1

Page 2: ADsafety - USENIX

2

Page 3: ADsafety - USENIX

3

Page 4: ADsafety - USENIX

4

third-party ad

third-party ad

Page 5: ADsafety - USENIX

5

Who is running code in your

browser?

Page 6: ADsafety - USENIX

5

Who is running code in your

browser?

Page 7: ADsafety - USENIX

5

Who is running code in your

browser?

Page 8: ADsafety - USENIX

the host you visit

6

Page 9: ADsafety - USENIX

the host you visit

6

Page 10: ADsafety - USENIX

the host you visit the ad server

6

Page 11: ADsafety - USENIX

the host you visit the ad server

6

same JavaScript context

Page 12: ADsafety - USENIX

the host you visit the ad server

6

<iframe>

Page 13: ADsafety - USENIX

the host you visit the ad server

6

<iframe>

top.location.href

Page 14: ADsafety - USENIX

Facebook JavaScript (FBJS)

GoogleCaja

Yahoo!ADsafe

All are defining safe sub-languages

7

Microsoft Web Sandbox

Page 15: ADsafety - USENIX

8

Page 16: ADsafety - USENIX

eval

8

Page 17: ADsafety - USENIX

eval

8

Page 18: ADsafety - USENIX

evale

8

Page 19: ADsafety - USENIX

evale

wrap(e)

8

Page 20: ADsafety - USENIX

wrap

evale

wrap(e)

8

Page 21: ADsafety - USENIX

wrap

evale

wrap(e)

“filters”

“wrappers”

— Maffeis, Mitchell, and Taly, ESORICS 2009

“rew

riter

s”8

Page 22: ADsafety - USENIX

9

Page 23: ADsafety - USENIX

eval

9

Page 24: ADsafety - USENIX

eval

ADSAFE.get(obj, x)

9

untrusted widget

Page 25: ADsafety - USENIX

ADSAFE.get

eval

ADSAFE.get(obj, x)

9

untrusted widget

Page 26: ADsafety - USENIX

• 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

Page 27: ADsafety - USENIX

• 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

?

Page 28: ADsafety - USENIX

Type-based Verification of

11

Page 29: ADsafety - USENIX

Definition 1 (ADsafety): If all embedded widgets pass JSLint, then:

12

Page 30: ADsafety - USENIX

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

Page 31: ADsafety - USENIX

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

Page 32: ADsafety - USENIX

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

Page 33: ADsafety - USENIX

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

Page 34: ADsafety - USENIX

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

Page 35: ADsafety - USENIX

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

Page 36: ADsafety - USENIX

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

Page 37: ADsafety - USENIX

13

ADSAFE.get

eval

ADSAFE.get(obj, x)

Goal: Verify ADsafe

Page 38: ADsafety - USENIX

13

ADSAFE.get

eval

ADSAFE.get(obj, x)

Goal: Verify ADsafe

untrusted, but passes JSLint

Page 39: ADsafety - USENIX

13

ADSAFE.get

eval

ADSAFE.get(obj, x)

Goal: Verify ADsafe

untrusted, but passes JSLint

Goal: model JSLint

Page 40: ADsafety - USENIX

14

JSLint ensures: no DOM references

node

“Widgets cannot obtain direct references to DOM nodes.”

<div>

<p> <div>

<b>

ADsafeUntrusted

Widget

Page 41: ADsafety - USENIX

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

Page 42: ADsafety - USENIX

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

Page 43: ADsafety - USENIX

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

Page 44: ADsafety - USENIX

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?

Page 45: ADsafety - USENIX

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

Page 46: ADsafety - USENIX

15

ADSAFE.get

eval

ADSAFE.get(obj, x)

Goal 2: Verify ADsafe

untrusted, but passes JSLint

Goal 1: model JSLint

Page 47: ADsafety - USENIX

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

16

Page 48: ADsafety - USENIX

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

16

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

Page 49: ADsafety - USENIX

17

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

Page 50: ADsafety - USENIX

{ x: 6, b: "car" }

17

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

Page 51: ADsafety - USENIX

★: 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 +

Page 52: ADsafety - USENIX

★: 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 +

Page 53: ADsafety - USENIX

★: 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 +

Page 54: ADsafety - USENIX

JSLint Widget type-checker

typable widgets

widgets that pass JSLint

18

Page 55: ADsafety - USENIX

JSLint Widget type-checker

typable widgets

widgets that pass JSLint

or, passing JSLint ⇒Widget-typable

Claim:evidence:1,100 LOC of tests

18

Page 56: ADsafety - USENIX

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

Page 57: ADsafety - USENIX

19

ADSAFE.get

eval

ADSAFE.get(obj, x)

untrusted, but passes JSLint

Goal 1: model JSLint

Goal 2: Verify ADsafe

Page 58: ADsafety - USENIX

20

window.setTimeout(callback, delay);

eval

window.setTimeout

Widget→Widget

String

Page 59: ADsafety - USENIX

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"; }

}

Page 60: ADsafety - USENIX

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"; }

}

Page 61: ADsafety - USENIX

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

Page 62: ADsafety - USENIX

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

Page 63: ADsafety - USENIX

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.

Page 64: ADsafety - USENIX

JSLinted widgetadsafe.js

21

Page 65: ADsafety - USENIX

JSLinted widgetadsafe.js

21

Page 66: ADsafety - USENIX

JSLinted widgetadsafe.js

21

Page 67: ADsafety - USENIX

JSLinted widget

WidgetW

idget

Wid

get

Wid

get

adsafe.js21

Page 68: ADsafety - USENIX

JSLinted widget

WidgetW

idget

Wid

get

Wid

get

adsafe.js21

Page 69: ADsafety - USENIX

JSLinted widget

WidgetW

idget

Wid

get

Wid

get

adsafe.js

Widget

Widget

Widget

Widget

21

Page 70: ADsafety - USENIX

JSLinted widget

WidgetW

idget

Wid

get

Wid

get

adsafe.js

Widget

Widget

Widget

Widget

Arr

ay<N

ode>

Nod

e

21

Page 71: ADsafety - USENIX

JSLinted widget

WidgetW

idget

Wid

get

Wid

get

adsafe.js

Widget

Widget

Widget

Widget

Arr

ay<N

ode>

Nod

e

Node

Node

21

Page 72: ADsafety - USENIX

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

+ = ⋯

Page 73: ADsafety - USENIX

23

typable widgets

widgets that pass JSLint

var fakeNode = { tagName: "div", appendChild: function(elt) { var win = elt.ownerDocument.defaultView; win.eval("alert('hacked')"); }};

Page 74: ADsafety - USENIX

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

Page 75: ADsafety - USENIX

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>

Page 76: ADsafety - USENIX

/*: 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

Page 77: ADsafety - USENIX

/*: 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

Page 78: ADsafety - USENIX

/*: 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

Page 79: ADsafety - USENIX

/*: 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

Page 80: ADsafety - USENIX

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

Page 81: ADsafety - USENIX

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

Page 82: ADsafety - USENIX

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

Page 83: ADsafety - USENIX

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

Page 84: ADsafety - USENIX

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

Page 85: ADsafety - USENIX

Caveats:

• 11 LOC unverified

• subtree property unverified

26

!

Page 86: ADsafety - USENIX

JavaScriptprogram

Proofs for JavaScript?

27

Page 87: ADsafety - USENIX

JavaScriptprogram

λJS

programdesugar

Proofs for JavaScript?

Proofs for λJS.

27

Page 88: ADsafety - USENIX

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

Page 89: ADsafety - USENIX

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

Page 90: ADsafety - USENIX

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 + ...

Page 91: ADsafety - USENIX

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

Page 92: ADsafety - USENIX

Extra Slides30

Spiridon Aristides Eliopoulos

Page 93: ADsafety - USENIX

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

Page 94: ADsafety - USENIX

Theorems

32

Page 95: ADsafety - USENIX

Full Widget Type

33