JSON.parse()
drop-in replacement with prototype poisoning protection.
Consider this:
> const a = '{"__proto__":{ "b":5}}'; '{"__proto__":{ "b":5}}' > const b = JSON.parse(a); { __proto__: { b: 5 } } > b.b; undefined > const c = Object.assign({}, b); {} > c.b 5
The problem is that JSON.parse()
retains the __proto__
property as a plain object key. By itself, this is not a security issue. However, as soon as that object is assigned to another or iterated on and values copied, the __proto__
property leaks and becomes the object's prototype.
npm i secure-json-parse
Pass the option object as a second (or third) parameter for configuring the action to take in case of a bad JSON, if nothing is configured, the default is to throw a SyntaxError
.
You can choose which action to perform in case __proto__
is present, and in case constructor.prototype
is present.
const sjson = require('secure-json-parse') const goodJson = '{ "a": 5, "b": 6 }' const badJson = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "constructor": {"prototype": {"bar": "baz"} } }' console.log(JSON.parse(goodJson), sjson.parse(goodJson, undefined, { protoAction: 'remove', constructorAction: 'remove' })) console.log(JSON.parse(badJson), sjson.parse(badJson, undefined, { protoAction: 'remove', constructorAction: 'remove' }))
Parses a given JSON-formatted text into an object where:
text
- the JSON text string.reviver
- theJSON.parse()
optionalreviver
argument.options
- optional configuration object where:protoAction
- optional string with one of:'error'
- throw aSyntaxError
when a__proto__
key is found. This is the default value.'remove'
- deletes any__proto__
keys from the result object.'ignore'
- skips all validation (same as callingJSON.parse()
directly).
constructorAction
- optional string with one of:'error'
- throw aSyntaxError
when aconstructor.prototype
key is found. This is the default value.'remove'
- deletes anyconstructor
keys from the result object.'ignore'
- skips all validation (same as callingJSON.parse()
directly).
safe
- optional boolean:true
- returnsnull
instead of throwing when a forbidden prototype property is found.false
- default behavior (throws or removes based onprotoAction
/constructorAction
).
Scans a given object for prototype properties where:
obj
- the object being scanned.options
- optional configuration object where:protoAction
- optional string with one of:'error'
- throw aSyntaxError
when a__proto__
key is found. This is the default value.'remove'
- deletes any__proto__
keys from the inputobj
.
constructorAction
- optional string with one of:'error'
- throw aSyntaxError
when aconstructor.prototype
key is found. This is the default value.'remove'
- deletes anyconstructor
keys from the inputobj
.
safe
- optional boolean:true
- returnsnull
instead of throwing when a forbidden prototype property is found.false
- default behavior (throws or removes based onprotoAction
/constructorAction
).
Machine: 2.4 GHz 14-core Intel Core i7-13670HX
v22.20.0 > node valid.js JSON.parse x 1,802,844 ops/sec ±1.79% (88 runs sampled) JSON.parse proto x 1,219,302 ops/sec ±2.10% (92 runs sampled) secure-json-parse parse x 1,657,400 ops/sec ±1.65% (94 runs sampled) secure-json-parse parse proto x 1,807,648 ops/sec ±2.41% (90 runs sampled) secure-json-parse safeParse x 1,652,255 ops/sec ±1.50% (93 runs sampled) secure-json-parse safeParse proto x 1,103,083 ops/sec ±1.73% (93 runs sampled) JSON.parse reviver x 287,161 ops/sec ±2.37% (91 runs sampled) Fastest is secure-json-parse parse proto > node ignore.js JSON.parse x 1,210,169 ops/sec ±1.44% (92 runs sampled) secure-json-parse parse x 1,158,560 ops/sec ±1.14% (95 runs sampled) secure-json-parse safeParse x 1,124,013 ops/sec ±0.67% (98 runs sampled) reviver x 196,308 ops/sec ±0.57% (92 runs sampled) Fastest is JSON.parse > node no__proto__.js JSON.parse x 1,159,929 ops/sec ±1.19% (88 runs sampled) secure-json-parse parse x 1,057,914 ops/sec ±0.55% (96 runs sampled) secure-json-parse safeParse x 1,049,393 ops/sec ±1.37% (95 runs sampled) reviver x 185,783 ops/sec ±0.72% (94 runs sampled) Fastest is JSON.parse > node remove.js JSON.parse x 1,247,924 ops/sec ±0.94% (94 runs sampled) secure-json-parse parse x 487,633 ops/sec ±1.62% (91 runs sampled) secure-json-parse safeParse x 1,124,136 ops/sec ±0.85% (97 runs sampled) reviver x 183,020 ops/sec ±1.41% (95 runs sampled) Fastest is JSON.parse > node throw.js JSON.parse valid x 1,234,770 ops/sec ±0.64% (90 runs sampled) JSON.parse error x 126,202 ops/sec ±2.22% (82 runs sampled) secure-json-parse parse x 292,250 ops/sec ±1.74% (94 runs sampled) secure-json-parse safeParse x 331,809 ops/sec ±2.72% (89 runs sampled) reviver x 119,842 ops/sec ±1.24% (79 runs sampled) Fastest is JSON.parse valid
This project has been forked from hapijs/bourne. All credit before commit 4690682 goes to the hapijs/bourne project contributors. After, the project will be maintained by the Fastify team.
Licensed under BSD-3-Clause.