前面的話:本人項(xiàng)目實(shí)際遇上并解決的問題青团,可能寫得不夠完善,求大神給個(gè)裝X的機(jī)會(huì)芦昔,也希望能夠給未遇到過此問題的后來者一點(diǎn)提點(diǎn)!
正文:?jiǎn)栴}是在前一段時(shí)間調(diào)用一個(gè)第三方網(wǎng)站的接口(A接口)時(shí)遇上的娃肿。
A接口是一個(gè)jsonp調(diào)用的接口,當(dāng)前使用get請(qǐng)求時(shí)凭豪,會(huì)返回一個(gè)js函數(shù)調(diào)用晒杈,由于我使用的是Node.js的http原生庫(kù)進(jìn)行封裝的請(qǐng)求庫(kù),默認(rèn)是不會(huì)自動(dòng)處理jsonp的返回?cái)?shù)據(jù)末早,所以得自行進(jìn)行解析说庭。
如,以下是某次請(qǐng)求返回的消息體
var body = `_jsInvoke({
"errcode": 1000,
"errmsg": "Invalid request trace id.",
"data": [],
"datetime": "2018-05-02 15:22:53"
})`
于是姿搜,出于一般的思維,我們就使用了new Function進(jìn)行處理返回的消息體梭纹,代碼如下:
var obj = (new Function(`function _jsInvoke(obj) {return obj} return ${body}`))();
Then? 代碼正常執(zhí)行致份,結(jié)果成功返回,沒有任何問題绍载,如些正常運(yùn)行了半個(gè)月滔蝉,直到這一天,我們突然發(fā)現(xiàn)在pm2 日志里面突然多次出現(xiàn)一些問題日志阳谍。
[2018-05-10 12:56:23] [Laravel Req Error] 非法請(qǐng)求螃概,已拒絕連接!
[2018-05-12 08:41:50] [Laravel Req Error] 訂單號(hào)出錯(cuò)!
[2018-05-16 17:32:56] [Laravel Req Error] IP黑名單已滿!
問題來了時(shí),當(dāng)時(shí)最受關(guān)注的是茧痒,在一個(gè)pm2運(yùn)行的Node.js項(xiàng)目里面融蹂,怎么會(huì)有Laravel的日志?因?yàn)樵趐m2中的log日志区拳,是不會(huì)顯示出錯(cuò)棧跟蹤的意乓,所以,我們只能做一些日志跟蹤測(cè)試笆凌,(跟蹤過程省略......)
終于士葫,在5月22日早上,奇怪的pm2日志再次出現(xiàn)爪模,而這次我們就迅速定位到上面的用到的new Function處理方法中,而這些錯(cuò)誤的pm2日志洁段,正是從里面打印出來的共郭,
奇怪?
new Function里面沒有console.log芭ε薄憾赁?
除非散吵,console.log被調(diào)用了龙考,不可能打印出log啊?
對(duì),console.log被調(diào)用了矾睦,console.log被調(diào)用了晦款,console.log被調(diào)用了...
好恐怖的現(xiàn)象......現(xiàn)在想起來還有點(diǎn)后怕,幸虧沒造成什么損失!
以下枚冗,我們來分析一下缓溅,問題是怎樣產(chǎn)生的,這個(gè)現(xiàn)象會(huì)造成什么嚴(yán)重效果赁温。
我們都知道:
- jsonp請(qǐng)求返回的是一個(gè)javascript腳本字符串坛怪,并且響應(yīng)頭為Content-Type:text/javascript;
- new Function 方法的調(diào)用,可以把javascript腳本字符串翻譯為javascript語(yǔ)法進(jìn)行執(zhí)行股囊;
- node.js的變量使用或才函數(shù)調(diào)用袜匿,如果沒有嚴(yán)格規(guī)定作用域稚疹,則會(huì)一層層往上追溯到最頂層的對(duì)象global中;
所以為什么之前剛接入接口是居灯,能正常執(zhí)行,是因?yàn)榻涌诜祷氐臄?shù)據(jù)正好是符合我們需要的規(guī)范内狗,所以return之后能夠把obj當(dāng)成js對(duì)象返回怪嫌。
但后來,返回的數(shù)據(jù)變成了如下:
var body = `console.log("這里是錯(cuò)誤信息!")`
則new Function里面的字符串就變成了如下:
var obj = (new Function(`function _jsInvoke(obj) {return obj} return console.log("這里是錯(cuò)誤信息!")`))();
于時(shí)柳沙,console.log就被調(diào)用了!
深入一下:
如果請(qǐng)求響應(yīng)數(shù)據(jù)是這樣子:
var body = `(function() {
while(true){}
})()`
那么當(dāng)前進(jìn)程就一直處于死循環(huán)中岩灭,直到資源耗盡,進(jìn)程崩潰偎行!如果對(duì)一個(gè)正在線上運(yùn)行的項(xiàng)目川背,這是致命的贰拿!
再深入一下:
如果請(qǐng)求響應(yīng)數(shù)據(jù)是這樣子:
var body = `this.constructor.constructor("return require('child_process')")()`
呵呵,這樣子處理的數(shù)據(jù)就可以逃逸出去熄云,嚴(yán)重的話膨更,服務(wù)器就GG了。
所以缴允,在任何第三請(qǐng)求以及回調(diào)中荚守,一定要經(jīng)過嚴(yán)格的數(shù)據(jù)處理,不然最后自己的服務(wù)器被奪走了练般,也沒發(fā)現(xiàn)問題!