Tip

Prevent JavaScript Object Tampering with the SES Library harden Function

By default, the objects you create in JavaScript are mutable. This means that if anyone wants to change the code you write in run-time, they can. We can use the SES harden function to prevent this type code hijacking.

By default, JavaScript allows you to mutate global objects. We've seen how when you call lockdown() from the ses library that it makes all of JavaScript's shared global objects immutable.

This is great, but the rest of our JavaScript code that we write can still be tampered with.

Say we have this counter object that holds some state and has methods for incrementing and decrementing that state.

const makeCounter = () => {
  let count = 0;
  return {
    count,
    incr() {
      this.count += 1
      return this.count;
      },
    decr() {
      this.count -= 1
      return this.count;
      },
  };
};

const myCounter = makeCounter();
myCounter.incr();
myCounter.incr();
myCounter.incr();
myCounter.decr();
console.log(myCounter); // { count: 2, incr: {}, decr: {} }

Someone can come in and mutate our object.

const myCounter = makeCounter();
myCounter.incr();
myCounter.incr();
myCounter.count = 'hehehehe';
myCounter.incr();
myCounter.decr();
console.log(myCounter); // { count: NaN, incr: {}, decr: {} }

This completely breaks our program. The first thing we can do to prevent this type of tampering is to make count private.

const makeCounter = () => {
  let count = 0;
  return {
    incr() {
      this.count += 1
      return this.count;
      },
    decr() {
      this.count -= 1
      return this.count;
      },
  };
};

const myCounter = makeCounter();
myCounter.incr();
myCounter.incr();
myCounter.incr();
myCounter.count = 'hehehehe';
myCounter.decr();
const lastValue = myCounter.incr();
console.log({lastValue}); // { lasValue: 3 }

This prevents us from directly accessing count and mutating it but we can still mutate our methods.

const myCounter = makeCounter();
myCounter.incr();
myCounter.incr();
myCounter.incr();
myCounter.incr = () => {
  console.log('I have hijacked your increment. There is nothing you can do.');
};
myCounter.decr();
const lastValue = myCounter.incr();
console.log({lastValue}); // { lasValue: undefined }

These methods need to be accessible to people because that's the API we want to expose so we can't just make them private.

We can use the harden function from the ses library to make our methods tamper-proof.

const makeCounter = () => {
  let count = 0;
  return harden({
    incr() {
      this.count += 1
      return this.count;
      },
    decr() {
      this.count -= 1
      return this.count;
      },
  });
};

This locks down the object and prevents anyone from mutating it. Now if you try to reassign incr you'll get an error telling you TypeError: Cannot assign to read only property 'incr' of object '[object Object]'.