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-13650HX
v22.20.0 > benchmarks@1.0.0 valid > node valid.js valid benchmark βββββββββββ¬ββββββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββ¬βββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬ββββββββββ β (index) β Task name β Latency avg (ns) β Latency med (ns) β Throughput avg (ops/s) β Throughput med (ops/s) β Samples β βββββββββββΌββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββΌβββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββΌββββββββββ€ β 0 β 'JSON.parse' β '610.10 Β± 0.39%' β '600.00 Β± 0.00' β '1740515 Β± 0.02%' β '1666667 Β± 0' β 1639075 β β 1 β 'JSON.parse proto' β '875.42 Β± 0.39%' β '800.00 Β± 0.00' β '1210508 Β± 0.03%' β '1250000 Β± 0' β 1142308 β β 2 β 'secure-json-parse parse' β '634.34 Β± 0.32%' β '600.00 Β± 0.00' β '1624445 Β± 0.01%' β '1666667 Β± 0' β 1576434 β β 3 β 'secure-json-parse parse proto' β '657.25 Β± 0.42%' β '600.00 Β± 0.00' β '1666577 Β± 0.03%' β '1666667 Β± 0' β 1521499 β β 4 β 'secure-json-parse safeParse' β '646.03 Β± 1.68%' β '600.00 Β± 0.00' β '1622543 Β± 0.02%' β '1666667 Β± 0' β 1547914 β β 5 β 'secure-json-parse safeParse proto' β '912.34 Β± 0.20%' β '900.00 Β± 0.00' β '1122250 Β± 0.02%' β '1111111 Β± 0' β 1096080 β β 6 β 'JSON.parse reviver' β '3448.5 Β± 0.59%' β '3200.0 Β± 0.00' β '300173 Β± 0.04%' β '312500 Β± 0' β 289982 β βββββββββββ΄ββββββββββββββββββββββββββββββββββββββ΄βββββββββββββββββββ΄βββββββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄ββββββββββ > benchmarks@1.0.0 ignore > node ignore.js ignore benchmark βββββββββββ¬ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββ¬ββββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬ββββββββββ β (index) β Task name β Latency avg (ns) β Latency med (ns) β Throughput avg (ops/s) β Throughput med (ops/s) β Samples β βββββββββββΌββββββββββββββββββββββββββββββββΌβββββββββββββββββββΌββββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββΌββββββββββ€ β 0 β 'JSON.parse' β '897.15 Β± 0.53%' β '800.00 Β± 0.00' β '1201546 Β± 0.03%' β '1250000 Β± 0' β 1114647 β β 1 β 'secure-json-parse parse' β '891.22 Β± 0.45%' β '800.00 Β± 0.00' β '1168492 Β± 0.02%' β '1250000 Β± 0' β 1122056 β β 2 β 'secure-json-parse safeParse' β '938.74 Β± 0.56%' β '900.00 Β± 0.00' β '1106881 Β± 0.02%' β '1111111 Β± 0' β 1065255 β β 3 β 'reviver' β '5741.8 Β± 0.79%' β '4900.0 Β± 100.00' β '188823 Β± 0.08%' β '204082 Β± 4252' β 174162 β βββββββββββ΄ββββββββββββββββββββββββββββββββ΄βββββββββββββββββββ΄ββββββββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄ββββββββββ > benchmarks@1.0.0 no_proto > node no__proto__.js no __proto__ benchmark βββββββββββ¬ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββ¬ββββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬ββββββββββ β (index) β Task name β Latency avg (ns) β Latency med (ns) β Throughput avg (ops/s) β Throughput med (ops/s) β Samples β βββββββββββΌββββββββββββββββββββββββββββββββΌβββββββββββββββββββΌββββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββΌββββββββββ€ β 0 β 'JSON.parse' β '930.41 Β± 0.56%' β '800.00 Β± 0.00' β '1154630 Β± 0.03%' β '1250000 Β± 0' β 1074798 β β 1 β 'secure-json-parse parse' β '996.09 Β± 0.27%' β '900.00 Β± 0.00' β '1039752 Β± 0.02%' β '1111111 Β± 0' β 1003921 β β 2 β 'secure-json-parse safeParse' β '1050.5 Β± 7.38%' β '900.00 Β± 0.00' β '1038060 Β± 0.02%' β '1111111 Β± 0' β 951942 β β 3 β 'reviver' β '5424.7 Β± 3.23%' β '5100.0 Β± 100.00' β '192362 Β± 0.05%' β '196078 Β± 3922' β 184341 β βββββββββββ΄ββββββββββββββββββββββββββββββββ΄βββββββββββββββββββ΄ββββββββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄ββββββββββ > benchmarks@1.0.0 remove > node remove.js remove benchmark βββββββββββ¬ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββ¬ββββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬ββββββββββ β (index) β Task name β Latency avg (ns) β Latency med (ns) β Throughput avg (ops/s) β Throughput med (ops/s) β Samples β βββββββββββΌββββββββββββββββββββββββββββββββΌβββββββββββββββββββΌββββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββΌββββββββββ€ β 0 β 'JSON.parse' β '927.86 Β± 0.51%' β '800.00 Β± 0.00' β '1161336 Β± 0.03%' β '1250000 Β± 0' β 1077745 β β 1 β 'secure-json-parse parse' β '1968.1 Β± 0.51%' β '1900.0 Β± 100.00' β '525418 Β± 0.02%' β '526316 Β± 26316' β 508117 β β 2 β 'secure-json-parse safeParse' β '930.60 Β± 0.19%' β '900.00 Β± 0.00' β '1103037 Β± 0.02%' β '1111111 Β± 0' β 1074579 β β 3 β 'reviver' β '5531.4 Β± 0.36%' β '5100.0 Β± 100.00' β '187392 Β± 0.06%' β '196078 Β± 3922' β 180786 β βββββββββββ΄ββββββββββββββββββββββββββββββββ΄βββββββββββββββββββ΄ββββββββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄ββββββββββ > benchmarks@1.0.0 throw > node throw.js throw benchmark βββββββββββ¬ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββ¬ββββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬ββββββββββ β (index) β Task name β Latency avg (ns) β Latency med (ns) β Throughput avg (ops/s) β Throughput med (ops/s) β Samples β βββββββββββΌββββββββββββββββββββββββββββββββΌβββββββββββββββββββΌββββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββΌββββββββββ€ β 0 β 'JSON.parse valid' β '908.52 Β± 0.54%' β '800.00 Β± 0.00' β '1178218 Β± 0.03%' β '1250000 Β± 0' β 1100690 β β 1 β 'JSON.parse error' β '7993.2 Β± 0.54%' β '7600.0 Β± 200.00' β '128668 Β± 0.06%' β '131579 Β± 3374' β 125108 β β 2 β 'secure-json-parse parse' β '3436.4 Β± 2.67%' β '3100.0 Β± 100.00' β '312206 Β± 0.05%' β '322581 Β± 10081' β 291001 β β 3 β 'secure-json-parse safeParse' β '2800.9 Β± 0.33%' β '2700.0 Β± 0.00' β '364670 Β± 0.03%' β '370370 Β± 0' β 357026 β β 4 β 'reviver' β '9045.6 Β± 1.12%' β '8300.0 Β± 200.00' β '115581 Β± 0.08%' β '120482 Β± 2975' β 110552 β βββββββββββ΄ββββββββββββββββββββββββββββββββ΄βββββββββββββββββββ΄ββββββββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄ββββββββββ
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.