OpenResty簡介
OpenResty是將Nginx與 Lua粘合的高性能 Web 平臺,其內部集成了大量精良的 Lua 庫、第三方模塊等。用于方便地搭建高并發、擴展性極高的動態Web 應用、Web 服務和動態網關。從聽說到使用了OpenResty之后,感覺這是瑞士軍刀,應用實在太廣泛。
我們公司目前沒有專門的安全工程師,安全的一些工作就落到我們運維工程師頭上,遇到安全問題就找到我們身上了,因此我想做一個web應用防火墻,讓開發人員不用考慮安全問題只關心業務開發;因此就考慮開發WAF來解決現在遇到的問題。
WAF簡介
WAF就是web應用防火墻,是對網站進行安全防護的。一個http/https的請求,包含數據: url、get參數、post參數、協議版本/method/host/agent/cookie等http請求頭。 如下圖所示:
而WAF就可以對請求中的參數、訪問頻率進行安全檢查(正則匹配為主),觸發危險操作的請求,就進行攔截、記錄操作。 當然WAF解決是還是web安全1.0和2.0的問題。對應業務邏輯本身WAF目前是不能通用的進行防護的。
WEB安全1.0
在1.0時代下,攻擊是通過服務器漏洞(IIS6溢出等)、WEB應用漏洞(SQL注入、文件上傳、命令執行、文件包含等)屬于服務器類的攻擊,該類型漏洞雖然經歷了這么多年,很遺憾,此類漏洞還是存在,并且重復在犯相同的錯誤。
WEB安全2.0
隨著社交網絡的興起,原來不被重視的XSS、CSRF等漏洞逐漸進入人們的視野,那么在2.0時代,漏洞利用的思想將更重要,發揮你的想象,可以有太多可能。
WEB安全3.0
同開發設計模式類似(界面、業務邏輯、數據),3.0將關注應用本身的業務邏輯和數據安全,如密碼修改繞過、二級密碼繞過、支付類漏洞、刷錢等類型的漏洞,故注重的是產品本身的業務安全、數據安全。
WAF設計、編碼
我設計的WAF過濾流程,只是根據我的理解來設計他的處理流程,不一定是最合理的,但是解決了我們現在遇到的問題。整個設計是在Nginx執行的相應階段完成一些相應的動作,如在init階段完成數據初始化、rewrite階段暫時沒用,預計未來跳轉使用(如驗證碼、set cookie等)、access階段實施攔截防護(大多數過濾在這完成)、body_filter階段進行響應內容的替換(如非法關鍵詞替換)。
init階段
在nginx初始化階段,我們可以初始化如ip黑白名單;初始化ip計數器;此處我們用到了幾個共享字典來存儲這些數據(如1MB大約存ip數量1000個)。
lua_shared_dicttoken_list 20m;??? # token存放
lua_shared_dictcount_dict 5m;???? #用于攔截計數數據的保存
lua_shared_dictconfig_dict 5m;??? #保存config中部分配置
lua_shared_dictip_dict 30m;?????? #用于記錄黑、白名單ip
lua_shared_dictlimit_ip_dict 100m;?? #用于 IP 訪問計數統計
在初始化階段,會讀取全局配置文件config.json和規則配置文件xxx_Mod.json,存放到config_dict中;其次,讀取配置的ip黑白名單存放到ip_dict;然后定義一些公共函數如sayHtml,sayFile,sayLua等等。
access階段
我們主要的攔截就在該階段操作的,整體的防護分為13個步驟進行分層防護的,如下圖所示。
0、realIpFrom_Mod==>獲取用戶真實IP:獲取用戶真實ip 在一些網絡場景下,用戶真實ip在http頭中,所以我們先要把真實ip取出來;
1、ip_Mod==>ip黑白名單的過濾(黑白名單):ip白名單后續的規則都跳過了,請求繼續走;ip黑名單,就是在應用層進行攔截,請求到這里就停了,不會到后端了;
2、host_method_Mod==>host&method過濾(白名單):這里就是域名準入,和method的準入了,可以配置允許的host,允許的method;
3、app_Mod==>自定義過濾動作:這里提供的幾個動作是:返回字符串、返回文件內容、動態執行lua腳本、記錄log、匹配白名單(ip,args參數);基本匹配的是:host、url在后續的場景中我在舉一些例子;
4、referer_Mod==>referer規則過濾(白名單):一些圖片資源的防盜鏈,站外的CSRF就是通過referer來過濾的;
5、url_Mod==>url過濾(黑、白名單):
“\.(svn|git|htaccess|bash_history)” —敏感文件、目錄、備份文件黑名單攔截;
“\.(css|js|flv|swf|woff|txt)$” –靜態資源文件白名單,這樣后續的過濾規則都會跳過,減少總的匹配次數,提高效率;以及訪問頻率統計時,去掉這些靜態文件的統計;
6、header_Mod==>header過濾(黑名單):一些掃描器,爆破工具,黑客工具有時在http頭中會有一些標記,這里我們就可以過掉;
{
"state": "on",
"url":["*",""],
"hostname":["*",""],
"header":["Acunetix_Aspect","*",""]
},
--掃描器 wvs 的標記
{
"state": "on",
"url":["*",""],
"hostname":["*",""],
"header": ["X_Scan_Memo","*",""]
}
--掃描器 Scan 的標記
7、useragent_Mod==>user-agent過濾(黑名單):這里是過濾一些常見黑客工具,壓測工具等;
havij|sqlmap|nmap|HTTrack...
8、cookie_Mod==>cookie過濾(黑名單):這里是對cookie進行SQL注入、XSS、遍歷、敏感文件讀取等一些規則的過濾;
9、args_Mod==>GET參數過濾(黑名單):這里是對args參數進行SQL注入、XSS、遍歷、敏感文件讀取等一些規則的過濾;
if config_is_on("args_Mod") then
local args_mod =getDict_Config("args_Mod")
local args =ngx.unescape_uri(ngx.var.query_string)
if args ~= "" then
for i,v in ipairs(args_mod) do
if v.state == "on" then
if remath(host,v.hostname[1],v.hostname[2])then
if remath(args,v.args[1],v.args[2]) then
Set_count_dict("args_deny count")
action_deny()
break
end
end
end
end
end
end
看一下代碼,對于參數污染這種繞WAF的方式,還有url編碼繞過方式也是無法繞過的,代碼中首先進行了轉碼、在取整個args,而不是一個一個取在拼接上;
10、post_Mod==>post參數過濾(黑名單):這里是對post參數進行SQL注入、XSS、遍歷、敏感文件讀取等一些規則的過濾;
localfunction get_postargs()
ngx.req.read_body()
local data = ngx.req.get_body_data() --ngx.req.get_post_args()
if not data then
local datafile =ngx.req.get_body_file()
if datafile then
local fh, err = io.open(datafile,"r")
if fh then
fh:seek("set")
data = fh:read("*a")
fh:close()
end
end
end
return ngx.unescape_uri(data)
end
if config_is_on("post_Mod") and method == "POST" then
--debug("post_Mod is on")
local post_mod =getDict_Config("post_Mod")
local postargs = get_postargs()
if postargs ~= "" then
for i,v in ipairs(post_mod) do
if v.state == "on" then
--debug(i.." post_modstate is on")
if remath(host,v.hostname[1],v.hostname[2]) then
if remath(postargs,v.post[1],v.post[2]) then
Set_count_dict("post_deny count")
debug("post_Mod :"..postargs.."No : "..i,"post_deny",ip)
action_deny()
break
end
end
end
end
end
end
代碼中是取整個body的,而不是通過ngx.req.get_post_args(),且最后也轉碼了,所以通過參數污染、url轉碼是不能繞過的;
11、network_Mod==>訪問頻率過濾(頻率黑名單):先看一個配置:
--單個URL的頻率限制
--因為一個網站一般情況下容易被CC的點就那么幾個
{
"state": "on",
"network":{"maxReqs":10,"pTime":10,"blackTime":600},
"hostname":[["101.200.122.200","127.0.0.1"],"table"],
"url":["/api/time",""]
}
--限制整個網站的(范圍大的一定要放下面)
{
"state": "on",
"network":{"maxReqs":30,"pTime":10,"blackTime":600},
"hostname":[["101.200.122.200","127.0.0.1"],"table"],
"url":["*",""]
}
--限制ip的不區分host和url
{
"state": "on",
"network":{"maxReqs":100,"pTime":10,"blackTime":600},
"hostname":["*",""],
"url":["*",""]
}
相對來說比較靈活,可以配置某個url的ip訪問頻率,以及在前面提到過,一些靜態文件css/js/img文件是可以不計數的。
body_filter階段
12、replace_Mod==>應答內容的替換:這里就是對返回內容的動態替換的功能,比如替換一些非法關鍵詞等。
場景應用
1、簡單的正則添加:如我們需要過濾以.sql結尾的請求URL,那么就在url_Mod中設置
{
"state": "on",
"hostname": [
"*",
""
],
"url": [
"\\.(bak|inc|old|mdb|sql|backup|java|class)$",
"jio"
],
"action": "deny"
}
2、我們需要設置管理后臺僅允許部分ip可以訪問,那么就在app_Mod中設置
{
"state": "on",
"action":["allow"],
"allow":["ip",["106.37.236.170","1.1.1.1"],"table"],
"hostname":[["101.200.122.200","127.0.0.1"],"table"],
"url":["/api/.*","jio"]
}
看著配置也比較容易理解,host是101.200.122.200或者127.0.0.1的,訪問的目錄是/api/.*的,僅允許allow中的2個IP。
3、CC攻擊防護
攻擊類型
1)、行為(GET、POST等):目前主要還是這兩種method攻擊為主,其他的基本沒有,因為比較互聯網上的web應用也都是這兩種居多;
2)、被攻擊的點(前端的緯度) a: 用戶可直接訪問的URL(搜索、重CPU、IO、數據庫的點);b:嵌入的URL(驗證碼、ajax接口等);c:面向非瀏覽器的接口(一些API、WEBservice等);d:基于特定web服務、語言等的特定攻擊(慢速攻擊、PHP-dos等)。
防護方法
1)網絡層:通過訪問ip的頻率、統計等使用閥值的方式進行頻率和次數的限制,黑名單方式;
2)網絡層+應用層:在后來的互聯網網絡下,有了CDN的加入,現在增加的網絡層的防護需要擴展,那么統計的IP將是在HTTP頭中的IP,仍然使用頻率、次數、黑名單的方式操作。
3)應用層:TAG驗證、SET COOKIE、URL跳轉、JS跳轉、驗證碼、頁面嵌套、強制靜態緩存等;防護是需要根據攻擊點進行分別防護的,如攻擊的是嵌入的url,我們就不能使用JS跳轉、302驗證碼等這樣的方法;在多次的CC防護實戰中,如使用url跳轉、setcookie;在新型的CC攻擊下,這些防護都已經失效了。瀏覽器是可以執行JS和flash的,這里我分享一些基于JS的防護算法,flash需要自己去寫(比js復雜一些),可以實現flash應用層的安全防護和防頁面抓取(開動你的大腦吧);
4)客戶端防護:使用JS進行前端的防護(瀏覽器識別、鼠標軌跡判斷、url有規則添加尾巴(args參數)、隨機延遲、鼠標鍵盤事件獲取等)其實這里非常復雜,如瀏覽器的識別 ie 支持?!-[1,]這個特殊JS,一些瀏覽器有自定義標簽等等;
5)服務端防護:url添加的尾巴(args參數)是服務器動態生成的token,而不是使用靜態的正則去匹配其合法性;
6)特定攻擊:該類特定攻擊,可以通過特征快速匹配出來(慢速攻擊、PHP5.3的http頭攻擊)。
目前已經有了一些應用層的防護都是基于js的(還得自己寫js),后續有時間會把驗證碼等加上,這個很實用。
誤報肯定是有的,使用時需要根據自己的業務進行調整。
動態配置規則
提供了相應的http接口來增刪相應的規則;方便規則的維護。
更多案例和配置示例請參考https://github.com/starjun/openstar。
由于本應用時為了解決當前公司的存在的問題,代碼質量、代碼實現可能存在不到位的地方,多多探討。感謝春哥、OpenResty社區、loveshell。