Skip to content

Node.js 安全防护

1. 原型链污染 (Prototype Pollution) 🔥

漏洞原理

攻击者通过恶意 JSON 数据 (包含 __proto__constructorprototype) 修改 Object.prototype

javascript
// 恶意 Payload
const malicious = JSON.parse('{"a": 1, "__proto__": {"isAdmin": true}}');

// 不安全的深度合并
function merge(target, source) {
    for (let key in source) {
        if (typeof source[key] === 'object') {
            target[key] = merge(target[key] || {}, source[key]);
        } else {
            target[key] = source[key];
        }
    }
    return target;
}

const user = {};
merge(user, malicious);

// 污染成功!
console.log({}.isAdmin);  // true (任何对象都有 isAdmin!)

2. 严重后果 🔥

逻辑绕过

javascript
function checkPermission(user) {
    if (user.isAdmin) {  // 从原型链找到 true
        return true;
    }
    return false;
}

const normalUser = {};  // 没有 isAdmin 属性
checkPermission(normalUser);  // 被污染后返回 true!

拒绝服务 (DoS)

javascript
// 注入循环引用
const payload = '{"__proto__": {"toString": {}}}';
merge({}, JSON.parse(payload));

// 后续所有对象调用 toString 都会报错
const obj = {};
obj.toString();  // TypeError!

远程代码执行 (RCE)

结合某些模板引擎或 child_process

javascript
// 污染 env 或 shell 选项
{"__proto__": {"shell": "/bin/bash", "env": {"NODE_OPTIONS": "--require=./malicious.js"}}}

CAUTION

EJS、Pug 等模板引擎曾因原型链污染导致 RCE 漏洞。


3. 防御手段 🔥

方法 1: 使用 Map 代替 Object

javascript
// ✅ Map 没有原型链污染问题
const cache = new Map();
cache.set('key', value);

方法 2: 无原型对象

javascript
// ✅ 使用 Object.create(null)
const safeObj = Object.create(null);
safeObj.__proto__ = 'attack';  // 只是普通属性,不会污染
console.log({}.hasOwnProperty);  // 仍然正常

方法 3: 冻结原型

javascript
// ⚠️ 应用启动时立即执行 (可能导致某些库不兼容)
Object.freeze(Object.prototype);
Object.freeze(Array.prototype);

方法 4: 输入过滤

javascript
// ✅ 安全的深度合并
function safeMerge(target, source) {
    const FORBIDDEN = ['__proto__', 'constructor', 'prototype'];
    
    for (let key in source) {
        if (FORBIDDEN.includes(key)) continue;  // 过滤危险键
        
        if (typeof source[key] === 'object' && source[key] !== null) {
            target[key] = safeMerge(target[key] || {}, source[key]);
        } else {
            target[key] = source[key];
        }
    }
    return target;
}

方法 5: 使用安全的库

javascript
// ✅ lodash 4.17.12+ 已修复
const _ = require('lodash');
// 确保使用最新版本

// ✅ 或使用 @hapi/hoek
const Hoek = require('@hapi/hoek');
Hoek.merge(target, source);

4. 检测工具

bash
# npm audit
npm audit

# snyk
npx snyk test

自动化检测

javascript
// 检测是否被污染
function isPrototypePolluted() {
    const test = {};
    return test.polluted !== undefined;
}

// 定期检查
setInterval(() => {
    if (isPrototypePolluted()) {
        console.error('Prototype pollution detected!');
        process.exit(1);
    }
}, 60000);

前端面试知识库