JavaScript prototypes are known and used by many people, but many people use prototype inheritance in the security problems caused by few people know, next we will understand it well.

In real development, we often use property accessors in our code and use user input parameters to access the properties of an object. This may seem like a very rare operation, but often in the process our code has created a big security hole!

​Why does writing code this way create security problems?

We can look at the following example

const internal = {
  foo: {
    bar: null
  }
}

const acceptUserInput = (type, subtype, value) => {
  internal[type][subtype] = value
}

Applications.
// Normal access
acceptUserInput('foo', 'bar', 'I am so clever')

// malicious attack
acceptUserInput('__proto__', 'polluted', 'Hello I am a hacker and permission is allowed')

In our malicious attack, we added a polluted attribute to our prototype. This causes all our newly created objects to have a polluted attribute with the value "Hello I'm a hacker and permission is allowed", which gives an opportunity to the bad guys with bad intentions to take advantage of it.
const obj = {}
console.debug(obj.polluted) // 'Hello I'm a hacker and permissions are allowed'

Why is it insecure?

If this is on the client, this may not be a big problem, if this is on the server, then it may provide a vulnerability for hacking.

Suppose the hacker knows that your code will create a new object when it runs, and you don't use Object.create(null) to create an object without a prototype.

By adding properties to the prototype, the hacker can unlock additional user privileges, such as site modification privileges, vip privileges, etc. to attack your site and make your site suffer damage.

Code example

const internal = {
  foo: {
    bar: null
  }
}

const acceptUserInput = (type, subtype, value) => {
  internal[type][subtype] = value
}

// Assuming an object
// object is created at code runtime
// Assume that the data is fetched from the database
const getRoles = () => ({ canAccessThat: true })

const userCanAccessThis = () => {
  const me = getCurrentUser() // get from session etc
  const roles = getRoles(me.id)
  return roles.canAccessThis === true
}

// malicious attack
acceptUserInput('__proto__', 'canAccessThis', true)

// Now each user will return true for each user's canAccessThis
userCanAccessThis()

This is a simple simulation example that allows us to see the power of this vulnerability more visually.

How do you prevent vulnerabilities from arising?

  • Reduce the use of property accessors in your code by using . to access the properties of an object

  • or use Map or Set instead of our objects

  • Check the prototype chain of the object to see if the prototype of the newly created object has been maliciously added with properties that should not be there, or if the properties have been modified

  • Check user input, only to prevent malicious user input

  • Use Object.create(null) to create an object without a prototype