javascriptnode.jssecurity

How to prevent prototype pollution in JavaScript


Recently I stumbled across a vulnerability in doT.js. The vulnerability exists because attackers can use prototype pollution to modify the values of the options passed in to doT.

Example:

var doT = require("dot");
var tempFn = doT.template("<h1>Here is a sample template " +
    "{{=console.log(23)}}</h1>");
tempFn({})
var doT = require("dot"); // prototype pollution attack vector
Object.prototype.templateSettings = {varname:"a,b,c,d,x=console.log(25)"};
// benign looking template compilation + application
var dots = require("dot").process({path: "./resources"});
dots.mytemplate();

Then I got to thinking: doesn't this mean that virtually any JavaScript library's API options can be compromised through prototype pollution?

For example, here's express.static used with options.

var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now())
  }
}

app.use(express.static('public', options))

Couldn't an attacker set Object.prototype.redirect = true, and, if unspecified by the user, a redirect would occur? And there are surely many more malicious use cases.

What can be done, as a library author, to allow passing in options but safeguard against prototype pollution?

EDIT: I'm focusing specifically on packages distributed with NPM. For example, what could the authors of doT.js do to resolve the vulnerability?


Solution

  • What can be done, as a library author, to allow passing in options but safeguard against prototype pollution?

    You can detect whether a property is on your actual object or inherited via the prototype with .hasOwnProperty(). But, heck an attacker could overwrite .hasOwnProperty() too and change its behavior.

    As I've said in the comments, someone using your library in their Javascript program has FULL source code access to your code. So, they don't even have to use prototype pollution to modify things - they can just hack away at your code however they want.

    To fully protect your code, you'd have to either distribute only a compiled executable that runs in a different process and has an interprocess API (such as an http server) or you'd have to put your code into a service and only offer access that way. If you're distributing a Javascript library, by its very nature, you have to distribute source so the programmer using your library can really do anything they want to it. They don't even have to resort to prototype trickery.