防范原型污染的关键是输入层立即清洗__proto__字段并运行时尽早冻结内置原型。优先用object.create(null)创建无原型对象,禁用并递归清除外部输入中的__proto__、constructor等危险键,改用object.getprototypeof等安全api,冻结object.prototype等内置原型。

直接在对象字面量里写 __proto__ 是一种显式设置原型的方式,但它会覆盖默认原型,且该字段可被恶意利用或意外误用,导致原型链被篡改、全局行为异常,甚至触发原型污染。防范的关键不是禁止写法本身,而是切断其生效路径,并用更安全的替代方案替代。
优先使用 Object.create(null) 创建无原型对象
当你只需要一个纯数据容器(比如配置项、映射表、临时缓存),就别用 { __proto__: null } 或 { __proto__: someObj }:
-
Object.create(null)创建的对象没有__proto__属性,也不继承任何方法,天然免疫原型篡改 - 它不参与原型链查找,避免了
hasOwnProperty、toString等被污染或遮蔽的风险 - 需要调用原生方法时,显式借用:
Object.prototype.hasOwnProperty.call(obj, key)
禁用并清洗 __proto__ 字段的输入路径
如果对象来自外部(如 JSON.parse()、表单解析、API 响应),必须在解析后立即扫描并移除所有 __proto__ 相关键:
- 不只检查顶层 key,还要递归检测嵌套结构,例如
{"a": {"__proto__": {"x": 1}}}或"x.__proto__.y"这类路径 - 使用正则
/(__proto__|constructor|prototype)/i对每个键名和完整路径字符串匹配 - 清洗动作必须放在
JSON.parse()后第一时间执行,不能等到合并或赋值阶段
避免依赖 __proto__ 的检测与操作方式__proto__ 是非标准、可写、可配置的访问器属性,极易被覆盖或删除:
- 用
Object.getPrototypeOf(obj)替代obj.__proto__—— 它读取内部[[Prototype]],不受外部赋值干扰 - 类型判断不用
instanceof,改用Object.prototype.toString.call(obj),返回[object Array]等固定字符串,基于不可篡改的内部标识 - 不要用
obj.__proto__ = ...主动设置原型,改用Object.setPrototypeOf(obj, newProto)(仅在必要时),并确认目标环境支持
冻结关键原型作为运行时兜底
在应用初始化最早期(第三方脚本加载前)冻结内置原型:
-
Object.freeze(Object.prototype) -
Object.freeze(Array.prototype)、Object.freeze(String.prototype)等 - 冻结后,任何对这些原型的增删改操作都会静默失败(非严格模式)或抛错(严格模式)
- 注意:冻结不可逆,且无法恢复已被篡改的原型,所以时机必须早、范围必须全
不复杂但容易忽略,真正起效的核心就两点:输入层立刻清洗,运行时尽早冻结。











