蓝桥杯-网络安全比赛(6)基础学习-JavaScript 原型链污染
原型链污染是指攻击者通过修改JavaScript对象的原型(prototype),从而影响了基于该原型创建的所有对象的行为。
在JavaScript中,每个对象都有一个指向其原型的内部链接,这个原型本身也可以有它自己的原型,形成了一条原型链。
当访问一个对象的属性时,如果该对象自身没有这个属性,JavaScript引擎会沿着原型链向上查找。
攻击者可能会利用这个机制,通过修改对象的原型来注入恶意属性或方法,进而在应用程序的其他部分执行恶意代码或改变预期行为。
这通常发生在攻击者能够控制或影响对象原型的情况下,比如通过用户输入或某些不安全的操作。
什么是原型链污染
例如之前讲的原型案例:
// 假设我们有一个名为 Alice 的对象,它有一个 greet 方法
let Alice = {
greet: function() {
console.log("Hello, I'm Alice.");
}
};
// Alice 调用 greet 方法,输出 "Hello, I'm Alice."
Alice.greet(); // 输出: Hello, I'm Alice.
// 接下来,攻击者试图通过修改 Alice 的原型来污染原型链
Alice.__proto__.evilGreet = function() {
console.log("You've been hacked!");
// 这里可以执行更多的恶意代码...
};
// 攻击者现在创建了一个新的对象 Bob,它基于 Object.prototype(即 Alice 的原型)
let Bob = {};
// Bob 调用 evilGreet 方法,这是通过原型链污染注入的
Bob.evilGreet(); // 输出: You've been hacked!
// 更糟糕的是,由于原型链污染,现在所有基于 Object.prototype 创建的对象都继承了这个恶意的 evilGreet 方法
// 包括 Alice 对象本身(尽管它原本有自己的 greet 方法)
Alice.evilGreet(); // 输出: You've been hacked!
在这个案例中,攻击者通过向 Alice.__proto__
(即 Object.prototype
)添加一个新的 evilGreet
方法,成功地污染了原型链。
这不仅影响了新创建的对象 Bob
,还影响了原有的 Alice
对象。
即使 Alice
对象原本有自己的 greet
方法,它现在也能访问到通过原型链污染注入的 evilGreet
方法。
原型链污染通常发生在以下几种情况:
- 不安全的对象合并:当开发者尝试将多个对象合并成一个时,如果没有正确检查和处理原型链上的属性,就可能导致污染。
例如,如果攻击者能够控制合并操作中的某个对象,并为其添加__proto__属性,那么合并后的对象可能会继承攻击者指定的属性。- 使用不安全的库或框架:某些库或框架在处理对象时可能存在漏洞,允许攻击者通过特定输入修改原型链。
- 用户输入处理不当:如果应用程序没有正确验证和清理用户输入,攻击者可能会构造恶意的输入来修改原型链。
不安全的对象合并
在实际应用中,哪些情况下可能存在原型链能被攻击者修改的情况呢?
我们思考一下,哪些情况下我们可以设置_proto__的值呢?其实找找能够控制数组(对象)的“键名”的操作即可:
。对象merge 结合 拼接
。对象clone(其实内核就是将待操作的对象merge到一个空对象中)复制以对象merge为例,我们想象一个简单的merge函数:
function merge(target, source){
for (let key in source){
if(key in source && key in target){
merge(target[key],source[key]);
} else {
target[key]= source[key];
}
}
}
// 假设有一个用户输入的对象
const userInput = {
"name":"alice",
__proto__: {
isAdmin: true
}
};
// 合并对象时,原型链被污染
merge({}, userInput);
// 所有对象都会继承这个isAdmin属性
console.log({}.isAdmin); // 输出: undefined
实际上Object输出的是undefined
,这是因为,我们用lavaScript创建userInput的过程const userInput = { ... }
中,__proto__
已经代表userInput的原型了,
此时遍历userInput的所有键名,你拿到的是[name]
,__proto__
并不是一个key自然也不会修改Object的原型。
现在将代码改成:
let userInput =JSON.parse('{"name":"alice","__proto__":{"isAdmin":true}}')
// 合并对象,导致原型链污染
merge({}, userInput);
// 正常来说所有对象都会继承这个isAdmin属性
console.log({}.isAdmin); // 输出: true
这样的话alice对象的原型就被污染了,所有继承自Object.prototype的对象都将具有isAdmin属性 。
总结
系列文章
蓝桥杯-网络安全比赛(5)基础学习-JavaScript原型链
蓝桥杯-网络安全比赛(4)基础学习-JavaScript同步与异步、宏任务与微任务
蓝桥杯-网络安全比赛(3)基础学习- JavaScript的闭包与this
蓝桥杯-网络安全比赛(2)基础学习-正则表达式匹配
蓝桥杯-网络安全比赛(1)基础学习-使用PHP、PYTHON、JAVA