當(dāng)前位置:首頁(yè) > IT技術(shù) > Web編程 > 正文

JS原型鏈污染(0)
2022-04-29 13:59:06

參考文檔

https://xz.aliyun.com/t/10032

https://www.cnblogs.com/jofun/p/8727814.html

https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html

原型鏈繼承

函數(shù)對(duì)象和實(shí)例對(duì)象

javascript中沒有類的概念,但是,它的很多“面向?qū)ο蟆辈僮骱湍切┯蓄惖恼Z言差不多。這是通過“原型對(duì)象”和“原型鏈”實(shí)現(xiàn)的。

javascript中的對(duì)象分為函數(shù)對(duì)象(構(gòu)造函數(shù))實(shí)例對(duì)象兩類。

以下是一個(gè)函數(shù)對(duì)象的例子:

function wow(){
	this.a=1;
    this.b=2;
}

使用console.log();作用于原型對(duì)象的時(shí)候,它會(huì)直接打印出它的代碼。

console.log(wow);
? wow(){
	this.a=1;
    this.b=2;
}

以下是一些實(shí)例對(duì)象的例子:

var a=["hidden_er","onequiz","sjh"];
//默認(rèn)構(gòu)造方式,由 Array() 構(gòu)造,構(gòu)造函數(shù)自帶。(數(shù)組)

let o=new f();
//一般構(gòu)造方式,由上面那個(gè)函數(shù)對(duì)wow()構(gòu)造,構(gòu)造函數(shù)自定。

object1={"a":1,"b":2};
//JSON構(gòu)造方式,解析成鍵值對(duì)的形式,允許嵌套。由Object()構(gòu)造。

使用console.log();作用于實(shí)例對(duì)象的時(shí)候,它會(huì)打印出一堆層次性結(jié)構(gòu)。

原型對(duì)象

原型對(duì)象是實(shí)例對(duì)象,而不是函數(shù)對(duì)象。

原型對(duì)象null是萬物之源,即js原型繼承鏈的源頭。null對(duì)象沒有任何屬性。

我們介紹或熟知的js對(duì)象聲明方式無法直接聲明原型對(duì)象;原型對(duì)象是js自己創(chuàng)建的,通過一些屬性和我們創(chuàng)建的對(duì)象相關(guān)聯(lián)。

關(guān)鍵屬性

prototype:指向其對(duì)應(yīng)的原型對(duì)象。只有函數(shù)對(duì)象才擁有這個(gè)屬性。

你每通過代碼聲明一個(gè)構(gòu)造函數(shù),js都會(huì)自動(dòng)生成一個(gè)它的原型對(duì)象,并且在你的函數(shù)對(duì)象中添加該屬性。

_proto_屬性:指向它的構(gòu)造函數(shù)的原型對(duì)象。只有實(shí)例對(duì)象才擁有這個(gè)屬性。

constructor屬性:指向它的構(gòu)造函數(shù)本身。任何對(duì)象都有這個(gè)屬性(因?yàn)槿魏螌?duì)象都有相應(yīng)的構(gòu)造函數(shù))。

需要注意的是,構(gòu)造函數(shù)是函數(shù)對(duì)象,它有prototype屬性;而prototype原型對(duì)象中的constructor又指向由它的構(gòu)造函數(shù)本身。這是個(gè)套娃。

[[prototype]]:我覺得這玩意就是_proto_

證據(jù):

函數(shù)對(duì)象Object

在上文中,我們已經(jīng)知道,原型對(duì)象的萬物之源是null。

但這實(shí)際上還不夠;實(shí)際上,null沒有直接作為任何構(gòu)造函數(shù)的原型對(duì)象。我們獲取null的最快途徑是Object.prototype._proto_ ;即,構(gòu)造函數(shù)Object的原型對(duì)象的構(gòu)造函數(shù)的原型對(duì)象 是null。

(構(gòu)造函數(shù)本身屬性我不會(huì)讀取,要通過其原型對(duì)象的constructor屬性讀??;而null沒有這玩意,所以 構(gòu)造函數(shù)Object的原型對(duì)象的構(gòu)造函數(shù) 這一層實(shí)際上也是沒有東西的。)

這里不需要再深入理解了;我們只需要記住,構(gòu)造函數(shù)的萬物之源是Object,找到了Object就相當(dāng)于找到了null。(實(shí)際上找null也沒用,我們以后最深的地方也就是找Object)

還需要說明的是,以JSON生成的對(duì)象直接繼承于Object,數(shù)組繼承于Array-->Object,函數(shù)繼承于Function-->Object。這些都是JS自帶的原型對(duì)象。

總結(jié)0

在之后的利用中,

如果初始對(duì)象是函數(shù)對(duì)象,那就用一次prototype,然后開始_proto_一直向上跑;

如果初始對(duì)象是實(shí)例對(duì)象,那就直接_proto_一直向上跑。

總結(jié)1**

圖中黑字部分是原型鏈的核心部分;其中,原型對(duì)象的源頭是原型對(duì)象null(也可以理解為Object原型對(duì)象),構(gòu)造函數(shù)(函數(shù)對(duì)象)的源頭是構(gòu)造函數(shù)Function。

藍(lán)色部分是最初的外延擴(kuò)展部分。其中,JSON對(duì)象是最特殊的,它直接與Object原型對(duì)象和構(gòu)造函數(shù)Object相關(guān);而數(shù)組相關(guān)的內(nèi)容實(shí)際上只需要理解為右邊內(nèi)容的特例,即,JS已經(jīng)寫好了相應(yīng)的構(gòu)造函數(shù)(并生成了相應(yīng)原型對(duì)象),不需要我們自己去寫。自定義構(gòu)造函數(shù)的對(duì)象可以一直繼承,從而把原型鏈一直延長(zhǎng)下去。

箭頭標(biāo)注了這些東西相互之間的溝通情況及溝通方式。實(shí)例對(duì)象(包括但不限于原型對(duì)象)僅有1、2兩種屬性,而構(gòu)造函數(shù)(函數(shù)對(duì)象)有1、2、3三種屬性。

關(guān)于prototype

以上的很多分析實(shí)際上我們都不需要記;只需要記住一點(diǎn):用prototype就是為了防止每次new的時(shí)候都運(yùn)行一次方法。

具體的解釋,就是:在有的面向?qū)ο缶幊陶Z言中,對(duì)象的方法肯定寫在類里面,而不是寫在構(gòu)造方法里面。而JS沒有的概念,表面看起來想生成對(duì)象只能使用構(gòu)造方法,于是只能把對(duì)象的方法寫在構(gòu)造函數(shù)里面。這就導(dǎo)致,每生成一個(gè)對(duì)象,對(duì)象方法就會(huì)被執(zhí)行一次。我們往往不想這樣,所以,我們可以把對(duì)象方法寫入原型對(duì)象中,來避免這種情況。

//不使用prototype,每生成一個(gè)對(duì)象,都必須調(diào)用方法show。
function Foo() {
    this.bar = 1
    this.show = function() {
        console.log(this.bar)
    }
}
//使用prototype,避免了上述的尷尬情況
function Foo() {
    this.bar = 1
}

Foo.prototype.show = function show() {
    console.log(this.bar)
}

注意

原型鏈繼承 只是JS對(duì)象繼承中的一種方法;實(shí)際上,還有很多種方法可以實(shí)現(xiàn)繼承,具體可參考 參考文檔。

原型鏈污染

JS對(duì)象的屬性檢索

JavaScript 對(duì)象有一個(gè)指向一個(gè)原型對(duì)象的鏈。當(dāng)試圖訪問一個(gè)對(duì)象的屬性時(shí),它不僅僅在該對(duì)象上搜尋,還會(huì)搜尋該對(duì)象的原型,以及該對(duì)象的原型的原型,依次層層向上搜索,直到找到一個(gè)名字匹配的屬性或到達(dá)原型鏈的末尾。

看一些樣例:

//原型鏈繼承與修改
var Parent=function(){
	this.parent="this is an attribute in Parent";
}
var Children=function(){
    this.children="this is an attribute in Children";
}
Children.prototype=new Parent();//原型鏈繼承的關(guān)鍵語句,把Children對(duì)應(yīng)的原型對(duì)象接到了Parent對(duì)應(yīng)的原型對(duì)象的前面。

//使用上述兩個(gè)函數(shù)對(duì)象分別創(chuàng)建實(shí)例對(duì)象
var a=new Children();
var b=new Parent();
console.log(a.children);

//由于原型鏈繼承,子實(shí)例對(duì)象具有父親的屬性。
console.log(a.parent);

//修改子實(shí)例對(duì)象的值;對(duì)其他任何東西都沒有影響。
a.parent='???';
var c=new Children();
console.log(c.parent);

//修改父實(shí)例對(duì)象的值;對(duì)其他任何東西也沒有影響。
b.parent='???';
console.log(c.parent);

//使用原型鏈;此處修改就是原型對(duì)象了,而不是實(shí)例對(duì)象。會(huì)對(duì)所有以 該原型對(duì)象對(duì)應(yīng)的函數(shù)對(duì)象 生成的實(shí)例對(duì)象產(chǎn)生影響,??!包含已經(jīng)實(shí)例化了的??!
a.__proto__.parent='???';
console.log(c.parent);

//不過,它不能對(duì)子類原有的屬性產(chǎn)生影響;因?yàn)榧词乖诟割愒蛯?duì)象里修改了該屬性,在子類構(gòu)造方法里,它也會(huì)被覆蓋為正確的值。
a.__proto__.children='?????';
console.log(c.children);

回顯:

this is an attribute in Children
this is an attribute in Parent
this is an attribute in Parent
this is an attribute in Parent
???
this is an attribute in Children

也就是說,我們只要能控制一個(gè)子實(shí)例對(duì)象的內(nèi)容(屬性),就可以控制所有其祖先原型對(duì)象,以及用它的祖先原型對(duì)象生成的實(shí)例對(duì)象。

這是一個(gè)比較高的權(quán)限;很容易產(chǎn)生漏洞。

一個(gè)遺留問題

var Parent=function(){
	this.parent="this is an attribute in Parent";
}
var Children=function(){
    this.children="this is an attribute in Children";
}
Children.prototype=new Parent();
var a=new Children();
console.log(a.children);
console.log(a.constructor);
console.log(a.__proto__.constructor);
console.log(a.__proto__.__proto__.constructor);
console.log(a.__proto__.constructor.__proto__.constructor);
console.log(a.__proto__.__proto__.__proto__.constructor);

回顯:

this is an attribute in Children
? (){
	this.parent="this is an attribute in Parent";
}
? (){
	this.parent="this is an attribute in Parent";
}
? (){
	this.parent="this is an attribute in Parent";
}
? Function() { [native code] } //構(gòu)造函數(shù)Function
? Object() { [native code] } //構(gòu)造函數(shù)Object

我對(duì)于第二個(gè)回顯很不理解,其他幾個(gè)回顯也沒完全搞清楚。

實(shí)際污染樣例01--Merge()

function merge(target,source){
    for (let key in source){
        console.log('key is: ',key,' ',source[key]);
        if (key in source && key in target){
            merge(target[key],source[key])
        }
        else{
            target[key]=source[key]
        }
    }
}
let object1={}
let object2=JSON.parse('{"a":1,"__proto__":{"b":2}}')
console.log(object2)
merge(object1,object2)
console.log(object1.a,object1.b)
object3={}
console.log(object3)

在實(shí)際環(huán)境中,我們能夠得到的權(quán)限往往比之前所述的要少。一般來說,我們最多有任意傳參(JSON格式對(duì)象)的權(quán)限,而不能像之前那樣直接對(duì) 對(duì)象相關(guān)屬性 進(jìn)行命令執(zhí)行。

此處能夠進(jìn)行原型鏈污染的核心原因在于JSON和JS本身解析方式的不同:我們傳入的參數(shù),在經(jīng)過JSON解析后,"__proto__"被認(rèn)為是一個(gè)沒有特殊意義的普通的鍵名(就跟"a"一樣);但是,在Merge函數(shù)中,key掃到"__proto__"時(shí),執(zhí)行的target[key]=source[key]命令就變成了object1.__proto__=object2.__proto__,導(dǎo)致Object原型函數(shù)被修改,進(jìn)而影響了所有實(shí)例對(duì)象。

本文摘自 :https://www.cnblogs.com/

開通會(huì)員,享受整站包年服務(wù)立即開通 >