Remaining Hazards and Mitigating Patterns for Secure Mashups in - - PowerPoint PPT Presentation

remaining hazards and mitigating patterns for secure
SMART_READER_LITE
LIVE PREVIEW

Remaining Hazards and Mitigating Patterns for Secure Mashups in - - PowerPoint PPT Presentation

Remaining Hazards and Mitigating Patterns for Secure Mashups in EcmaScript 5 Mark S. Miller and the Cajadores Overview The Mashup Problem The Offensive and Defensive Code Problems JavaScript (EcmaScript) gets simpler ES3, ES5, ES5/strict,


slide-1
SLIDE 1

Remaining Hazards and Mitigating Patterns for Secure Mashups in EcmaScript 5

Mark S. Miller and the Cajadores

slide-2
SLIDE 2

Overview

The Mashup Problem

The Offensive and Defensive Code Problems

JavaScript (EcmaScript) gets simpler

ES3, ES5, ES5/strict, SES-on-ES5

Secure EcmaScript (SES) defenses

Confinement and Tamper Proofing

Remaining SES Security Hazards

Riddles: Attack these example

Mitigating Patterns for Attack Resistant Code

slide-3
SLIDE 3

New Skills open up New Worlds

Remember learning

“Avoid goto” “Beware pointer arithmetic” “Beware threads and locks” “Zero index origin likes closed-open intervals” “Manual encoding is better than string append” “Auto-escaping is better than manual encoding” and various oo patterns and their hazards?

Co-evolution of skills and tools

Student drivers think hard to avoid accidents. Experts avoid traps, but think about destination. Cars learn to help.

slide-4
SLIDE 4

Mashups are Everywhere

… <script src=“https://evil.com/matrix.js”> <script> var prod = matMult(matrixA, matrixB); </script>

Why can matMult hijack my account?

slide-5
SLIDE 5

A Trivial Mashup Scenario

Alice says: var bobSrc = //site B var carolSrc = //site C var bob = eval(bobSrc); var carol = eval(carolSrc); bob carol Alice Bob Carol

slide-6
SLIDE 6

A Trivial Mashup Scenario

Alice says: Alice bob carol Bob Carol

slide-7
SLIDE 7

Bob Carol bob carol counter

A Trivial Mashup Scenario

Alice says: var counter = makeCounter(); bob(counter); carol(counter.decr);

count count count incr incr decr decr

slide-8
SLIDE 8

Bob Carol bob carol counter

A Trivial Mashup Scenario

Alice says: var counter = makeCounter(); bob(counter); carol(counter.decr);

count count count incr incr decr decr

Bob can count up and down and see result. Carol can count down and see the result.

slide-9
SLIDE 9

Bob Carol bob carol counter

A Trivial Mashup Scenario

Alice says: var counter = makeCounter(); bob(counter); carol(counter.decr);

count count count incr incr decr decr

Principle of least authority: Bob can only count up and down and see result. Carol can only count down and see the result.

slide-10
SLIDE 10

Bob Carol bob carol counter

A Trivial Mashup Attack Scenario

Alice says: var counter = makeCounter(); bob(counter); carol(counter.decr); Bob says: counter.decr = evil;

count count count incr incr evil evil

When Alice or Carol try to count down, they call Bob’s evil function instead.

slide-11
SLIDE 11

Bob Carol bob carol counter

A Trivial Mashup Attack Scenario

Alice says: var counter = makeCounter(); bob(counter); carol(counter.decr); Bob says: counter.decr = evil; …window…document.cookie…

count count count incr incr evil evil

Bob can do much worse damage!

slide-12
SLIDE 12

The Mashup Problem

“A Mashup is a Self Inflicted Cross-Site Script” —Douglas Crockford The Offensive Code Problem

Solved by SES

The Defensive Code Problem

Mitigated by patterns made possible by SES Still Hard! A puzzle solving skill to learn.

slide-13
SLIDE 13

The Offensive Code Problem

Abuse of Global Authority

Phishing, Redirection, Cookies

Prototype Poisoning

Object.prototype.toString = evilFunc;

Global Scope Poisoning

JSON = {parse: eval};

slide-14
SLIDE 14

Turning EcmaScript 5 into SES

<script src=“initSES.js”></script> Monkey patch away bad non-std behaviors Remove non-whitelisted primordials Install leaky WeakMap emulation Make virtual global root Freeze whitelisted global variables

  • Replace eval & Function with safe alternatives

Freeze accessible primordials

slide-15
SLIDE 15

SES eval  Confinement

Alice says: var bobSrc = //site B var carolSrc = //site C var bob = eval(bobSrc); var carol = eval(carolSrc); Alice bob carol Bob Carol Bob cannot yet cause any effects outside himself!

slide-16
SLIDE 16

Bob Carol bob carol counter

Need Bullet-proof Defensive Objects

Alice says: var counter = makeCounter(); bob(counter); carol(counter.decr); Bob says: counter.decr = evil; …window…document.cookie…

count count count incr incr evil evil

Bob can still subvert a non-defensive counter

slide-17
SLIDE 17

The Defensive Code Problem

Violating Encapsulation Tampering with API surface Violating Assumptions  Loss of Integrity Contagious Corruption

slide-18
SLIDE 18

Classic JS Prototypal Objects

function Counter() { this.count = 0; } Counter.prototype = { incr: function() { return ++this.count; }, decr: function() { return --this.count; }, constructor: Counter }; var c = new Counter();

Object Object.prototype Counter Counter.prototype incr decr

c count: 0 inherits from (__proto__) prototype constructor prototype constructor inherits from (__proto__) c count: 0

c count: 0

slide-19
SLIDE 19

Classic JS Prototypal Objects

function Counter() { this.count = 0; } Counter.prototype = { incr: function() { return ++this.count; }, decr: function() { return --this.count; }, constructor: Counter }; var c = new Counter();

Counter.prototype incr decr

c count: 0 inherits from (__proto__) c count: 0

c count: 0

slide-20
SLIDE 20

Classic JS Prototypal Objects

function Counter() { this.count = 0; } Counter.prototype = { incr: function() { return ++this.count; }, decr: function() { return --this.count; }, constructor: Counter }; var c = new Counter();

c.incr();

Counter.prototype incr decr

c count: 0 inherits from (__proto__) c count: 0

c count: 0

slide-21
SLIDE 21

Classic JS Prototypal Objects

function Counter() { this.count = 0; } Counter.prototype = { incr: function() { return ++this.count; }, decr: function() { return --this.count; }, constructor: Counter }; var c = new Counter();

{

method

c.incr();

{ {

implicit “this” arg arguments

Counter.prototype incr decr

c count: 0 inherits from (__proto__) c count: 0

c count: 0

slide-22
SLIDE 22

Function Call, Method Call, Reflection

c.incr() Method call “this” is c c[‘incr’]() Method call “this” is c (c.incr)() Method call “this” is c (c[‘incr’])() Method call “this” is c var incr = c.incr; incr() Function call “this” is undefined (1,c.incr)() Function call “this” is undefined d.incr = c.incr; d.incr() Method call “this” is d c.incr.apply(d, []) Reflective call “this” is d applyFn(c.incr, d, []) Reflective call “this” is d

{

method

c.incr();

{ {

implicit “this” arg arguments

slide-23
SLIDE 23

Reflection Helper

var applyFn = Function.prototype.call.bind(Function.prototype.apply);

  • bj.name(…args)  applyFn(obj.name, obj, args)

func(…args)  applyFn(func, undefined, args)

slide-24
SLIDE 24

Classic JS Prototypal Objects

function Counter() { this.count = 0; } Counter.prototype = { incr: function() { return ++this.count; }, decr: function() { return --this.count; }, constructor: Counter }; var c = new Counter(); // Confusion attacks: applyFn(c.incr, nonCounter, []); // Corruption attacks: c.count = -Infinity;

Counter.prototype incr decr

c count: 0 inherits from (__proto__) c count: 0

c count: 0

slide-25
SLIDE 25

First Lesson

Classic JS Prototype Pattern is hazardous “this” is hazardous

slide-26
SLIDE 26

function makeCounter() { var count = 0; return { incr: function() { return ++count; }, decr: function() { return --count; } }; }

makeCounter count incr incr decr decr count incr incr decr decr count incr incr decr decr

Objects as Closures in JavaScript

slide-27
SLIDE 27

Objects as Closures in JavaScript

function makeCounter() { var count = 0; return { incr: function() { return ++count; }, decr: function() { return --count; } }; }

makeCounter count incr incr decr decr count incr incr decr decr count incr incr decr decr

A record of closures hiding state is a fine representation of an

  • bject of methods hiding instance vars
slide-28
SLIDE 28

Robustness impossible in ES3

Mandatory mutability (monkey patching) Not statically scoped – repaired by ES5

(function n() {…x…}) // named function exprs try{throw fn;}catch(f){f();…x…} // thrown function Object = Date; …{}… // “as if by”

Not statically scoped – repaired by ES5/strict

with (o) {…x…} // attractive but botched delete x; // dynamic deletion eval(str); …x… // eval exports binding

slide-29
SLIDE 29

function makeCounter() { var count = 0; return { incr: function() { return ++count; }, decr: function() { return --count; } }; }

  • Mandatory mutability
  • Scoping confusions
  • Encapsulation leaks

makeCounter count incr incr decr decr count incr incr decr decr count incr incr decr decr

Objects as Closures in EcmaScript 3

slide-30
SLIDE 30

Using ES5 to Stop Bob’s 1st Attack

function makeCounter() { var count = 0; return Object.freeze({ incr: function() { return ++count; }, decr: function() { return --count; } }); }

  • Unexpressed mutability
  • Scoping confusions
  • Encapsulation leaks

makeCounter count incr incr decr decr count incr incr decr decr count incr incr decr decr

slide-31
SLIDE 31

Encapsulation Leaks in non-strict ES5

function doSomething(ifBobKnows, passwd) { if (ifBobKnows() === passwd) { //… do something with passwd } }

slide-32
SLIDE 32

Encapsulation Leaks in non-strict ES5

function doSomething(ifBobKnows, passwd) { if (ifBobKnows() === passwd) { //… do something with passwd } } Bob says: var stash; function ifBobKnows() { stash = arguments.caller.arguments[1]; return arguments.caller.arguments[1] = badPasswd; }

slide-33
SLIDE 33

Encapsulation in ES5/strict

“use strict”; function doSomething(ifBobKnows, passwd) { if (ifBobKnows() === passwd) { //… do something with passwd } } Bob’s attack fails: return arguments.caller.arguments[1] = badPasswd; Parameters not joined to arguments.

slide-34
SLIDE 34

Encapsulation in ES5/strict

“use strict”; function doSomething(ifBobKnows, passwd) { if (ifBobKnows() === passwd) { //… do something with passwd } } Bob’s attack fails: return arguments.caller.arguments[1] = badPasswd; Poison pills.

slide-35
SLIDE 35

Encapsulation in ES5/strict

“use strict”; function doSomething(ifBobKnows, passwd) { if (ifBobKnows() === passwd) { //… do something with passwd } } Bob’s attack fails: return arguments.caller.arguments[1] = badPasswd; Even non-strict “.caller” can’t reveal a strict caller.

slide-36
SLIDE 36

Defensive Objects in SES on ES5

“use strict”; function makeCounter() { var count = 0; return def({ incr: function() { return ++count; }, decr: function() { return --count; } }); }

makeCounter count incr incr decr decr count incr incr decr decr count incr incr decr decr

A tamper-proof record of lexical closures encapsulating state is a defensible object

slide-37
SLIDE 37

Bob Carol bob carol counter

Goal Achieved!

Alice says: var counter = makeCounter(); bob(counter); carol(counter.decr);

count count count incr incr decr decr

Principle of least authority: Bob can only count up and down and see result. Carol can only count down and see the result.

slide-38
SLIDE 38

Lessons

“this” rebinding

Avoid “this” and Prototypes Use objects-as-closures or traits.js (when security is worth extra allocations)

Mutability Leakage

def traits.js

slide-39
SLIDE 39

Encapsulation Riddle

function Table() { var array = []; return def({ add: function(v) { array.push(v); }, store: function(i, v) { array[i] = v; }, get: function(i) { return array[i]; } }); }

Riddle: Steal array from table

slide-40
SLIDE 40

Encapsulation Riddle

function Table() { var array = []; return def({ add: function(v) { array.push(v); }, store: function(i, v) { array[i] = v; }, get: function(i) { return array[i]; } }); }

Attack 1:

var stash; table.store(‘push’, function(v) { stash = this; }); table.add(“doesn’t matter”);

slide-41
SLIDE 41

Encapsulation Riddle

function Table() { var array = []; return def({ add: function(v) { array.push(v); }, store: function(i, v) { array[i] = v; }, get: function(i) { return array[i]; } }); }

Attack 2 by Jorge Chamorro on es-discuss:

var stash; table.store(‘__proto__’, { push: function(v) { stash = this; } }); table.add(“doesn’t matter”);

slide-42
SLIDE 42

Encapsulation Riddle, solved

function Table() { var array = []; return def({ add: function(v) { array.push(v); }, store: function(i, v) { array[+i] = v; }, get: function(i) { return array[+i]; } }); }

Both attacks foiled

var stash; table.store(‘__proto__’, { push: function(v) { stash = this; } }); table.add(“doesn’t matter”);

slide-43
SLIDE 43

Bob Carol bob carol counter

Publish or Perish

count count subscribers publish publish subscribe subscribe

Bob can only publish or subscribe. Carol can only subscribe. All subscribers see all publications in order

slide-44
SLIDE 44

Publish or Perish

function Topic() { var subscribers = []; return def({ subscribe: function(subscriber) { subscribers.push(subscriber); }, publish: function(publication) { for (var i = 0; i < subscribers.length; i++) { subscribers[+i](publication); } } }); }

Riddle: find three attacks

slide-45
SLIDE 45

Confusing Callbacks and Methods

function Topic() { var subscribers = []; return def({ subscribe: function(subscriber) { subscribers.push(subscriber); }, publish: function(publication) { for (var i = 0; i < subscribers.length; i++) { subscribers[+i](publication); } } }); } topic.subscribe(function evilSubscriber(publication) { this[+0] = evilSubscriber; this.length = 1; });

slide-46
SLIDE 46

Confusing Callbacks and Methods

function Topic() { var subscribers = []; return def({ subscribe: function(subscriber) { subscribers.push(subscriber); }, publish: function(publication) { for (var i = 0; i < subscribers.length; i++) { (1,subscribers[+i])(publication); } } }); } topic.subscribe(function evilSubscriber(publication) { this[+0] = evilSubscriber; this.length = 1; });

slide-47
SLIDE 47

Aborting the Wrong Plan

function Topic() { var subscribers = []; return def({ subscribe: function(subscriber) { subscribers.push(subscriber); }, publish: function(publication) { for (var i = 0; i < subscribers.length; i++) { (1,subscribers[+i])(publication); } } }); } topic.subscribe(function evilSubscriber(publication) { throw new Error(“skip those losers”); });

slide-48
SLIDE 48

Nested Publication

function Topic() { var subscribers = []; return def({ subscribe: function(subscriber) { subscribers.push(subscriber); }, publish: function(publication) { for (var i = 0; i < subscribers.length; i++) { (1,subscribers[+i])(publication); } } }); } topic.subscribe(function evilSubscriber(publication) { topic.publish(outOfOrderPublication); });

slide-49
SLIDE 49

Asynchronous Helpers

var applyFn = Function.prototype.call.bind(Function.prototype.apply); function applyLater(func, self, args) { setTimeout(function() { applyFn(func, self, args); }, 0); }

  • bj.name(…args)  applyFn(obj.name, obj, args)

func(…args)  applyFn(func, undefined, args)

  • bj ! name(…args)  applyLater(obj.name, obj, args)

func ! (…args)  applyLater(func, undefined, args)

slide-50
SLIDE 50

Publish or Perish, solved

function Topic() { var subscribers = []; return def({ subscribe: function(subscriber) { subscribers.push(subscriber); }, publish: function(publication) { for (var i = 0; i < subscribers.length; i++) { applyLater(subscribers[+i], undefined, [publication]); } } }); }

Thwarts all three attacks

slide-51
SLIDE 51

Publish or Perish, solved beautifully

function Topic() { var subscribers = []; return def({ subscribe: function(subscriber) { subscribers.push(subscriber); }, publish: function(publication) { for (var i = 0; i < subscribers.length; i++) { subscribers[+i] ! (publication); } } }); }

Thwarts all three attacks

slide-52
SLIDE 52

New Skills open up New Worlds

Remember learning

Avoid “this”. Use closures rather than prototypes Freeze everything by default: def, traits Use bare “[” only for reflection: a[+i], map.get(k) Deny callbacks inappropriate “this”: (1,x.foo)(args…), applyFn(x.foo, that, args) Beware Synchronous Callbacks: Use applyLater or infix “!” and various JS oo patterns and their hazards?

Co-evolution of skills and tools

Student SES programmers think to avoid vulnerabilities. Experts avoid enough traps to think about composition. Libraries and IDEs need to learn to help.