Upgrade to Pro — share decks privately, control downloads, hide ads and more …

nodejsday 2020: JavaScript Prototypes Behind the Scenes

nodejsday 2020: JavaScript Prototypes Behind the Scenes

JavaScript is a prototype-oriented language. But for many, what this really means or how it actually works is a little fuzzy. It seems, that as quickly as you learn how prototypes work behind the scenes, as quickly do you forget it again. If you can relate to this feeling, this talk is for you.

We’ll dive into why JavaScript is a prototype-oriented language instead of class-based and what that means for you as a developer. You’ll learn exactly how prototypal inheritance works in JavaScript - in a way that’s easy to remember - so that you can become a better programmer.

Finally, you’ll learn about the security pitfalls that exist in the way prototypal inheritance is implemented in JavaScript and how to write more secure code to avoid those pitfalls.

Thomas Watson

October 02, 2020
Tweet

More Decks by Thomas Watson

Other Decks in Programming

Transcript

  1. wa7son Thomas Watson wa7son github.com/watson

  2. wa7son Who am I? • Thomas Watson • Principal Software

    Engineer at Elastic • Open Source developer at github.com/watson • Node.js Core Member • Tweets as @wa7son • Slides: github.com/watson/talks
  3. wa7son JavaScript Prototypes Behind the Scenes wa7son

  4. wa7son In the beginning… wa7son

  5. wa7son

  6. wa7son

  7. wa7son

  8. wa7son wa7son Classes vs Prototypes

  9. wa7son Class Inheritance wa7son

  10. wa7son Image - height - width - bitmap

  11. wa7son Image - height - width - bitmap Button -

    height - width - label - onClick()
  12. wa7son Button - label - onClick() Image - bitmap UIElement

    - height - width
  13. wa7son Button - label - onClick() Image - bitmap -

    onClick() UIElement - height - width
  14. wa7son Button - label Image - bitmap UIElement - height

    - width - onClick()
  15. wa7son Button - label Image - bitmap UIElement - height

    - width ClickableElement - onClick()
  16. wa7son Banana / Gorilla Problem wa7son “You wanted a banana

    but what you got was a gorilla holding the banana and the entire jungle” - Joe Armstrong, the creator of Erlang
  17. wa7son Prototypes wa7son

  18. wa7son wa7son Delegates • Shared properties / methods • Don’t

    find what you’re looking for on the object? Look at its prototype! • Conserve memory Prototypes
  19. wa7son Java’isk Object Creation wa7son function Cat (name) { this.name

    = name } Cat.prototype.talk = function () { return `Meow, I'm ${this.name}!` } const whiskers = new Cat('Mr. Whiskers') whiskers.talk() // Meow, I'm Mr. Whiskers!
  20. wa7son Cleaner Prototype Object Creation wa7son const proto = {

    talk () { return `Meow, I'm ${this.name}!` } } const whiskers = Object.create(proto) whiskers.name = 'Mr. Whiskers' whiskers.talk() // Meow, I'm Mr. Whiskers!
  21. wa7son wa7son Delegates Cloning / Concatenation • Shared properties /

    methods • Don’t find what you’re looking for on the object? Look at its prototype! • Conserve memory • Default state / mixins Prototypes
  22. wa7son Mixing it up wa7son class Image extends UIElement {

    // ... } const myImage = new Image() Object.assign(myImage, Clickable)
  23. wa7son wa7son Delegates Cloning / Concatenation • Shared properties /

    methods • Don’t find what you’re looking for on the object? Look at its prototype! • Conserve memory • Default state / mixins Prototypes
  24. wa7son Time to get our hands dirty… wa7son

  25. {} Object Object.prototype Function Function.prototype null Object Function __proto__ __proto__

    __proto__ __proto__ __proto__ prototype prototype constructor constructor constructor constructor constructor Inherited Own © Thomas Watson // @wa7son
  26. {} Object Object.prototype Function Function.prototype null Object Function __proto__ __proto__

    __proto__ __proto__ __proto__ prototype prototype constructor constructor constructor constructor constructor Inherited Own {} __proto__ constructor Object with custom prototype © Thomas Watson // @wa7son
  27. MyObj.prototype Object Object.prototype Function Function.prototype null Object Function © Thomas

    Watson // @wa7son __proto__ __proto__ __proto__ __proto__ __proto__ prototype prototype constructor constructor constructor constructor constructor Inherited Own new MyObj() __proto__ constructor MyObj prototype constructor __proto__
  28. wa7son Prototype Pollution wa7son

  29. wa7son How to clone an object wa7son function merge (target,

    source) { for (const [key, value] of Object.entries(source)) { target[key] = value } return target } merge({ a: 1 }, { b: 2 }) // { a: 1, b: 2 } merge( { a: { foo: 1 } }, { a: { bar: 2 } } ) // { a: { bar: 2 } }
  30. wa7son wa7son function merge (target, source) { for (const [key,

    value] of Object.entries(source)) { target[key] = value } return target } merge({ a: 1 }, { b: 2 }) // { a: 1, b: 2 } merge( { a: { foo: 1 } }, { a: { bar: 2 } } ) // { a: { bar: 2 } } How to clone an object
  31. wa7son wa7son function merge (target, source) { for (const [key,

    value] of Object.entries(source)) { target[key] = value } return target } merge({ a: 1 }, { b: 2 }) // { a: 1, b: 2 } merge( { a: { foo: 1 } }, { a: { bar: 2 } } ) // { a: { bar: 2 } } ✅ ❌ How to clone an object
  32. wa7son wa7son function deepMerge (target, source) { for (const [key,

    value] of Object.entries(source)) { if (isObject(value)) { const clone = deepMerge({}, value) if (isObject(target[key])) { deepMerge(target[key], clone) } else { target[key] = clone } } else { target[key] = value } } return target } How to deep-clone an object
  33. wa7son wa7son function deepMerge (target, source) { for (const [key,

    value] of Object.entries(source)) { if (isObject(value)) { const clone = deepMerge({}, value) if (isObject(target[key])) { deepMerge(target[key], clone) } else { target[key] = clone } } else { target[key] = value } } return target } How to deep-clone an object
  34. wa7son wa7son function deepMerge (target, source) { for (const [key,

    value] of Object.entries(source)) { if (isObject(value)) { const clone = deepMerge({}, value) if (isObject(target[key])) { deepMerge(target[key], clone) } else { target[key] = clone } } else { target[key] = value } } return target } How to deep-clone an object
  35. wa7son wa7son function deepMerge (target, source) { for (const [key,

    value] of Object.entries(source)) { if (isObject(value)) { const clone = deepMerge({}, value) if (isObject(target[key])) { deepMerge(target[key], clone) } else { target[key] = clone } } else { target[key] = value } } return target } How to deep-clone an object
  36. wa7son wa7son function deepMerge (target, source) { for (const [key,

    value] of Object.entries(source)) { if (isObject(value)) { const clone = deepMerge({}, value) if (isObject(target[key])) { deepMerge(target[key], clone) } else { target[key] = clone } } else { target[key] = value } } return target } How to deep-clone an object ✅ deepMerge( { a: { foo: 1 } }, { a: { bar: 2 } } ) // { a: { foo: 1, bar: 2 } }
  37. wa7son The Attack wa7son function isAdmin (user) { return user.admin

    === true } const user = {} isAdmin(user) // false const payload = '{"__proto__":{"admin":true}}' deepMerge({}, JSON.parse(payload)) isAdmin(user) // true
  38. wa7son The Attack wa7son function isAdmin (user) { return user.admin

    === true } const user = {} isAdmin(user) // false const payload = '{"__proto__":{"admin":true}}' deepMerge({}, JSON.parse(payload)) isAdmin(user) // true
  39. wa7son The Attack wa7son function isAdmin (user) { return user.admin

    === true } const user = {} isAdmin(user) // false const payload = '{"__proto__":{"admin":true}}' deepMerge({}, JSON.parse(payload)) isAdmin(user) // true
  40. wa7son The Attack wa7son function isAdmin (user) { return user.admin

    === true } const user = {} isAdmin(user) // false const payload = '{"__proto__":{"admin":true}}' deepMerge({}, JSON.parse(payload)) isAdmin(user) // true
  41. wa7son The Attack wa7son function isAdmin (user) { return user.admin

    === true } const user = {} isAdmin(user) // false const payload = '{"__proto__":{"admin":true}}' deepMerge({}, JSON.parse(payload)) isAdmin(user) // true
  42. wa7son wa7son • Look out for `__proto__` in untrusted user

    input • Look out for `constructor.prototype` in untrusted user input The Antidote
  43. {} Object Object.prototype Function Function.prototype null Object Function __proto__ __proto__

    __proto__ __proto__ __proto__ prototype prototype constructor constructor constructor constructor constructor Inherited Own © Thomas Watson // @wa7son
  44. wa7son wa7son • Look out for `__proto__` in untrusted user

    input • Look out for `constructor.prototype` in untrusted user input npm install secure-json-parse The Antidote
  45. wa7son wa7son The Antidote

  46. github.com/watson Grazie mille wa7son