原型污染安全漏洞

概念

原型污染安全漏洞(Prototype Pollution) 是前端 / Node.js 生态里非常重要、也非常容易被忽略的一类漏洞。该漏洞最终可导致权限绕过,是前端安全中的高危问题。

什么是原型污染?

攻击者通过可控输入,修改 Object.prototype 或其他内建原型,从而影响所有对象的行为。

JS 原型链

javascript 复制代码
obj → Object.prototype → null

一旦污染了 Object.prototype

ini 复制代码
Object.prototype.isAdmin = true;
({}).isAdmin === true; // 💣

模拟攻击

下面我们通过一段代码来分析一下什么是原型污染。

js 复制代码
function deepMerge(target, source) { 
  for (const key in source) { 
    if (typeof source[key] === 'object') { 
      if (!target[key]) { 
        target[key] = {}; 
      } 
      deepMerge(target[key], source[key]); 
    } else { 
      target[key] = source[key]; 
    } 
  } 
  return target; 
} 
 
// 模拟业务代码 
// 方案1:监听 message 事件 
const config = {}; 
window.addEventListener('message', e => { 
  deepMerge(config, e.data); 
}); 
 
// 方案2:使用 qs 解析 query 
import qs from 'qs'; 
const searchQuery = qs.parse(location.search); 
const routeQuery = { 
  type: 'low', 
  tab: 1, 
}; 
deepMerge(routeQuery, searchQuery); 
 
 
// 业务逻辑 
const userInfo = {}; 
if (userInfo.isAdmin) { 
  console.log('你是管理员'); 
} 

 // 我想成为管理员,请输入攻击荷载: 

这段代码存在典型的原型污染(Prototype Pollution)安全漏洞,而且两个"方案"都能成为攻击入口。下面我按「问题 → 利用方式 → 影响 → 修复建议」来拆解。


一、核心问题在哪?

不安全的 deepMerge

js 复制代码
function deepMerge(target, source) { 
  for (const key in source) { 
    if (typeof source[key] === 'object') { 
      if (!target[key]) { 
        target[key] = {}; 
      } 
      deepMerge(target[key], source[key]); 
    } else { 
      target[key] = source[key]; 
    } 
  } 
}

问题点:

  • 使用 for...in(会遍历原型链)
  • 未过滤 proto / constructor / prototype
  • 未判断是否为「纯对象」
  • 直接递归写入 target[key]

👉 攻击者可以修改 Object.prototype


二、两个业务场景为什么都危险?

方案1:

js 复制代码
window.addEventListener('message', e => {
  deepMerge(config, e.data);
});
攻击 payload
js 复制代码
window.postMessage({proto: {isAdmin: true
  }
}, '*');
结果
js 复制代码
const userInfo = {};
userInfo.isAdmin === true; // 💣

🎯 攻击者无需控制 userInfo,只要污染原型即可


方案2:

js 复制代码
qs.parse(location.search)
const searchQuery = qs.parse(location.search);
deepMerge(routeQuery, searchQuery);
恶意 URL
js 复制代码
https://example.com/?_proto_[isAdmin]=true

或(更隐蔽)

js 复制代码
?constructor[prototype][isAdmin]=true
结果同样是:
js 复制代码
({}).isAdmin === true;

三、最终危害点(业务逻辑被绕过)

js 复制代码
const userInfo = {};
if (userInfo.isAdmin) {
  console.log('你是管理员');
}

🚨 这是一个权限绕过漏洞 在真实系统中,可能导致:

  • 后台权限获取
  • 功能越权
  • 安全校验全部失效

四、为什么这是"高危漏洞"?

特性 说明
利用门槛 低(URL / postMessage 即可)
影响范围 全局(污染 Object.prototype)
隐蔽性 高(调试很难发现)
常见度 非常高(deepMerge / lodash.merge)

👉 已多次出现在真实 CVE (Common Vulnerabilities and Exposures) 中。


五、正确的修复方式 ✅

✅ 1. 严格过滤危险 key(必须)

js 复制代码
const blockedKeys = ['__proto__', 'prototype', 'constructor'];

function safeDeepMerge(target, source) {
  for (const key of Object.keys(source)) {
    if (blockedKeys.includes(key)) continue;

    if (
      typeof source[key] === 'object' &&
      source[key] !== null &&
      !Array.isArray(source[key])
    ) {
      if (!target[key]) target[key] = {};
      safeDeepMerge(target[key], source[key]);
    } else {
      target[key] = source[key];
    }
  }
}

✅ 2. postMessage 必须校验来源

js 复制代码
window.addEventListener('message', e => {
  if (e.origin !== 'https://trusted.com') return;
  safeDeepMerge(config, e.data);
});

✅ 3. qs 使用安全模式

js 复制代码
qs.parse(location.search, {
  allowPrototypes: false
});

⚠️ 默认 qs 是危险的(旧版本尤其)


✅ 4. 使用成熟库

如 URLSearchParams

js 复制代码
const params = Object.fromEntries(new URLSearchParams(location.search));

✅ 5. 防御性编程(推荐)

js 复制代码
const userInfo = Object.create(null);

js 复制代码
if (Object.prototype.hasOwnProperty.call(userInfo, 'isAdmin')) {
  ...
}

为什么它是"安全漏洞"而不是 Bug?

因为它会导致:

后果 示例
权限绕过 if (user.isAdmin)
逻辑篡改 if (config.debug)
RCE(Node) 与 eval / child_process 结合
XSS 模板渲染、配置拼接
DoS JSON stringify / 递归崩溃

👉 影响是"全局的、隐式的、持久的"


真实世界中的 CVE (Common Vulnerabilities and Exposures)

CVE
lodash.merge CVE-2019-10744
jQuery.extend CVE-2019-11358
minimist CVE-2020-7598
yargs CVE-2020-7608
qs 多次

📌 这不是"理论漏洞",而是"被打烂的漏洞类型"


如何判断你的代码是否有原型污染?

🔍 自查 checklist

  • 是否把 用户输入 merge 到对象?
  • 是否使用 for...in
  • 是否递归拷贝对象?
  • 是否信任 qs.parse / JSON?
  • 是否存在 obj[key] = value,且 key 来自外部?

如果有 2 条以上,高风险

相关推荐
QUST-Learn3D10 小时前
geometry4Sharp Ray-Mesh求交 判断点是否在几何体内部
服务器·前端·数据库
持续升级打怪中10 小时前
ES6 Promise 完全指南:从入门到精通
前端·javascript·es6
AC赳赳老秦10 小时前
前端可视化组件开发:DeepSeek辅助Vue/React图表组件编写实战
前端·vue.js·人工智能·react.js·信息可视化·数据分析·deepseek
小白冲鸭10 小时前
苍穹外卖-前端环境搭建-nginx双击后网页打不开
运维·前端·nginx
wulijuan88866610 小时前
Web Worker
前端·javascript
深念Y10 小时前
仿B站项目 前端 3 首页 整体结构
前端·ai·vue·agent·bilibili·首页
IT_陈寒10 小时前
React 18实战:这5个新特性让我的开发效率提升了40%
前端·人工智能·后端
深念Y10 小时前
仿B站项目 前端 5 首页 标签栏
前端·vue·ai编程·bilibili·标签栏·trae·滚动栏
克里斯蒂亚诺更新10 小时前
vue3使用pinia替代vuex举例
前端·javascript·vue.js
Benny的老巢11 小时前
用 Playwright 启动指定 Chrome 账号的本地浏览器, 复用原账号下的cookie信息
前端·chrome