1. 基础概念
JavaScript是一种基于原型的语言,每个对象都有一个原型对象(__proto__属性),对象从原型继承属性和方法。原型链是JavaScript实现继承的主要方式。
在 JavaScript 中,没有引入类这个概念,通常需要定义一个构造函数然后通过 new 操作符调用该函数来创建实例。而 JavaScript 中的继承关系则是靠一种叫做 “原型链” 的模式来实现的。
谈到继承,JavaScript 只有一种结构:对象。每个函数对象有 prototype 属性,而实例对象没有,但所有的实例对象(函数,数组,对象)都会初始化一个私有属性 __proto__ 指向它的 构造函数的原型对象 prototype。该原型对象也有一个自己的原型对象 __proto__,层层向上直到一个对象的原型对象为 null。
1 | function Person(name) { |
原型链污染是指攻击者通过修改对象的原型属性,从而影响所有基于该原型的对象。当对象属性查找时,如果对象本身没有该属性,JavaScript会沿着原型链向上查找。
2. 常见场景
2.1 合并操作
1 | function merge(target, source) { |
2.2 复制/克隆
1 | function clone(obj) { |
2.3 属性赋值漏洞
1 | let obj = {}; |
3. 常见运用
3.1 修改全局对象属性
1 | // 污染后,所有对象都会继承污染属性 |
3.2 覆盖内置方法
1 | // 覆盖toString方法 |
3.3 影响模板引擎
许多模板引擎(如Handlebars, EJS等)会使用原型链查找属性,污染后可导致XSS或代码执行。
1 | // 以EJS为例 |
3.4 影响Express等框架
1 | // Express中的路由处理可能受到原型污染影响 |
4. 防御措施
4.1 使用Object.create(null)
创建没有原型的对象:
1 | let safeObj = Object.create(null); |
4.2 避免使用__proto__
使用Object.getPrototypeOf和Object.setPrototypeOf替代直接访问__proto__。
4.3 安全的对象合并
1 | function safeMerge(target, source) { |
4.4 使用Map代替Object
Map不会受到原型链污染影响:
1 | let map = new Map(); |
5. 实际案例分析
Lodash CVE-2018-3721
Lodash的defaultsDeep函数存在原型污染漏洞:
1 | const _ = require('lodash'); |
jQuery CVE-2019-11358
jQuery的extend函数在特定条件下可被用于原型污染:
1 | jQuery.extend(true, {}, JSON.parse('{"__proto__": {"polluted": true}}')); |
6. 总结
JavaScript原型链污染是CTF Web题目中常见的高级漏洞类型,理解原型链机制是关键。防御措施主要包括:
- 避免不安全的对象操作
- 使用无原型对象(Object.create(null))
- 对用户输入进行严格过滤
- 使用现代JavaScript特性替代危险操作
在CTF比赛中,遇到对象合并、克隆等操作时要特别警惕原型链污染的可能性,尝试通过污染全局原型来影响程序行为。