<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Lodash 嚴(yán)重安全漏洞背后 你不得不知道的 JavaScript 知識(shí)

          共 5597字,需瀏覽 12分鐘

           ·

          2020-09-29 02:31

          作者:Lucas HC

          原文:https://zhuanlan.zhihu.com/p/73186974

          可能有信息敏感的同學(xué)已經(jīng)了解到:Lodash 庫爆出嚴(yán)重安全漏洞,波及 400萬+ 項(xiàng)目。這個(gè)漏洞使得 lodash “連夜”發(fā)版以解決潛在問題,并強(qiáng)烈建議開發(fā)者升級(jí)版本。

          我們?cè)诿χ翱礋狒[”或者“”升級(jí)版本”的同時(shí),靜下心來想:真的有理解這個(gè)漏洞產(chǎn)生的原因,明白漏洞修復(fù)背后的原理了嗎?

          這篇短文將從原理層面分析這一事件,相信“小白”讀者會(huì)有所收獲。

          漏洞原因

          其實(shí)漏洞很簡(jiǎn)單,舉一個(gè)例子:lodash 中 defaultsDeep 方法,

          _.defaultsDeep({?'a':?{?'b':?2?}?},?{?'a':?{?'b':?1,?'c':?3?}?})

          輸出:

          {?'a':?{?'b':?2,?'c':?3?}?}

          如上例,該方法:

          分配來源對(duì)象(該方法的第二個(gè)參數(shù))的可枚舉屬性到目標(biāo)對(duì)象(該方法的第一個(gè)參數(shù))所有解析為 undefined 的屬性上

          這樣的操作存在的隱患:

          const?payload?=?'{"constructor":?{"prototype":?{"toString":?true}}}'

          _.defaultsDeep({},?JSON.parse(payload))

          如此一來,就觸發(fā)了原型污染。原型污染是指:

          攻擊者通過某種手段修改 JavaScript 對(duì)象的原型(prototype)

          對(duì)應(yīng)上例,Object.prototype.toString 就會(huì)非常不安全了。

          詳解原型污染

          理解原型污染,需要讀者理解 JavaScript 當(dāng)中的原型、原型鏈的知識(shí)。我們先來看一個(gè)例子:

          //?person?是一個(gè)簡(jiǎn)單的?JavaScript?對(duì)象
          let?person?=?{name:?'lucas'}

          //?輸出?lucas
          console.log(person.name)

          //?修改?person?的原型
          person.__proto__.name?=?'messi'

          //?由于原型鏈順序查找的原因,person.name?仍然是?lucas
          console.log(person.name)

          //?再創(chuàng)建一個(gè)空的?person2?對(duì)象
          let?person2?=?{}

          //?查看?person2.name,輸出?messi
          console.log(person2.name)

          把危害擴(kuò)大化:

          let?person?=?{name:?'lucas'}

          console.log(person.name)

          person.__proto__.toString?=?()?=>?{alert('evil')}

          console.log(person.name)

          let?person2?=?{}

          console.log(person2.toString())

          這段代碼執(zhí)行將會(huì) alert 出 evil 文字。同時(shí) Object.prototype.toString 這個(gè)方法會(huì)在隱式轉(zhuǎn)換以及類型判斷中經(jīng)常被用到:

          Object.prototype.toString 方法返回一個(gè)表示該對(duì)象的字符串

          每個(gè)對(duì)象都有一個(gè) toString() 方法,當(dāng)該對(duì)象被表示為一個(gè)文本值時(shí),或者一個(gè)對(duì)象以預(yù)期的字符串方式引用時(shí)自動(dòng)調(diào)用。默認(rèn)情況下,toString() 方法被每個(gè) Object 對(duì)象繼承。如果此方法在自定義對(duì)象中未被覆蓋,toString() 返回 [object type],其中 type 是對(duì)象的類型。

          如果 Object 原型上的 toString 被污染,后果可想而知。以此為例,可見 lodash 這次漏洞算是比較嚴(yán)重了。

          再談原型污染(NodeJS 漏洞案例)

          由上分析,我們知道原型污染并不是什么新鮮的漏洞,它“隨時(shí)可見”,“隨處可見”。在 Nullcon HackIM 比賽中就有一個(gè)類似的 hack 題目:

          'use?strict';
          ?
          const?express?=?require('express');
          const?bodyParser?=?require('body-parser')
          const?cookieParser?=?require('cookie-parser');
          const?path?=?require('path');
          ?
          const?isObject?=?obj?=>?obj?&&?obj.constructor?&&?obj.constructor?===?Object;
          ?
          function?merge(a,?b)?{
          ????for?(var?attr?in?b)?{
          ????????if?(isObject(a[attr])?&&?isObject(b[attr]))?{
          ????????????merge(a[attr],?b[attr]);
          ????????}?else?{
          ????????????a[attr]?=?b[attr];
          ????????}
          ????}
          ????return?a
          }
          ?
          function?clone(a)?{
          ????return?merge({},?a);
          }
          ?
          //?Constants
          const?PORT?=?8080;
          const?HOST?=?'0.0.0.0';
          const?admin?=?{};
          ?
          //?App
          const?app?=?express();
          app.use(bodyParser.json())
          app.use(cookieParser());
          ?
          app.use('/',?express.static(path.join(__dirname,?'views')));
          app.post('/signup',?(req,?res)?=>?{
          ????var?body?=?JSON.parse(JSON.stringify(req.body));
          ????var?copybody?=?clone(body)
          ????if?(copybody.name)?{
          ????????res.cookie('name',?copybody.name).json({
          ????????????"done":?"cookie?set"
          ????????});
          ????}?else?{
          ????????res.json({
          ????????????"error":?"cookie?not?set"
          ????????})
          ????}
          });
          app.get('/getFlag',?(req,?res)?=>?{
          ????var?аdmin?=?JSON.parse(JSON.stringify(req.cookies))
          ????if?(admin.аdmin?==?1)?{
          ????????res.send("hackim19{}");
          ????}?else?{
          ????????res.send("You?are?not?authorized");
          ????}
          });
          app.listen(PORT,?HOST);
          console.log(`Running?on?http://${HOST}:${PORT}`);

          這段代碼的漏洞就在于 merge 函數(shù)上,我們可以這樣攻擊:

          curl?-vv?--header?'Content-type:?application/json'?-d?'{"__proto__":?{"admin":?1}}'?'http://0.0.0.0:4000/signup';?

          curl?-vv?'http://0.0.0.0:4000/getFlag'

          首先請(qǐng)求 /signup 接口,在 NodeJS 服務(wù)中,我們調(diào)用了有漏洞的 merge 方法,并通過 __proto__Object.prototype(因?yàn)?{}.__proto__ === Object.prototype) 添加上一個(gè)新的屬性 admin,且值為 1。

          再次請(qǐng)求 getFlag 接口,條件語句 admin.аdmin == 1true,服務(wù)被攻擊。

          攻擊案例出自:Prototype pollution attacks in NodeJS applications

          這樣的漏洞在 jQuery $.extend 中也經(jīng)常見到:

          • jQuery 修復(fù)原型污染 PR
          • jQuery prototype pollution vulnerability

          對(duì)于 jQuery:如果擔(dān)心安全問題,建議升級(jí)至最新版本 jQuery 3.4.0,如果還在使用 jQuery 的 1.x 和 2.x 版本,那么你的應(yīng)用程序和網(wǎng)站仍有可能遭受攻擊。

          防范原型污染

          了解了漏洞潛在問題以及攻擊手段,那么如何防范呢?

          在 lodash “連夜”發(fā)版的修復(fù)中:

          我們可以清晰的看到,在遍歷 merge 時(shí),當(dāng)遇見 constructor 以及 __proto__ 敏感屬性,則退出程序。

          那么作為業(yè)務(wù)開發(fā)者,我們需要注意些什么,防止攻擊出現(xiàn)呢?總結(jié)一下有:

          • 凍結(jié) Object.prototype,使原型不能擴(kuò)充屬性

          我們可以采用 Object.freeze 達(dá)到目的:

          Object.freeze() 方法可以凍結(jié)一個(gè)對(duì)象。一個(gè)被凍結(jié)的對(duì)象再也不能被修改;凍結(jié)了一個(gè)對(duì)象則不能向這個(gè)對(duì)象添加新的屬性,不能刪除已有屬性,不能修改該對(duì)象已有屬性的可枚舉性、可配置性、可寫性,以及不能修改已有屬性的值。此外,凍結(jié)一個(gè)對(duì)象后該對(duì)象的原型也不能被修改。freeze() 返回和傳入的參數(shù)相同的對(duì)象。

          看代碼:

          Object.freeze(Object.prototype);

          Object.prototype.toString?=?'evil'

          consoel.log(Object.prototype.toString)
          ??toString()?{?[native?code]?}

          對(duì)比:

          Object.prototype.toString?=?'evil'

          console.log(Object.prototype.toString)
          "evil"
          • 建立 JSON schema
            在解析用戶輸入內(nèi)容是,通過 JSON schema 過濾敏感鍵名。
          • 規(guī)避不安全的遞歸性合并
            這一點(diǎn)類似 lodash 修復(fù)手段,完善了合并操作的安全性,對(duì)敏感鍵名跳過處理
          • 使用無原型對(duì)象
            在創(chuàng)建對(duì)象時(shí),不采用字面量方式,而是使用 Object.create(null)

          Object.create()方法創(chuàng)建一個(gè)新對(duì)象,使用現(xiàn)有的對(duì)象來提供新創(chuàng)建的對(duì)象的__proto__

          Object.create(null) 的返回值不會(huì)鏈接到 Object.prototype

          let?foo?=?Object.create(null)
          console.log(foo.__proto__)
          //?undefined

          這樣一來,無論如何擴(kuò)充對(duì)象,都不會(huì)干擾到原型了。

          • 采用新的 Map 數(shù)據(jù)類型,代替 Object 類型

          Map 對(duì)象保存鍵/值對(duì),是鍵/值對(duì)的集合。任何值(對(duì)象或者原始值)都可以作為一個(gè)鍵或一個(gè)值。使用 Map 數(shù)據(jù)結(jié)構(gòu),不會(huì)存在 Object 原型污染狀況。

          這里總結(jié)一下 Map 和 Object 不同點(diǎn)::

          • Object 的鍵只支持 String 或者 Symbols 兩種類型,Map 的鍵可以是任意值,包括函數(shù)、對(duì)象、基本類型
          • Map 中的鍵值是有序的,而 Object 中的鍵則不是
          • 具體 API 上的差異:比如,通過 size 屬性直接獲取一個(gè) Map 的鍵值對(duì)個(gè)數(shù),而 Object 的鍵值無法獲取;再比如迭代一個(gè) Map 和 Object 差異也比較明顯
          • Map 在頻繁增刪鍵值對(duì)的場(chǎng)景下會(huì)有些性能優(yōu)勢(shì)

          補(bǔ)充:V8,chromium 的小機(jī)靈

          同樣存在風(fēng)險(xiǎn)的是我們常用的 JSON.parse 方法,但是如果你運(yùn)行:

          JSON.parse('{?"a":1,?"__proto__":?{?"b":?2?}}')

          你會(huì)發(fā)現(xiàn)返回的結(jié)果如圖:

          復(fù)寫 Object.prototype 失敗了,__proto__ 屬性還是我們熟悉的那個(gè)有安全感的 __proto__ 。這是因?yàn)椋?/p>

          V8 ignores keys named proto in JSON.parse

          這個(gè)相關(guān)討論 Doug Crockford,Brendan Eich,反正 chromium 和 JS 發(fā)明人討論過很多次。相關(guān) issue 和 PR:

          • chromium 討論
          • chromium 討論

          相關(guān) ES 語言設(shè)計(jì)的討論:ES 語言設(shè)計(jì)的討論:proto-and-json

          在上面鏈接中,你能發(fā)現(xiàn) JavaScript 發(fā)明人等一眾大佬哦~

          總之你可以記住,V8 默認(rèn)使用 JSON.parse 時(shí)候會(huì)忽略 __proto__,原因當(dāng)然是之前分析的安全性了。

          總結(jié)

          通過分析 lodash 的漏洞,以及解決方案,我們了解了原型污染的方方面面。涉及到的知識(shí)點(diǎn)包括但不限于:

          • Object 原型
          • 原型、原型鏈
          • NodeJS 相關(guān)問題
          • Object.create 方法
          • Object.freeze 方法
          • Map 數(shù)據(jù)結(jié)構(gòu)
          • 深拷貝
          • 以及其他問題

          這么來看,全是基礎(chǔ)知識(shí)。也正是基礎(chǔ),構(gòu)成了前端知識(shí)體系的方方面面。



          我是依揚(yáng),螞蟻集團(tuán)-保險(xiǎn)團(tuán)隊(duì)正在大量招聘中,詳情見:我們是螞蟻保險(xiǎn)前端團(tuán)隊(duì),我們今年在做什么,有興趣快來聯(lián)系我吧[email protected]



          》》面試官都在用的題庫,快來看看《

          瀏覽 47
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本在线人妻 | 一区二区三区四区无码 | 国产麻豆剧传媒精品国产 | 99精品在线观看视频 | 国产特级毛片AAAAAA |