透過nginx配置檔案抵禦攻擊
0x00 前言
大家好,我們是OpenCDN團隊的Twwy。這次我們來講講如何透過簡單的配置檔案來實現nginx防禦攻擊的效果。
其實很多時候,各種防攻擊的思路我們都明白,比如限制IP啊,過濾攻擊字串啊,識別攻擊指紋啦。可是要如何去實現它呢?用守護指令碼嗎?用PHP在外面包一層過濾?還是直接加防火牆嗎?這些都是防禦手段。不過本文將要介紹的是直接透過nginx的普通模組和配置檔案的組合來達到一定的防禦效果。
0x01 驗證瀏覽器行為
簡易版
我們先來做個比喻。
社群在搞福利,在廣場上給大家派發紅包。而壞人派了一批人形的機器人(沒有語言模組)來冒領紅包,聰明工作人員需要想出辦法來防止紅包被冒領。
於是工作人員在發紅包之前,會給領取者一張紙,上面寫著“紅包拿來”,如果那人能念出紙上的字,那麼就是人,給紅包,如果你不能念出來,那麼請自覺。於是機器人便被識破,灰溜溜地回來了。
是的,在這個比喻中,人就是瀏覽器,機器人就是攻擊器,我們可以透過鑑別cookie功能(念紙上的字)的方式來鑑別他們。下面就是nginx的配置檔案寫法。
#!shell
if ($cookie_say != "hbnl"){
add_header Set-Cookie "say=hbnl";
rewrite .* "$scheme://$host$uri" redirect;
}
讓我們看下這幾行的意思,當cookie中say為空時,給一個設定cookie say為hbnl的302重定向包,如果訪問者能夠在第二個包中攜帶上cookie值,那麼就能正常訪問網站了,如果不能的話,那他永遠活在了302中。你也可以測試一下,用CC攻擊器或者webbench或者直接curl發包做測試,他們都活在了302世界中。
當然,這麼簡單就能防住了?當然沒有那麼簡單。
增強版
仔細的你一定會發現配置檔案這樣寫還是有缺陷。如果攻擊者設定cookie為say=hbnl(CC攻擊器上就可以這麼設定),那麼這個防禦就形同虛設了。我們繼續拿剛剛那個比喻來說明問題。
壞人發現這個規律後,給每個機器人安上了揚聲器,一直重複著“紅包拿來,紅包拿來”,浩浩蕩蕩地又來領紅包了。
這時,工作人員的對策是這樣做的,要求領取者出示有自己名字的戶口本,並且念出自己的名字,“我是xxx,紅包拿來”。於是一群只會嗡嗡叫著“紅包拿來”的機器人又被攆回去了。
當然,為了配合說明問題,每個機器人是有戶口本的,被趕回去的原因是不會念自己的名字,雖然這個有點荒誕,唉。
然後,我們來看下這種方式的配置檔案寫法
#!shell
if ($cookie_say != "hbnl$remote_addr"){
add_header Set-Cookie "say=hbnl$remote_addr";
rewrite .* "$scheme://$host$uri" redirect;
}
這樣的寫法和前面的區別是,不同IP的請求cookie值是不一樣的,比如IP是1.2.3.4,那麼需要設定的cookie是say=hbnl1.2.3.4。於是攻擊者便無法透過設定一樣的cookie(比如CC攻擊器)來繞過這種限制。你可以繼續用CC攻擊器來測試下,你會發現CC攻擊器打出的流量已經全部進入302世界中。
不過大家也能感覺到,這似乎也不是一個萬全之計,因為攻擊者如果研究了網站的機制之後,總有辦法測出並預先偽造cookie值的設定方法。因為我們做差異化的資料來源正是他們本身的一些資訊(IP、user agent等)。攻擊者花點時間也是可以做出專門針對網站的攻擊指令碼的。
完美版
那麼要如何根據他們自身的資訊得出他們又得出他們算不出的數值?
我想,聰明的你一定已經猜到了,用salt加雜湊。比如md5("opencdn$remote_addr"),雖然攻擊者知道可以自己IP,但是他無法得知如何用他的IP來計算出這個雜湊,因為他是逆不出這個雜湊的。當然,如果你不放心的話,怕cmd5.com萬一能查出來的話,可以加一些特殊字元,然後多散幾次。
很可惜,nginx預設是無法進行字串雜湊的,於是我們藉助nginx_lua模組來進行實現。
#!shell
rewrite_by_lua '
local say = ngx.md5("opencdn" .. ngx.var.remote_addr)
if (ngx.var.cookie_say ~= say) then
ngx.header["Set-Cookie"] = "say=" .. say
return ngx.redirect(ngx.var.scheme .. "://" .. ngx.var.host .. ngx.var.uri)
end
';
透過這樣的配置,攻擊者便無法事先計算這個cookie中的say值,於是攻擊流量(代理型CC和低階發包型CC)便在302地獄無法自拔了。
大家可以看到,除了借用了md5這個函式外,其他的邏輯和上面的寫法是一模一樣的。因此如果可以的話,你完全可以安裝一個nginx的計算雜湊的第三方模組來完成,可能效率會更高一些。
這段配置是可以被放在任意的location裡面,如果你的網站有對外提供API功能的話,建議API一定不能加入這段,因為API的呼叫也是沒有瀏覽器行為的,會被當做攻擊流量處理。並且,有些弱一點爬蟲也會陷在302之中,這個需要注意。
同時,如果你覺得set-cookie這個動作似乎攻擊者也有可能透過解析字串模擬出來的話,你可以把上述的透過header來設定cookie的操作,變成透過高階大氣的js完成,發回一個含有doument.cookie=...的文字即可。
那麼,攻擊是不是完全被擋住了呢?只能說那些低階的攻擊已經被擋住而來,如果攻擊者必須花很大代價給每個攻擊器加上webkit模組來解析js和執行set-cookie才行,那麼他也是可以逃脫302地獄的,在nginx看來,確實攻擊流量和普通瀏覽流量是一樣的。那麼如何防禦呢?下節會告訴你答案。
0x02 請求頻率限制
不得不說,很多防CC的措施是直接在請求頻率上做限制來實現的,但是,很多都存在著一定的問題。
那麼是哪些問題呢?
首先,如果透過IP來限制請求頻率,容易導致一些誤殺,比如我一個地方出口IP就那麼幾個,而訪問者一多的話,請求頻率很容易到上限,那麼那個地方的使用者就都訪問不了你的網站了。
於是你會說,我用SESSION來限制就有這個問題了。嗯,你的SESSION為攻擊者敞開了一道大門。為什麼呢?看了上文的你可能已經大致知道了,因為就像那個“紅包拿來”的揚聲器一樣,很多語言或者框架中的SESSION是能夠偽造的。以PHP為例,你可以在瀏覽器中的cookie看到PHPSESSIONID,這個ID不同的話,session也就不同了,然後如果你杜撰一個PHPSESSIONID過去的話,你會發現,伺服器也認可了這個ID,為這個ID初始化了一個會話。那麼,攻擊者只需要每次發完包就構造一個新的SESSIONID就可以很輕鬆地躲過這種在session上的請求次數限制。
那麼我們要如何來做這個請求頻率的限制呢?
首先,我們先要一個攻擊者無法杜撰的sessionID,一種方式是用個池子記錄下每次給出的ID,然後在請求來的時候進行查詢,如果沒有的話,就拒絕請求。這種方式我們不推薦,首先一個網站已經有了session池,這樣再做個無疑有些浪費,而且還需要進行池中的遍歷比較查詢,太消耗效能。我們希望的是一種可以無狀態性的sessionID,可以嗎?可以的。
#!shell
rewrite_by_lua '
local random = ngx.var.cookie_random
if(random == nil) then
random = math.random(999999)
end
local token = ngx.md5("opencdn" .. ngx.var.remote_addr .. random)
if (ngx.var.cookie_token ~= token) then
ngx.header["Set-Cookie"] = {"token=" .. token, "random=" .. random}
return ngx.redirect(ngx.var.scheme .. "://" .. ngx.var.host .. ngx.var.uri)
end
';
大家是不是覺得好像有些眼熟?是的,這個就是上節的完美版的配置再加個隨機數,為的是讓同一個IP的使用者也能有不同的token。同樣的,只要有nginx的第三方模組提供雜湊和隨機數功能,這個配置也可以不用lua直接用純配置檔案完成。
有了這個token之後,相當於每個訪客有一個無法偽造的並且獨一無二的token,這種情況下,進行請求限制才有意義。
由於有了token做鋪墊,我們可以不做什麼白名單、黑名單,直接透過limit模組來完成。
#!shell
http{
...
limit_req_zone $cookie_token zone=session_limit:3m rate=1r/s;
}
然後我們只需要在上面的token配置後面中加入
#!shell
limit_req zone=session_limit burst=5;
於是,又是兩行配置便讓nginx在session層解決了請求頻率的限制。不過似乎還是有缺陷,因為攻擊者可以透過一直獲取token來突破請求頻率限制,如果能限制一個IP獲取token的頻率就更完美了。可以做到嗎?可以。
#!shell
http{
...
limit_req_zone $cookie_token zone=session_limit:3m rate=1r/s;
limit_req_zone $binary_remote_addr $uri zone=auth_limit:3m rate=1r/m;
}
#!shell
location /{
limit_req zone=session_limit burst=5;
rewrite_by_lua '
local random = ngx.var.cookie_random
if (random == nil) then
return ngx.redirect("/auth?url=" .. ngx.var.request_uri)
end
local token = ngx.md5("opencdn" .. ngx.var.remote_addr .. random)
if (ngx.var.cookie_token ~= token) then
return ngx.redirect("/auth?url=".. ngx.var.request_uri)
end
';
}
location /auth {
limit_req zone=auth_limit burst=1;
if ($arg_url = "") {
return 403;
}
access_by_lua '
local random = math.random(9999)
local token = ngx.md5("opencdn" .. ngx.var.remote_addr .. random)
if (ngx.var.cookie_token ~= token) then
ngx.header["Set-Cookie"] = {"token=" .. token, "random=" .. random}
return ngx.redirect(ngx.var.arg_url)
end
';
}
我想大家也應該已經猜到,這段配置檔案的原理就是:把本來的發token的功能分離到一個auth頁面,然後用limit對這個auth頁面進行頻率限制即可。這邊的頻率是1個IP每分鐘授權1個token。當然,這個數量可以根據業務需要進行調整。
需要注意的是,這個auth部分我lua採用的是access_by_lua,原因在於limit模組是在rewrite階段後執行的,如果在rewrite階段302的話,limit將會失效。因此,這段lua配置我不能保證可以用原生的配置檔案實現,因為不知道如何用配置檔案在rewrite階段後進行302跳轉,也求大牛能夠指點一下啊。
當然,你如果還不滿足於這種限制的話,想要做到某個IP如果一天到達上限超過幾次之後就直接封IP的話,也是可以的,你可以用類似的思路再做個錯誤頁面,然後到達上限之後不返回503而是跳轉到那個錯誤頁面,然後錯誤頁面也做個請求次數限制,比如每天只能訪問100次,那麼當超過報錯超過100次(請求錯誤頁面100次)之後,那天這個IP就不能再訪問這個網站了。
於是,透過這些配置我們便實現了一個網站訪問頻率限制。不過,這樣的配置也不是說可以完全防止了攻擊,只能說讓攻擊者的成本變高,讓網站的扛攻擊能力變強,當然,前提是nginx能夠扛得住這些流量,然後頻寬不被堵死。如果你家門被堵了,你還想開門營業,那真心沒有辦法了。
然後,做完流量上的防護,讓我們來看看對於掃描器之類的攻擊的防禦。
0x03 防掃描
這個是一個不錯的waf模組,這塊我們也就不再重複造輪子了。可以直接用這個模組來做防護,當然也完全可以再配合limit模組,用上文的思路來做到一個封IP或者封session的效果。
0x04 總結
本文旨在達到拋磚引玉的作用,我們並不希望你直接單純的複製我們的這些例子中的配置,而是希望根據你的自身業務需要,寫出適合自身站點的配置檔案。
相關文章
- JeecgBoot抵禦XSS攻擊實現方案2022-05-13boot
- 如何成功抵禦DOS攻擊?教你4個方法!2022-07-14
- DDoS攻擊的危害是什麼?如何抵禦?2022-09-06
- 【知識分享】伺服器遭受攻擊的方式,如何抵禦攻擊?2023-02-28伺服器
- 什麼是中間人攻擊?如何抵禦中間人攻擊?2022-09-14
- 大促活動如何抵禦大流量 DDoS 攻擊?2022-09-06
- 【知識分享】 伺服器抵禦ddos攻擊的方法2023-02-28伺服器
- 密碼學系列之:碰撞抵禦和碰撞攻擊collision attack2021-06-03密碼學
- Chrome關閉7大埠,抵禦NAT Slipstreaming 2.0 漏洞攻擊2021-02-02Chrome
- 抵禦惡意http攻擊,使用redis製作一個簡單的防禦2020-11-02HTTPRedis
- 資料安全:透過資料雲備份來抵禦勒索病毒2020-07-24
- 什麼是無檔案惡意軟體攻擊?如何防禦?2022-11-25
- 透過python讀取ini配置檔案2019-08-01Python
- 透過命令列修改nacos配置檔案2024-10-22命令列
- DDoS攻擊、CC攻擊的攻擊方式和防禦方法2019-02-27
- nginx配置檔案2024-05-01Nginx
- Nginx 配置檔案2024-10-16Nginx
- 抵禦網路攻擊的背後——成熟創新的技術是關鍵2022-11-28
- XXE攻擊攻擊原理是什麼?如何防禦XXE攻擊?2023-02-13
- 如何有效防禦DDoS攻擊和CC攻擊?2023-12-27
- CSRF 攻擊與防禦2019-11-18
- WEB攻擊與防禦2018-03-31Web
- CSRF攻擊與防禦2024-11-01
- 【nacos】透過curl語句更新配置檔案2024-08-27
- Chrome釋出重要安全更新,將抵禦量子計算機發起的黑客攻擊2018-05-02Chrome計算機黑客
- 小鳥雲伺服器 新增香港高防雲伺服器,抵禦DDoS攻擊2021-08-26伺服器
- 什麼是SSRF攻擊?如何防禦SSRF攻擊?2023-03-07
- 什麼是CSRF攻擊?如何防禦CSRF攻擊?2023-02-17
- 抵禦勒索攻擊,戴爾科技構築三位一體的終極防線2022-04-11
- WMI攻擊與安全防禦2020-06-27
- CSS keylogger:攻擊與防禦2019-01-03CSS
- XXE攻擊是什麼?如何有效防禦XXE攻擊?2024-04-12
- 三家機構聯合開發可抵禦量子計算攻擊的區塊鏈2021-08-11區塊鏈
- Nginx防止DDOS攻擊2018-05-04Nginx
- 隱秘的攻擊形式:無檔案攻擊型別分析2020-08-03型別
- CC攻擊的原理是什麼?如何防禦CC攻擊?2023-03-14
- CC攻擊分為幾種?遭遇CC攻擊如何防禦?2022-08-03
- DDoS攻擊的危害是什麼?如何防禦DDoS攻擊?2022-09-16