注意,JS 里的 JSON.parse() 也有安全隐患!
时间:2026-6-9 00:18 作者:独元殇 分类: 前端技术
其实我们天天使用的那个 JSON.parse() 也是一个坑。
而且还挺危险。有点像 PHP 里面的注入一样。
首先就是它是干什么的。
// 一个JSON格式的字符串
const jsonString='{"name":"在线企业交易平台","listings":5000,"types":["网站","电商","SaaS","应用"]}';
// JSON.parse() 将其转化为对象
const businessData=JSON.parse(jsonString);
console.log(businessData.name); // 输出: 在线企业交易平台
console.log(`共${businessData.listings}个待售业务`); // 输出: 共5000个待售业务
它就是把 JSON 转化为 JavaScript 可直接使用的对象。
不要使用反模式来解析 JSON
我们经常用其,在 request 请求回复、cookie、localstorage、readfilesync 等读取上、但是....
请看大屏幕!(当然,这个坑在 2023 年以后被部分浏览器修复了,包括 node 18.13+ )
const maliciousInput = '{"__proto__":{"hasAdminPrivilege":true}}';
const targetObj = {};
JSON.parse(maliciousInput, (key, value) => {
targetObj[key] = value;
return value;
});
//------
// 从这里开始,所有新创建的 {} 这种空对象,就都完蛋了。
console.log({}.hasAdminPrivilege); // 输出: true!全局原型被成功篡改
console.log(new Object().hasAdminPrivilege); // 输出: true! 一样!
有没有觉得背后一凉!
俺就是读了一个装着 JSON 的字符串,怎么就把全局变量给污染了呢?
这是著名的【原型污染漏.洞】我估计现在的很多前端,都不知道有这个知识点,这个特性(bug)危害还挺大,神不知鬼不觉,就能绕过验证,甚至篡改一些属性,比如给你加个 rm -rf / ,你天真的就发过去执行了。
虽然很多浏览器已经修复这个 bug ,但是我们依然不要使用 JSON.parse(value, (key, value)=>{ a[key]= value;return value;}); 这种语法。因为直接写 JSON.parse(value) 其实就够了,是很安全的。
(你可能会问,我一直都这样写的啊,谁非得拆开写?对!正常人可能懒得这样写,但是凡事总有意外。因为 jqurey 的源代码,以前就这样写的, 然后就出事了.... 影响够大吧!)
当然,如果你非要这样写,可以试试过滤掉这三个特殊的值。
function secureJSONParse(str) {
return JSON.parse(str, (key, value) => {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
throw new Error('Forbidden key detected');
}
return value;
});
}
如果你是小白,不知道 proto 、constructor、prototype 都是什么的话。我简单说说:
首先是第一个,叫【公开访问器属性】,在 2024 年之前, proto 是一个,你可以理解为是指针,指的 Object.prototype ,也就是全局对象的根原型,全部对象都改了!不过现在这个 bug 刚刚被修,但是你知道,全世界大把的浏览器,都是好几年前没更新的,所以危害依然比较大。
况且.... node 版本,为了稳定,不乱改版本,全世界有多少服务器没更新?
而 constructor、prototype 也差不多,都是Object.prototype 的属性,都能顺藤摸瓜改变全局。
虽然 2026 年,修复很好了,但是依然还有点危险,还能污染全局:
上面让人惊悚的代码:
// 深度合并
function deepMerge(target, source) {
for (const key in source) {
const val = source[key];
val && typeof val === 'object' && !Array.isArray(val)
? deepMerge(target[key] || (target[key] = {}), val)
: (target[key] = val);
}
return target;
}
// 污染
deepMerge({}, {"constructor":{"prototype":{"isAdmin":true}}});
// 验证全局污染
console.log({}.isAdmin); // true
console.log(new Object().isAdmin); // true
console.log([].isAdmin); // true
我们要合并一些 对象,很容易写这种函数代码,,,如果有个坏人给你那个污染的属性,,,那也还是有危险。起码现在这个 bug ,2026-6 月了,还是存在。
做 SaaS 用 zod
我们制作 node 应用、或者前端应用时,要过滤数据。
自己写吗??? 重复造轮子干什么,如果要考虑全面的话,其实一个验证数据的函数,你可能要写很长很长。
其实在前端、全栈业内,有成熟的解决方案,一个叫 zod ,另一个叫 Joi , 前者更流行,推荐使用。包括今天讲的这个 JSON.parse() 大坑,也可以过滤掉。
使用起来,简单的很:
import { z } from 'zod';
// 定义用户数据的结构契约
const UserSchema = z.object({
id: z.number().int().positive(),
name: z.string().min(1).max(50),
email: z.string().email(),
isAdmin: z.boolean().optional().default(false),
});
只需要 z 一下,数据就安全了。
如果是做 SaaS 的,这个是必备知识点。因为用 node 的开发者,普遍是前端兼职的,一般不太注重数据安全过滤。zod 一定得知道,起码在和 AI 对话时,关键的信息提取,得加一个 zod 词语。