企業安全建設之 WAF 防禦
WAF (Web Application Firewall),Web 應用防火牆,通過解析 HTTP/HTTPS 請求內容,並執行一系列的安全檢測策略,對目標 Web 應用提供安全防護,同時記錄相關安全防禦日誌。文章將介紹 ModSecurity 相關部署配置概念、防禦規則及簡單地定製化實踐。
WAF 概念簡述
WAF 根據產品形態不同,通常可以分為硬體裝置類、軟體產品類及雲 WAF 類別,相關產品代表如下
ModSecurity
根據工作原理不同可分為反向代理模式、雲 AGENT 模式及容器安全模組形式
工作模式 | 工作原理 | 優劣分析 |
---|---|---|
反向代理模式 | 修改DNS,讓域名解析到反向代理伺服器。所有流量經過反向代理進行檢測,檢測無問題之後再轉發給後端的Web伺服器 | 集中式流量出入口,可針對大資料分析,動態新增一層,增加網路開銷,站點後端 Web 較多的情況,轉發規則比較複雜,流量都被捕捉,涉及敏感資料需要保護 |
雲 AGENT 模式 | 所有請求通過 Web 伺服器模組轉發到雲端進行安全檢測,NGINX 根據雲端檢測結果進行轉發,若檢測為攻擊則執行配置的動作 | 安全規則統一管理,規則更新只需要更新後端的決策系統,不涉及 Web 伺服器端規則更新,根據業務配置規則,需要較高成本,Web 伺服器變更時不便於更新 |
容器安全模組 | 所有請求流量均先經過檢測引擎的檢測,若未發現攻擊,則進行正常業務響應,否則按照配置的動作進行響應 | 網路結構簡單,僅部署容器安全模組,但維護困難,大規模伺服器叢集,任何更新涉及多臺伺服器,需部署操作,成本高,無集中化資料中心,安全事件彙總不方便 |
ModSecurity 部署
Nginx
載入 ModSecurity
模組安裝有兩種方式:一種是編譯為 Nginx
靜態模組,另一種是通過 ModSecurity-Nginx Connector
載入動態模組,下面將實踐 ModSecurity-Nginx Connector
動態模組的安裝和配置
安裝編譯 libmodsecurity
依賴庫
yum install -y pt-utils autoconf automake build-essential git libcurl4-openssl-dev libgeoip-dev liblmdb-dev libpcre++-dev libtool libxml2-dev libyajl-dev pkgconf wget zlib1g-dev doxygen libxml2-devel pcre-devel
編譯 libmodsecurity 模組
下載 libmodsecurity
原始碼並編譯模組
git clone --depth 1 -b v3/master --single-branch https://github.com/SpiderLabs/ModSecurity cd ModSecurity git submodule init git submodule update ./build.sh ./configure make make install
若編譯安裝過程出現如下錯誤提示,可直接忽略
fatal: No names found, cannot describe anything.
編譯 nginx connector 模組
下載 nginx connector
模組,並編譯成動態模組
git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git
目標機器已安裝 Nginx
,需確認 Nginx
版本,並下載指定版本的 Nginx
原始碼進行編譯,否則可下載任意版本 Nginx
原始碼進行編譯
下載 Nginx
原始碼進行編譯,編譯過程加入 ModSecurity
依賴庫模組
wget http://nginx.org/download/nginx-1.14.1.tar.gz tar zxvf nginx-1.14.1.tar.gz cd nginx-1.14.1 ./configure --add-dynamic-module=../ModSecurity-nginx make modules
網上部分文件編譯過程 configure
新增編譯引數 --with-compat
啟用模組相容性,導致編譯後的 so
模組無法使用(錯誤提示如下),去除該選項即可
nginx: [emerg] module "/usr/local/nginx/conf/modules/ngx_http_modsecurity_module.so" is not binary compatible in /usr/local/nginx/conf/nginx.conf:11
將編譯成功的 ModSecurity 模組複製到 Nginx 安裝目錄中,在 Nginx 配置檔案中新增如下配置,載入 ModSecurity 模組
load_module modules/ngx_http_modsecurity_module.so;
配置 ModSecurity 規則
下載 ModSecurity
配置規則
mkdir modseucirty wget -P xxx/modsecurity/ https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended mv xxx/modsecurity/modsecurity.conf-recommended xxx/modsecurity/modsecurity.conf
在對 ModSecurity
進行配置時,需要將原 ModSecurity
資料夾中的 unicode.mapping
複製到與上述 modsecurity.conf
檔案相同的資料夾中,否則啟動 Nginx
過程會提示檔案缺失,如下所示
nginx: [emerg] "modsecurity_rules_file" directive Rules error. File: /usr/local/nginx/conf/modsec/modsecurity.conf. Line: 236. Column: 17. Failed to locate the unicode map file from: unicode.mapping Looking at: 'unicode.mapping', 'unicode.mapping', '/usr/local/nginx/conf/modsec/unicode.mapping', '/usr/local/nginx/conf/modsec/unicode.mapping'.in /usr/local/nginx/conf/nginx.conf:41
修改 ModSecurity
配置檔案 modsecurity.conf
中檢測模式 SecRuleEngine DetectionOnly
為 SecRuleEngine On
,如下圖所示
# -- Rule engine initialization ---------------------------------------------- # Enable ModSecurity, attaching it to every transaction. Use detection # only to start with, because that minimises the chances of post-installation # disruption. # #SecRuleEngine DetectionOnly SecRuleEngine On
在 Nginx
Server
配置中開啟 ModSecurity
配置,如下所示
nginx.conf 配置檔案 server { listen80; server_namelocalhost; modsecurity on; modsecurity_rules_file /usr/local/nginx/conf/modsec/main.conf; location / { roothtml; indexindex.html index.htm; } error_page500 502 503 504/50x.html; location = /50x.html { roothtml; } } ---------------------------------------------------------------------------------------------- main.conf 配置檔案 Include "/usr/local/nginx/conf/modsec/modsecurity.conf" # 新增測試規則,當請求中包含引數 testparam 同時其值為 test 時,攔截請求並返回 403 SecRule ARGS:testparam "@contains test" "id:1234,deny,status:403"
ModSecurity 規則測試
ModSecurity
規則測試如下圖所示,當攜帶引數 ?testparam=test
發起請求時,請求被拒絕

ModSecurity 配置簡介
ModSecurity 日誌配置
ModSecurity
應用中的日誌包括 Debug
(除錯日誌)、 Audit
(審計日誌)兩大類,除錯日誌記錄 ModSecurity
規則匹配、檢測過程,並依據除錯日誌相關引數的不同,其所記錄的日誌欄位均不同,除錯日誌配置如下所示
除錯日誌配置 | 引數說明 |
---|---|
SecDebugLog | 除錯日誌記錄路徑 |
SecDebugLogLevel | 除錯日誌記錄等級 |
除錯日誌記錄等級分為 7 個等級,如下所示
- 0 不輸出日誌
- 1 輸出錯誤日誌,例如致命的處理錯誤、阻塞的會話
- 2 記錄警告日誌,例如非阻塞的規則匹配
- 3 輸出通知日誌,例如非致命的處理錯誤
- 4 資訊
- 5 詳情
- 9 輸出所有除錯日誌
審計日誌,可用於輸出所有 HTTP 會話日誌,其日誌資料如下所示
日誌屬性 | 引數說明 |
---|---|
SecAuditEngine | 控制審計日誌輸出,其可選值為 On、Off、RelevantOnly |
SecAuditLogRelevantStatus | 待審計的 HTTP 狀態碼,例如 ^(?:5|4(?!04)) 用於過濾 5xx 或 4xx,排除 404 |
SecAuditLogParts | ABCDEFGHIJKZ 所需記錄的 HTTP 請求欄位 |
SecAuditLogType | 審計日誌型別,可選值為 Serial、Concurrent |
SecAuditLog | 記錄審計日誌路徑 |
SecAuditLogStorageDir | Concurrent 審計日誌時所輸出的目錄 |
SecAuditLogParts
取值為 ABCDEFGHIJKZ
說明如下所示
日誌屬性 | 引數說明 |
---|---|
A | 審計日誌頭 |
B | 請求頭 |
C | 請求 Body |
D | 保留 |
E | 響應 Body |
F | 響應頭 |
G | 保留 |
H | 審計標記,包含額外審計會話資料 |
I | 保留 |
J | 保留 |
K | 所匹配的規則集合 |
Z | 終結標記 |
如下為一段審計日誌示例
---Xd59KAAO---A-- [26/Nov/2018:09:46:59 +0800] 154319681942.596956 10.211.55.2 58760 10.211.55.2 80 ---Xd59KAAO---B-- GET /?testparam=test HTTP/1.1 Host: 10.211.55.14 Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7 If-None-Match: "5bf974af-264" If-Modified-Since: Sat, 24 Nov 2018 15:56:31 GMT ---Xd59KAAO---C-- ---Xd59KAAO---D-- ---Xd59KAAO---E-- <html>\x0d\x0a<head><title>403 Forbidden</title></head>\x0d\x0a<body bgcolor="white">\x0d\x0a<center><h1>403 Forbidden</h1></center>\x0d\x0a<hr><center>nginx/1.14.1</center>\x0d\x0a</body>\x0d\x0a</html>\x0d\x0a<!-- a padding to disable MSIE and Chrome friendly error page -->\x0d\x0a<!-- a padding to disable MSIE and Chrome friendly error page -->\x0d\x0a<!-- a padding to disable MSIE and Chrome friendly error page -->\x0d\x0a<!-- a padding to disable MSIE and Chrome friendly error page -->\x0d\x0a<!-- a padding to disable MSIE and Chrome friendly error page -->\x0d\x0a<!-- a padding to disable MSIE and Chrome friendly error page -->\x0d\x0a ---Xd59KAAO---F-- HTTP/1.1 403 Server: nginx/1.14.1 Date: Mon, 26 Nov 2018 01:46:59 GMT Content-Length: 571 Content-Type: text/html Connection: keep-alive ---Xd59KAAO---H-- ---Xd59KAAO---I-- ---Xd59KAAO---J-- ---Xd59KAAO---Z--
在測試 ModSecurity 功能時,想通過 WAF 輸出的審計日誌來學習和測試相應防護規則,結果發現將 SecRuleEngine 設定為 DetectionOnly 後,ModSecurity 並沒有在審計日誌中輸出匹配過程和規則;正確的做法應該是通過記錄除錯日誌,來跟蹤規則匹配、執行。
ModSecurity CRS 規則
ModSecurity CRS 是由 OWASP 社群所維護的攻擊檢測規則,可在 ModSecurity 中直接使用,其檢測規則覆蓋 OWASP Top 10 中常見漏洞和一些框架層面的漏洞。
CRS 部署使用
下載 CRS 規則到本地,可通過如下命令進行操作
git clone <a href="/cdn-cgi/l/email-protection" data-cfemail="d3b4baa793b4baa7bba6b1fdb0bcbe">[email protected]</a>:SpiderLabs/owasp-modsecurity-crs.git
在 CRS 的 crs-set.conf.example
中說明 CRS 的配置需要在 WebServer 中包含如下三種配置檔案
# The order of file inclusion in your webserver configuration should always be: # 1. modsecurity.conf # 2. crs-setup.conf (this file) # 3. rules/*.conf (the CRS rule files) # # Please refer to the INSTALL file for detailed installation instructions.
將下載目錄下的 rules
移動到 /usr/local/nginx/conf/modsec
目錄下,通知將 crs-set.conf.example
重新命名為 crs-set.conf
並移動到 /usr/local/nginx/conf/modsec
,部署玩 CRS 後的目錄如下所示
[<a href="/cdn-cgi/l/email-protection" data-cfemail="1f6d70706b5f5c7a716b706c28">[email protected]</a> modsec]# pwd /usr/local/nginx/conf/modsec [<a href="/cdn-cgi/l/email-protection" data-cfemail="34465b5b407477515a405b4703">[email protected]</a> modsec]# ls crs-setup.confmain.confmodsecurity.confrulesunicode.mapping [<a href="/cdn-cgi/l/email-protection" data-cfemail="f0829f9f84b0b3959e849f83c7">[email protected]</a> modsec]#
在上述 /usr/local/nginx/conf/modsec/main.conf
配置檔案中新增 CRS 配置,新增後的檔案內容如下所示
[<a href="/cdn-cgi/l/email-protection" data-cfemail="95e7fafae1d5d6f0fbe1fae6a2">[email protected]</a> modsec]# cat main.conf Include "/usr/local/nginx/conf/modsec/modsecurity.conf" Include "/usr/local/nginx/conf/modsec/crs-setup.conf" Include "/usr/local/nginx/conf/modsec/rules/*.conf"
測試 CRS 規則,訪問連線 http://10.211.55.14/?id=1'or'1=1
,如下所示,觸發了 CRS 中的 SQL 注入防禦規則
需注意 ModSecurity CRS 規則預設將本機加入白名單,所以直接通過 curl http://localhost?id=1'or'1=1
測試是無法觸發 CRS 防禦規則。

觸發 REQUEST-920-PROTOCOL-ENFORCEMENT
、 REQUEST-942-APPLICATION-ATTACK-SQLI.conf
等規則檢測,並且被預設攔截,其匹配記錄如下所示
ModSecurity: Warning. Matched "Operator `Rx' with parameter `^[\d.:]+$' against variable `REQUEST_HEADERS:Host' (Value: `10.211.55.14' ) [file "/usr/local/nginx/conf/modsec/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "777"] [id "920350"] [rev "2"] [msg "Host header is a numeric IP address"] [data "10.211.55.14"] [severity "4"] [ver "OWASP_CRS/3.0.0"] [maturity "9"] [accuracy "9"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "OWASP_CRS/PROTOCOL_VIOLATION/IP_HOST"] [tag "WASCTC/WASC-21"] [tag "OWASP_TOP_10/A7"] [tag "PCI/6.5.10"] [hostname "10.211.55.2"] [uri "/"] [unique_id "154530934235.756485"] [ref "o0,12v37,12"] ModSecurity: Warning. detected SQLi using libinjection. [file "/usr/local/nginx/conf/modsec/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [line "43"] [id "942100"] [rev "1"] [msg "SQL Injection Attack Detected via libinjection"] [data "Matched Data: s&s found within ARGS:id: 1'or'1=1"] [severity "2"] [ver "OWASP_CRS/3.0.0"] [maturity "1"] [accuracy "8"] [hostname "10.211.55.2"] [uri "/"] [unique_id "154530934235.756485"] [ref "v9,8"] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `8' ) [file "/usr/local/nginx/conf/modsec/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "44"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 8)"] [data ""] [severity "2"] [ver ""] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "10.211.55.2"] [uri "/"] [unique_id "154530934235.756485"] [ref ""] ModSecurity: Warning. Matched "Operator `Ge' with parameter `5' against variable `TX:INBOUND_ANOMALY_SCORE' (Value: `8' ) [file "/usr/local/nginx/conf/modsec/rules/RESPONSE-980-CORRELATION.conf"] [line "65"] [id "980130"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Inbound Score: 8 - SQLI=5,XSS=0,RFI=0,LFI=0,RCE=0,PHPI=0,HTTP=0,SESS=0): SQL Injection Attack Detected via libinjection"] [data ""] [severity "0"] [ver ""] [maturity "0"] [accuracy "0"] [tag "event-correlation"] [hostname "10.211.55.2"] [uri "/"] [unique_id "154530934235.756485"] [ref ""]
CRS 規則說明
/usr/local/nginx/conf/modsec/rules/*.conf
目錄下的 CRS 防禦規則檔案及相關說明如下所示
規則檔案 | 規則說明 |
---|---|
REQUEST-900-EXCLUSION-RULES-BEFORE-CRS | 定製化個人防護規則,調整規則防護場景 |
REQUEST-901-INITIALIZATION | CRS 配置初始化 |
REQUEST-910-IP-REPUTATION | IP 信譽庫 |
REQUEST-911-METHOD-ENFORCEMENT | HTTP 請求方法檢測 |
REQUEST-912-DOS-PROTECTION | 拒絕服務規則 |
REQUEST-913-SCANNER-DETECTION | 掃描器檢測 |
REQUEST-920-PROTOCOL-ENFORCEMENT | URL Scheme 協議檢測 |
REQUEST-921-PROTOCOL-ATTACK | HTTP 協議攻擊檢測 |
REQUEST-930-APPLICATION-ATTACK-LFI | LFI 本地檔案包含漏洞檢測 |
REQUEST-931-APPLICATION-ATTACK-RFI | RFI 遠端檔案包含檢測 |
REQUEST-932-APPLICATION-ATTACK-RCE | RCE 遠端命令執行檢測 |
REQUEST-933-APPLICATION-ATTACK-PHP | PHP 攻擊檢測 |
REQUEST-941-APPLICATION-ATTACK-XSS | XSS 攻擊檢測 |
REQUEST-942-APPLICATION-ATTACK-SQLI | SQL 注入攻擊檢測 |
REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION | 會話固定攻擊檢測 |
REQUEST-949-BLOCKING-EVALUATION | 基於風險值檢測入站請求 |
RESPONSE-950-DATA-LEAKAGES | 資料洩漏檢測 |
RESPONSE-951-DATA-LEAKAGES-SQL | SQL 資料洩漏檢測 |
RESPONSE-952-DATA-LEAKAGES-JAVA | JAVA 資料洩漏 |
RESPONSE-953-DATA-LEAKAGES-PHP | PHP 資料洩漏 |
RESPONSE-954-DATA-LEAKAGES-IIS | IIS 資料洩漏 |
RESPONSE-959-BLOCKING-EVALUATION | 基於風險值檢測出站請求 |
RESPONSE-980-CORRELATION | 入站、出站風險值關聯 |
RESPONSE-999-EXCLUSION-RULES-AFTER-CRS | 定製化個人防護規則,過載、更新、移除檢測規則 |
如下所示,CRS 支援兩種執行模式,CRS 預設採用的為異常評分模式
- 異常評分模式,每條防禦規則都有相應的風險評分,若匹配成功,則直接累加對應的風險值,當所有規則都匹配完成後比較入站、出站的風險值,若比設定的風險閾值還高,則預設返回 403;
- 檢測攔截模式,若請求匹配到了防禦規則,則依據所配置的行為進行響應,同時將忽略其後所有的防禦規則,該模式節約資源、效能;
若 CRS 要切換成檢測攔截模式,需將 crs-setup.conf
中異常評分模式配置註釋,同時開啟檢測攔截模式配置,如下所示
#SecDefaultAction "phase:1,log,auditlog,pass" #SecDefaultAction "phase:2,log,auditlog,pass" SecDefaultAction "phase:1,log,auditlog,deny,status:403" SecDefaultAction "phase:2,log,auditlog,deny,status:403"
在異常評分模式中,CRS 包含 規則等級(Paranoia)、異常閾值(Anomaly) 兩個量化值,隨著規則等級越來越高,其所啟用的安全防禦規則就越來越多,同時誤報也會越來越多,規則等級分為以下幾類,規則等級高於 PL2 在審計日誌中會輸出規則等級標籤
- 1(PL1),預設風險等級,啟用了大部分防禦規則,誤報較少
- 2(PL2),比 PL1 啟用更多防禦規則,例如基於正則的 SQL 注入和 XSS,比 PL1 誤報多
- 3(PL3),比 PL2 啟用更多防禦規則,面向經驗豐富使用者,滿足較高安全性場景
- 4(PL4),最嚴格的風險等級,會產生一定數量的誤報
基於異常告警模式,每條檢測規則都包含一定的風險值,不同危害風險值不同,如下所示,可根據業務場景自主調整
- CRITICAL,致命,風險值為 5
- ERROR,錯誤,風險值為 4
- WARNING,警告,風險值為 3
- NOTICE,通知,風險值為 2
根據對 CRS 防禦規則的熟知程度慢慢深入,相關異常閾值和規則等級設定可參考下表所示,即異常告警閾值慢慢降低,規則等級慢慢嚴格
階段編號 | 階段名稱 | 異常閾值 | 規則等級 |
---|---|---|---|
1 | 初始階段 | 高 | 低 |
2 | 實驗階段 | 高 | 高 |
3 | 標準階段 | 低 | 低 |
4 | 高安全性階段 | 低 | 高 |