0x00 前言 大家好,我們是OpenCDN團(tuán)隊(duì)的Twwy。這次我們來(lái)講講如何通過(guò)簡(jiǎn)單的配置文件來(lái)實(shí)現(xiàn)nginx防御攻擊的效果。 其實(shí)很多時(shí)候,各種防攻擊的思路我們都明白,比如限制IP啊,過(guò)濾攻擊字符串啊,識(shí)別攻擊指紋啦。可是要如何去實(shí)現(xiàn)它呢?用守護(hù)腳本嗎?用PHP在外面包 一層過(guò)濾?還是直接加防火墻嗎?這些都是防御手段。不過(guò)本文將要介紹的是直接通過(guò)nginx的普通模塊和配置文件的組合來(lái)達(dá)到一定的防御效果。 0x01 驗(yàn)證瀏覽器行為 簡(jiǎn)易版 我們先來(lái)做個(gè)比喻。 社區(qū)在搞福利,在廣場(chǎng)上給大家派發(fā)紅包。而壞人派了一批人形的機(jī)器人(沒(méi)有語(yǔ)言模塊)來(lái)冒領(lǐng)紅包,聰明工作人員需要想出辦法來(lái)防止紅包被冒領(lǐng)。 于是工作人員在發(fā)紅包之前,會(huì)給領(lǐng)取者一張紙,上面寫(xiě)著“紅包拿來(lái)”,如果那人能念出紙上的字,那么就是人,給紅包,如果你不能念出來(lái),那么請(qǐng)自覺(jué)。于是機(jī)器人便被識(shí)破,灰溜溜地回來(lái)了。 是的,在這個(gè)比喻中,人就是瀏覽器,機(jī)器人就是攻擊器,我們可以通過(guò)鑒別cookie功能(念紙上的字)的方式來(lái)鑒別他們。下面就是nginx的配置文件寫(xiě)法。
讓我們看下這幾行的意思,當(dāng)cookie中say為空時(shí),給一個(gè)設(shè)置cookie say為hbnl的302重定向包,如果訪問(wèn)者能夠在第二個(gè)包中攜帶上cookie值,那么就能正常訪問(wèn)網(wǎng)站了,如果不能的話(huà),那他永遠(yuǎn)活在了302中。你也可以測(cè)試一下,用CC攻擊器或者webbench或者直接curl發(fā)包做測(cè)試,他們都活在了302世界中。 當(dāng)然,這么簡(jiǎn)單就能防住了?當(dāng)然沒(méi)有那么簡(jiǎn)單。 增強(qiáng)版 仔細(xì)的你一定會(huì)發(fā)現(xiàn)配置文件這樣寫(xiě)還是有缺陷。如果攻擊者設(shè)置cookie為say=hbnl(CC攻擊器上就可以這么設(shè)置),那么這個(gè)防御就形同虛設(shè)了。我們繼續(xù)拿剛剛那個(gè)比喻來(lái)說(shuō)明問(wèn)題。 壞人發(fā)現(xiàn)這個(gè)規(guī)律后,給每個(gè)機(jī)器人安上了揚(yáng)聲器,一直重復(fù)著“紅包拿來(lái),紅包拿來(lái)”,浩浩蕩蕩地又來(lái)領(lǐng)紅包了。 這時(shí),工作人員的對(duì)策是這樣做的,要求領(lǐng)取者出示有自己名字的戶(hù)口本,并且念出自己的名字,“我是xxx,紅包拿來(lái)”。于是一群只會(huì)嗡嗡叫著“紅包拿來(lái)”的機(jī)器人又被攆回去了。 當(dāng)然,為了配合說(shuō)明問(wèn)題,每個(gè)機(jī)器人是有戶(hù)口本的,被趕回去的原因是不會(huì)念自己的名字,雖然這個(gè)有點(diǎn)荒誕,唉。 然后,我們來(lái)看下這種方式的配置文件寫(xiě)法
這樣的寫(xiě)法和前面的區(qū)別是,不同IP的請(qǐng)求cookie值是不一樣的,比如IP是1.2.3.4,那么需要設(shè)置的cookie是say=hbnl1.2.3.4。于是攻擊者便無(wú)法通過(guò)設(shè)置一樣的cookie(比如CC攻擊器)來(lái)繞過(guò)這種限制。你可以繼續(xù)用CC攻擊器來(lái)測(cè)試下,你會(huì)發(fā)現(xiàn)CC攻擊器打出的流量已經(jīng)全部進(jìn)入302世界中。 不過(guò)大家也能感覺(jué)到,這似乎也不是一個(gè)萬(wàn)全之計(jì),因?yàn)楣粽呷绻芯苛司W(wǎng)站的機(jī)制之后,總有辦法測(cè)出并預(yù)先偽造cookie值的設(shè)置方法。因?yàn)槲覀冏霾町惢臄?shù)據(jù)源正是他們本身的一些信息(IP、user agent等)。攻擊者花點(diǎn)時(shí)間也是可以做出專(zhuān)門(mén)針對(duì)網(wǎng)站的攻擊腳本的。 完美版 那么要如何根據(jù)他們自身的信息得出他們又得出他們算不出的數(shù)值? 我想,聰明的你一定已經(jīng)猜到了,用salt加散列。比如md5("opencdn$remote_addr"),雖然攻擊者知道可以自己IP,但是他無(wú)法得知如何用他的IP來(lái)計(jì)算出這個(gè)散列,因?yàn)樗悄娌怀鲞@個(gè)散列的。當(dāng)然,如果你不放心的話(huà),怕cmd5.com萬(wàn)一能查出來(lái)的話(huà),可以加一些特殊字符,然后多散幾次。 很可惜,nginx默認(rèn)是無(wú)法進(jìn)行字符串散列的,于是我們借助nginx_lua模塊來(lái)進(jìn)行實(shí)現(xiàn)。
通過(guò)這樣的配置,攻擊者便無(wú)法事先計(jì)算這個(gè)cookie中的say值,于是攻擊流量(代理型CC和低級(jí)發(fā)包型CC)便在302地獄無(wú)法自拔了。 大家可以看到,除了借用了md5這個(gè)函數(shù)外,其他的邏輯和上面的寫(xiě)法是一模一樣的。因此如果可以的話(huà),你完全可以安裝一個(gè)nginx的計(jì)算散列的第三方模塊來(lái)完成,可能效率會(huì)更高一些。 這段配置是可以被放在任意的location里面,如果你的網(wǎng)站有對(duì)外提供API功能的話(huà),建議API一定不能加入這段,因?yàn)锳PI的調(diào)用也是沒(méi)有瀏覽器行為的,會(huì)被當(dāng)做攻擊流量處理。并且,有些弱一點(diǎn)爬蟲(chóng)也會(huì)陷在302之中,這個(gè)需要注意。 同時(shí),如果你覺(jué)得set-cookie這個(gè)動(dòng)作似乎攻擊者也有可能通過(guò)解析字符串模擬出來(lái)的話(huà),你可以把上述的通過(guò)header來(lái)設(shè)置cookie的操作,變成通過(guò)高端大氣的js完成,發(fā)回一個(gè)含有doument.cookie=...的文本即可。 那么,攻擊是不是完全被擋住了呢?只能說(shuō)那些低級(jí)的攻擊已經(jīng)被擋住而來(lái),如果攻擊者必須花很大代價(jià)給每個(gè)攻擊器加上webkit模塊來(lái)解析js和執(zhí)行set-cookie才行,那么他也是可以逃脫302地獄的,在nginx看來(lái),確實(shí)攻擊流量和普通瀏覽流量是一樣的。那么如何防御呢?下節(jié)會(huì)告訴你答案。 0x02 請(qǐng)求頻率限制 不得不說(shuō),很多防CC的措施是直接在請(qǐng)求頻率上做限制來(lái)實(shí)現(xiàn)的,但是,很多都存在著一定的問(wèn)題。 那么是哪些問(wèn)題呢? 首先,如果通過(guò)IP來(lái)限制請(qǐng)求頻率,容易導(dǎo)致一些誤殺,比如我一個(gè)地方出口IP就那么幾個(gè),而訪問(wèn)者一多的話(huà),請(qǐng)求頻率很容易到上限,那么那個(gè)地方的用戶(hù)就都訪問(wèn)不了你的網(wǎng)站了。 于是你會(huì)說(shuō),我用SESSION來(lái)限制就有這個(gè)問(wèn)題了。嗯,你的SESSION為攻擊者敞開(kāi)了一道大門(mén)。為什么呢?看了上文的你可能已經(jīng)大致知道了,因?yàn)榫拖衲莻€(gè)“紅包拿來(lái)”的揚(yáng)聲器一樣,很多語(yǔ)言或者框架中的SESSION是能夠偽造的。以PHP為例,你可以在瀏覽器中的cookie看到PHPSESSIONID,這個(gè)ID不同的話(huà),session也就不同了,然后如果你杜撰一個(gè)PHPSESSIONID過(guò)去的話(huà),你會(huì)發(fā)現(xiàn),服務(wù)器也認(rèn)可了這個(gè)ID,為這個(gè)ID初始化了一個(gè)會(huì)話(huà)。那么,攻擊者只需要每次發(fā)完包就構(gòu)造一個(gè)新的SESSIONID就可以很輕松地躲過(guò)這種在session上的請(qǐng)求次數(shù)限制。 那么我們要如何來(lái)做這個(gè)請(qǐng)求頻率的限制呢? 首先,我們先要一個(gè)攻擊者無(wú)法杜撰的sessionID,一種方式是用個(gè)池子記錄下每次給出的ID,然后在請(qǐng)求來(lái)的時(shí)候進(jìn)行查詢(xún),如果沒(méi)有的話(huà),就拒絕請(qǐng)求。這種方式我們不推薦,首先一個(gè)網(wǎng)站已經(jīng)有了session池,這樣再做個(gè)無(wú)疑有些浪費(fèi),而且還需要進(jìn)行池中的遍歷比較查詢(xún),太消耗性能。我們希望的是一種可以無(wú)狀態(tài)性的sessionID,可以嗎?可以的。
大家是不是覺(jué)得好像有些眼熟?是的,這個(gè)就是上節(jié)的完美版的配置再加個(gè)隨機(jī)數(shù),為的是讓同一個(gè)IP的用戶(hù)也能有不同的token。同樣的,只要有nginx的第三方模塊提供散列和隨機(jī)數(shù)功能,這個(gè)配置也可以不用lua直接用純配置文件完成。 有了這個(gè)token之后,相當(dāng)于每個(gè)訪客有一個(gè)無(wú)法偽造的并且獨(dú)一無(wú)二的token,這種情況下,進(jìn)行請(qǐng)求限制才有意義。 由于有了token做鋪墊,我們可以不做什么白名單、黑名單,直接通過(guò)limit模塊來(lái)完成。 然后我們只需要在上面的token配置后面中加入
于是,又是兩行配置便讓nginx在session層解決了請(qǐng)求頻率的限制。不過(guò)似乎還是有缺陷,因?yàn)楣粽呖梢酝ㄟ^(guò)一直獲取token來(lái)突破請(qǐng)求頻率限制,如果能限制一個(gè)IP獲取token的頻率就更完美了??梢宰龅絾幔靠梢?。
我想大家也應(yīng)該已經(jīng)猜到,這段配置文件的原理就是:把本來(lái)的發(fā)token的功能分離到一個(gè)auth頁(yè)面,然后用limit對(duì)這個(gè)auth頁(yè)面進(jìn)行頻率限制即可。這邊的頻率是1個(gè)IP每分鐘授權(quán)1個(gè)token。當(dāng)然,這個(gè)數(shù)量可以根據(jù)業(yè)務(wù)需要進(jìn)行調(diào)整。 需要注意的是,這個(gè)auth部分我lua采用的是access_by_lua,原因在于limit模塊是在rewrite階段后執(zhí)行的,如果在rewrite階段302的話(huà),limit將會(huì)失效。因此,這段lua配置我不能保證可以用原生的配置文件實(shí)現(xiàn),因?yàn)椴恢廊绾斡门渲梦募趓ewrite階段后進(jìn)行302跳轉(zhuǎn),也求大牛能夠指點(diǎn)一下啊。 當(dāng)然,你如果還不滿(mǎn)足于這種限制的話(huà),想要做到某個(gè)IP如果一天到達(dá)上限超過(guò)幾次之后就直接封IP的話(huà),也是可以的,你可以用類(lèi)似的思路再做個(gè)錯(cuò)誤頁(yè)面,然后到達(dá)上限之后不返回503而是跳轉(zhuǎn)到那個(gè)錯(cuò)誤頁(yè)面,然后錯(cuò)誤頁(yè)面也做個(gè)請(qǐng)求次數(shù)限制,比如每天只能訪問(wèn)100次,那么當(dāng)超過(guò)報(bào)錯(cuò)超過(guò)100次(請(qǐng)求錯(cuò)誤頁(yè)面100次)之后,那天這個(gè)IP就不能再訪問(wèn)這個(gè)網(wǎng)站了。 于是,通過(guò)這些配置我們便實(shí)現(xiàn)了一個(gè)網(wǎng)站訪問(wèn)頻率限制。不過(guò),這樣的配置也不是說(shuō)可以完全防止了攻擊,只能說(shuō)讓攻擊者的成本變高,讓網(wǎng)站的扛攻擊能力變強(qiáng),當(dāng)然,前提是nginx能夠扛得住這些流量,然后帶寬不被堵死。如果你家門(mén)被堵了,你還想開(kāi)門(mén)營(yíng)業(yè),那真心沒(méi)有辦法了。 然后,做完流量上的防護(hù),讓我們來(lái)看看對(duì)于掃描器之類(lèi)的攻擊的防御。 0x03 防掃描 ngx_lua_waf模塊 這個(gè)是一個(gè)不錯(cuò)的waf模塊,這塊我們也就不再重復(fù)造輪子了??梢灾苯佑眠@個(gè)模塊來(lái)做防護(hù),當(dāng)然也完全可以再配合limit模塊,用上文的思路來(lái)做到一個(gè)封IP或者封session的效果。 0x04 總結(jié) 本文旨在達(dá)到拋磚引玉的作用,我們并不希望你直接單純的復(fù)制我們的這些例子中的配置,而是希望根據(jù)你的自身業(yè)務(wù)需要,寫(xiě)出適合自身站點(diǎn)的配置文件。 【源文地址】 本文地址:http://www./30/1255.html如非注明則為本站原創(chuàng)文章,歡迎轉(zhuǎn)載。轉(zhuǎn)載請(qǐng)注明轉(zhuǎn)載自:moon's blog |
|