在現代網路環境中,Web 應用程式面臨的安全威脅日益複雜且多樣化。從傳統的 SQL 注入、跨站腳本攻擊(XSS),到進階的應用層 DDoS、零日漏洞利用,攻擊手法不斷演進。單純依賴網路層防火牆已無法有效防護這些針對應用層的攻擊,因此 Web 應用程式防火牆(WAF)成為現代安全架構中不可或缺的防護層。

OWASP 核心規則集(Core Rule Set,CRS)是目前最廣泛使用的開源 WAF 規則集,它提供了一套通用的攻擊檢測規則,能夠識別並阻擋大多數常見的 Web 攻擊。將 CRS 與 NGINX 的 ModSecurity 模組整合,可以為 Web 應用程式建立強大的第一道防線。本文將深入探討 CRS 的架構設計、異常評分機制、規則調校策略,以及如何整合商業規則集與 IP 信譽系統,建構完整的 Web 應用程式安全防護體系。

OWASP 核心規則集的發展背景與設計理念

OWASP 核心規則集的發展始於 2006 年,由資安專家 Ofer Shezaf 首次發布。這套規則集的設計初衷是提供一個通用的、可攜式的 WAF 規則集,使組織能夠快速部署基本的 Web 應用程式保護,而不需要針對每個應用程式從頭編寫規則。經過多年的發展,CRS 已成為 ModSecurity 生態系統中最重要的規則集,目前由 Dr. Christian Folini 領導的 10 人開發團隊負責維護,並採用 Apache 軟體授權版本 2(ASLv2)開源釋出。

CRS 3.0 版本於 2016 年 11 月發布,這是一個里程碑式的版本更新。開發團隊針對規則進行了大幅度的重構,將誤報率降低了超過 90%,這意味著在預設配置下,合法的使用者請求被錯誤阻擋的情況大幅減少。這項改進使得 CRS 更適合在生產環境中直接部署,降低了初期調校的工作量。

從設計理念來看,CRS 採用的是黑名單(Blacklist)方法,也就是定義「什麼是攻擊」,而不是定義「什麼是合法請求」。規則集由大量已知攻擊中使用的程式碼片段(Attack Signatures)組成,當請求中出現這些特徵時,就會觸發相應的規則。這種方法的優點是部署簡單、通用性高,但缺點是無法防禦未知的攻擊手法,且可能產生誤報。

CRS 的另一個重要設計特點是採用異常評分模型(Anomaly Scoring Model),而不是單一規則觸發就立即阻擋。每條規則被觸發時會累加一定的分數,只有當累積分數超過設定的閾值時,請求才會被阻擋。這種設計提供了更細緻的風險評估能力,減少了因單一規則誤判而導致合法請求被阻擋的情況。

CRS 檔案結構與規則分類體系

理解 CRS 的檔案結構對於有效配置和調校規則集至關重要。CRS 託管在 GitHub 上,主要的專案位置是 github.com/coreruleset/coreruleset(原先位於 SpiderLabs 下,後來移轉至 coreruleset 組織)。下載並解壓縮後,會看到以下主要的目錄和檔案結構。

主要配置檔案 crs-setup.conf.example 是整個規則集的核心配置檔,它定義了異常評分閾值、警戒級別、要排除的規則、地理位置設定等關鍵參數。在實際部署時,需要將此檔案複製為 crs-setup.conf 並根據需求進行調整。

規則檔案儲存在 rules/ 目錄中,按照數字編號進行分類。這種編號系統不僅便於管理,也定義了規則的執行順序。以下是各數字區間的規則類別說明。

900 系列檔案是排除規則(Exclusion Rules),用於定義在特定情況下不應套用的規則。這些檔案對於減少誤報非常重要,特別是當您有已知的合法請求模式會觸發某些規則時。

910 系列檔案專門用於檢測惡意客戶端,包括已知的掃描器、爬蟲程式和機器人。這些規則會檢查 User-Agent 標頭、請求模式等特徵,識別非人類的自動化訪問。

920 系列檔案負責檢測協定違規(Protocol Violations),例如格式錯誤的 HTTP 請求、違反 HTTP 規範的標頭等。這類規則可以阻擋許多利用協定解析差異的攻擊。

930 和 940 系列檔案是 CRS 的核心,用於檢測各種應用程式攻擊。930 系列主要針對本地檔案包含(LFI)和遠端檔案包含(RFI)攻擊,而 940 系列則涵蓋 SQL 注入和跨站腳本(XSS)攻擊。這些規則在異常評分模型中被歸類為「嚴重」(Critical)等級,觸發時會增加較高的分數。

950 系列檔案用於檢測輸出資料洩漏(Data Leakage),例如應用程式錯誤訊息、伺服器版本資訊等敏感資料被暴露在回應中。需要注意的是,由於 NGINX 的 ModSecurity 模組在處理回應內容方面有限制,這些規則在 NGINX 環境中可能無法完全發揮作用。

除了規則檔案,CRS 還包含多個 .data 資料檔案,這些檔案儲存規則所需的參考資料。例如 crawlers-user-agents.data 包含已知掃描器使用的 User-Agent 字串列表,restricted-files.data 列出不應被直接存取的敏感檔案路徑,sql-errors.data 包含各種資料庫的錯誤訊息特徵等。

以下是 CRS 目錄結構的示意圖:

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

package "OWASP CRS 目錄結構" {
    folder "根目錄" {
        file "crs-setup.conf.example" as setup
        file "CHANGES" as changes
        file "LICENSE" as license

        folder "rules" {
            file "REQUEST-900-EXCLUSION-RULES.conf" as r900
            file "REQUEST-910-IP-REPUTATION.conf" as r910
            file "REQUEST-911-METHOD-ENFORCEMENT.conf" as r911
            file "REQUEST-912-DOS-PROTECTION.conf" as r912
            file "REQUEST-913-SCANNER-DETECTION.conf" as r913
            file "REQUEST-920-PROTOCOL-ENFORCEMENT.conf" as r920
            file "REQUEST-930-APPLICATION-ATTACK-LFI.conf" as r930
            file "REQUEST-941-APPLICATION-ATTACK-XSS.conf" as r941
            file "REQUEST-942-APPLICATION-ATTACK-SQLI.conf" as r942
            file "REQUEST-949-BLOCKING-EVALUATION.conf" as r949
        }

        folder "util" {
            file "regexp-assemble" as regexp
            file "join-multiline-rules" as join
        }
    }
}

note right of r900
  排除規則
  減少誤報
end note

note right of r913
  掃描器檢測
  機器人識別
end note

note right of r941
  XSS 攻擊檢測
  嚴重等級
end note

note right of r942
  SQL 注入檢測
  嚴重等級
end note

@enduml

異常評分機制的運作原理

異常評分機制是 CRS 最重要的特性之一,它提供了一種更加智慧和靈活的威脅評估方法。傳統的 WAF 規則通常採用「觸發即阻擋」的模式,但這種方法容易產生誤報,因為單一的可疑特徵不一定代表真正的攻擊。異常評分機制則是累積多個可疑特徵的分數,只有當總分超過閾值時才會採取阻擋行動。

在 CRS 中,每條規則都被指定了一個嚴重等級,對應不同的異常評分值。「嚴重」(Critical)等級的規則觸發時增加 5 分,這類規則主要來自 930 和 940 系列,用於檢測明確的應用程式攻擊,如 SQL 注入和 XSS。「錯誤」(Error)等級增加 4 分,主要來自 950 系列的資料洩漏檢測規則。「警告」(Warning)等級增加 3 分,來自 910 系列的惡意客戶端檢測。「通知」(Notice)等級增加 2 分,來自 920 系列的協定違規檢測。

預設的阻擋閾值設定在 crs-setup.conf 中,入站異常評分閾值(Inbound Anomaly Score Threshold)預設為 5,這意味著只要觸發一條嚴重等級的規則,請求就會被阻擋。在實際部署中,可能需要根據應用程式的特性調整這個閾值。對於誤報較多的應用程式,可以將閾值提高到 10 或更高,讓系統在累積更多可疑特徵後才阻擋請求。

異常評分的計算發生在請求處理的不同階段。ModSecurity 將 HTTP 交易分為五個處理階段:請求標頭(Phase 1)、請求主體(Phase 2)、回應標頭(Phase 3)、回應主體(Phase 4)和日誌(Phase 5)。CRS 規則主要在 Phase 1 和 Phase 2 執行,評估入站請求的威脅程度。在這兩個階段結束後,規則 949 會計算累積的異常評分,如果超過閾值就會阻擋請求。

以下配置範例展示如何在 crs-setup.conf 中調整異常評分閾值:

# CRS 異常評分閾值配置
# 此配置定義了觸發阻擋動作所需的累積異常評分
# 數值越低越嚴格,越高則越寬鬆

# 入站異常評分閾值
# 預設值為 5,表示單一嚴重規則觸發即阻擋
# 可根據應用程式特性調整:
# - 高安全性環境:保持 5
# - 一般應用程式:設為 10(需要兩個嚴重規則或多個低等級規則)
# - 誤報較多的環境:設為 15 或更高
SecAction \
  "id:900110,\
  phase:1,\
  nolog,\
  pass,\
  t:none,\
  setvar:tx.inbound_anomaly_score_threshold=5"

# 出站異常評分閾值
# 用於資料洩漏檢測(NGINX 環境中功能有限)
SecAction \
  "id:900111,\
  phase:1,\
  nolog,\
  pass,\
  t:none,\
  setvar:tx.outbound_anomaly_score_threshold=4"

# 警戒級別設定
# 控制哪些等級的規則會生效
# 1 = 只有嚴重等級
# 2 = 嚴重和錯誤
# 3 = 嚴重、錯誤和警告
# 4 = 所有等級(包括通知)
SecAction \
  "id:900000,\
  phase:1,\
  nolog,\
  pass,\
  t:none,\
  setvar:tx.paranoia_level=1"

警戒級別(Paranoia Level)是另一個重要的配置參數,它控制規則集的嚴格程度。級別 1 只啟用最可靠、誤報率最低的規則;級別 2 增加更多規則,提供更好的保護但可能產生一些誤報;級別 3 和 4 啟用越來越嚴格的規則,適合高安全性需求但需要更多調校工作的環境。大多數生產環境建議從級別 1 開始,在確認沒有嚴重誤報後再逐步提高。

NGINX 與 ModSecurity 的整合部署

在開始安裝 CRS 之前,需要先確保 NGINX 已經正確載入 ModSecurity 動態模組。ModSecurity 3.0 架構將核心引擎(libmodsecurity)與連接器(Connector)分離,NGINX 使用專屬的 ModSecurity-nginx 連接器來整合 WAF 功能。

以下是完整的 ModSecurity 與 NGINX 整合配置流程:

# 安裝 ModSecurity 相依套件
# 這些套件提供 ModSecurity 核心功能所需的函式庫
sudo apt-get update
sudo apt-get install -y \
    apt-utils \
    autoconf \
    automake \
    build-essential \
    git \
    libcurl4-openssl-dev \
    libgeoip-dev \
    liblmdb-dev \
    libpcre++-dev \
    libtool \
    libxml2-dev \
    libyajl-dev \
    pkgconf \
    wget \
    zlib1g-dev

# 下載並編譯 ModSecurity 核心引擎
# 使用 v3/master 分支以獲取最新穩定版本
cd /opt
git clone --depth 1 -b v3/master --single-branch \
    https://github.com/SpiderLabs/ModSecurity
cd ModSecurity

# 初始化並更新子模組
# 這會下載必要的第三方相依元件
git submodule init
git submodule update

# 執行建置腳本
# 這會生成 configure 腳本
./build.sh

# 配置編譯選項
# --with-pcre 啟用 PCRE 正規表示式支援
# --with-lmdb 啟用 LMDB 持久化儲存
./configure \
    --with-pcre \
    --with-lmdb

# 編譯並安裝
# -j$(nproc) 使用所有 CPU 核心加速編譯
make -j$(nproc)
sudo make install

# 下載 ModSecurity-nginx 連接器
# 此模組將 ModSecurity 與 NGINX 整合
cd /opt
git clone --depth 1 \
    https://github.com/SpiderLabs/ModSecurity-nginx.git

# 下載與已安裝 NGINX 相同版本的原始碼
# 需要用來編譯動態模組
# 請將 1.24.0 替換為您的 NGINX 版本
NGINX_VERSION=1.24.0
wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
tar -xzvf nginx-${NGINX_VERSION}.tar.gz
cd nginx-${NGINX_VERSION}

# 配置 NGINX 以編譯 ModSecurity 動態模組
# --with-compat 確保模組與已安裝的 NGINX 相容
./configure --with-compat \
    --add-dynamic-module=/opt/ModSecurity-nginx

# 編譯動態模組
make modules

# 將編譯好的模組複製到 NGINX 模組目錄
sudo cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules/

完成 ModSecurity 模組安裝後,需要在 NGINX 配置中載入模組並啟用 WAF 功能:

# /etc/nginx/nginx.conf
# NGINX 主配置檔案

# 載入 ModSecurity 動態模組
# 此指令必須放在配置檔案的最上層,在 events 區塊之前
load_module modules/ngx_http_modsecurity_module.so;

user nginx;
worker_processes auto;

# 錯誤日誌配置
# ModSecurity 的警告和錯誤會記錄到此檔案
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # 自訂日誌格式,包含更多請求詳情
    log_format main '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    '$request_time $upstream_response_time';

    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;

    # 全域啟用 ModSecurity
    # 也可以在個別 server 或 location 區塊中啟用
    modsecurity on;

    # 指定 ModSecurity 主配置檔案路徑
    modsecurity_rules_file /etc/nginx/modsec/main.conf;

    include /etc/nginx/conf.d/*.conf;
}

OWASP CRS 安裝與基本配置

現在可以開始安裝 OWASP CRS。建議從 GitHub 下載最新的穩定版本,而不是使用作業系統套件管理器提供的版本,因為後者通常較為過時。

# 下載 OWASP CRS 最新版本
# 請至 GitHub 確認最新版本號
cd /opt
CRS_VERSION=3.3.5
wget https://github.com/coreruleset/coreruleset/archive/v${CRS_VERSION}.tar.gz

# 解壓縮並移動到適當位置
tar -xzvf v${CRS_VERSION}.tar.gz
sudo mv coreruleset-${CRS_VERSION} /usr/local/owasp-crs

# 建立配置檔案
# 從範例檔案複製,保留原始檔案作為參考
cd /usr/local/owasp-crs
sudo cp crs-setup.conf.example crs-setup.conf

# 如果有特定應用程式需要排除規則
# 也需要複製相應的排除規則範例檔案
sudo cp rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example \
    rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
sudo cp rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example \
    rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf

接下來建立 ModSecurity 主配置檔案,整合所有規則:

# /etc/nginx/modsec/main.conf
# ModSecurity 主配置檔案
# 此檔案整合所有 ModSecurity 和 CRS 配置

# 包含 ModSecurity 推薦配置
# 這個檔案設定 ModSecurity 核心引擎的行為
Include /etc/nginx/modsec/modsecurity.conf

# 設定預設動作
# phase:1 處理請求標頭
# phase:2 處理請求主體
# deny 表示阻擋符合規則的請求
# status:403 回傳 HTTP 403 Forbidden
# log 記錄到錯誤日誌
# auditlog 記錄到稽核日誌
SecDefaultAction "phase:1,log,auditlog,deny,status:403"
SecDefaultAction "phase:2,log,auditlog,deny,status:403"

# 包含 OWASP CRS 設定檔
# 此檔案定義異常評分閾值、警戒級別等參數
Include /usr/local/owasp-crs/crs-setup.conf

# 包含 CRS 前置排除規則
# 這些規則在主要規則之前執行,用於排除已知的誤報
Include /usr/local/owasp-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf

# 包含所有 CRS 規則
# 使用萬用字元載入 rules 目錄中的所有 .conf 檔案
Include /usr/local/owasp-crs/rules/*.conf

ModSecurity 核心配置檔案 /etc/nginx/modsec/modsecurity.conf 需要進行以下關鍵設定:

# /etc/nginx/modsec/modsecurity.conf
# ModSecurity 核心引擎配置

# 啟用規則引擎
# On = 啟用並阻擋攻擊
# DetectionOnly = 只檢測不阻擋(適合初期測試)
# Off = 完全停用
SecRuleEngine On

# 請求主體處理設定
# 必須啟用才能檢查 POST 資料
SecRequestBodyAccess On

# 請求主體大小限制
# 超過此大小的請求將被拒絕
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072

# 當請求主體超過限制時的處理方式
# Reject = 拒絕請求
# ProcessPartial = 處理已接收的部分
SecRequestBodyLimitAction Reject

# 回應主體處理設定
# NGINX 環境中建議停用以提升效能
SecResponseBodyAccess Off

# 暫存目錄設定
# 用於處理大型請求主體
SecTmpDir /tmp/
SecDataDir /tmp/

# 稽核日誌設定
# 記錄所有相關的安全事件
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABIJDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/nginx/modsec_audit.log

# 調試日誌設定
# 0 = 停用
# 1-9 = 不同詳細程度
SecDebugLog /var/log/nginx/modsec_debug.log
SecDebugLogLevel 0

# 引數分隔符
# 定義 URL 查詢字串中的參數分隔字元
SecArgumentSeparator &

# Cookie 格式版本
# 0 = Netscape cookies
# 1 = RFC 6265 cookies
SecCookieFormat 0

# Unicode 對應檔案
# 用於 Unicode 正規化處理
SecUnicodeMapFile unicode.mapping 20127

# 狀態引擎
# 追蹤 IP 位址和 Session 的狀態
SecStatusEngine On

完成配置後,驗證 NGINX 配置語法並重新載入:

# 測試 NGINX 配置語法
# 確保沒有語法錯誤
sudo nginx -t

# 如果測試通過,重新載入配置
# 使用 reload 而非 restart 以避免服務中斷
sudo nginx -s reload

# 檢查 ModSecurity 是否正確載入
# 查看錯誤日誌中的初始化訊息
sudo tail -f /var/log/nginx/error.log | grep -i modsecurity

使用 Nikto 進行安全掃描測試

Nikto 是一個廣泛使用的開源 Web 伺服器掃描工具,它可以檢測各種已知的漏洞、配置錯誤和潛在的安全問題。使用 Nikto 對部署了 CRS 的 NGINX 進行掃描,可以驗證規則集的有效性,並識別可能需要調整的規則。

# 安裝 Nikto
# Nikto 使用 Perl 編寫,需要 Perl 執行環境
git clone https://github.com/sullo/nikto
cd nikto

# 執行基本掃描
# -h 指定目標主機
# 這會發送數千個測試請求
perl program/nikto.pl -h localhost

# 執行更詳細的掃描
# -o 輸出結果到檔案
# -Format htm 使用 HTML 格式
# -Tuning x 指定測試類別
perl program/nikto.pl -h localhost \
    -o nikto_report.html \
    -Format htm \
    -Tuning 123456789abcde

在未安裝 CRS 的情況下執行 Nikto 掃描,通常會報告數百個潛在漏洞。這些報告包括各種測試項目,例如尋找已知的易受攻擊檔案、嘗試 XSS 攻擊、探測預設管理介面等。安裝 CRS 後,大部分這些惡意請求會被識別並阻擋,掃描報告中的項目數量會大幅減少。

以下是 CRS 防護流程的示意圖:

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

participant "攻擊者\n(Nikto)" as attacker
participant "NGINX" as nginx
participant "ModSecurity\nCRS" as modsec
participant "後端\n應用程式" as backend

attacker -> nginx : HTTP 請求\n(含惡意 Payload)

nginx -> modsec : 轉發請求\n進行安全檢查

modsec -> modsec : Phase 1\n檢查請求標頭

alt 標頭檢查失敗
    modsec -> nginx : 阻擋請求\n(異常評分超標)
    nginx -> attacker : HTTP 403 Forbidden
else 標頭檢查通過
    modsec -> modsec : Phase 2\n檢查請求主體

    alt 主體檢查失敗
        modsec -> nginx : 阻擋請求\n(偵測到攻擊特徵)
        nginx -> attacker : HTTP 403 Forbidden
    else 所有檢查通過
        modsec -> nginx : 允許請求
        nginx -> backend : 轉發請求
        backend -> nginx : 回應
        nginx -> attacker : HTTP 200 OK
    end
end

@enduml

調整規則以降低誤報率

在實際部署中,CRS 可能會產生一些誤報,將合法請求誤判為攻擊。這是黑名單式規則集的固有限制,需要透過適當的調校來解決。以下是幾種常見的調校策略。

第一種情況是基於 User-Agent 的誤報。CRS 包含檢測已知掃描器和機器人的規則,這些規則會檢查 User-Agent 標頭中的特定字串。如果您的應用程式有合法的自動化客戶端使用了這些字串,就會被錯誤阻擋。

# /usr/local/owasp-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
# 在此檔案中新增排除規則

# 排除特定 IP 位址的所有規則檢查
# 適用於已知的信任來源,如監控系統
SecRule REMOTE_ADDR "@ipMatch 10.0.0.100,192.168.1.0/24" \
    "id:1000,\
    phase:1,\
    pass,\
    nolog,\
    ctl:ruleEngine=Off"

# 排除特定 URI 路徑的規則檢查
# 適用於已知會觸發誤報的 API 端點
SecRule REQUEST_URI "@beginsWith /api/webhook" \
    "id:1001,\
    phase:1,\
    pass,\
    nolog,\
    ctl:ruleRemoveById=942100"

# 排除特定參數的 SQL 注入檢查
# 適用於需要接受類似 SQL 語法的欄位
SecRule ARGS:code "@rx .*" \
    "id:1002,\
    phase:2,\
    pass,\
    nolog,\
    ctl:ruleRemoveTargetById=942100;ARGS:code"

第二種情況是應用程式特定的誤報。某些應用程式的正常功能可能包含類似攻擊特徵的內容。例如,程式碼編輯器可能需要接受包含 <script> 標籤的輸入,CMS 系統可能需要接受包含 SQL 關鍵字的搜尋查詢。

# 針對特定應用程式的排除規則範例

# WordPress 特定排除
# WordPress 的 AJAX 端點經常觸發誤報
SecRule REQUEST_URI "@beginsWith /wp-admin/admin-ajax.php" \
    "id:1100,\
    phase:1,\
    pass,\
    nolog,\
    ctl:ruleRemoveById=941100-941999"

# API 端點排除
# JSON API 可能包含需要特殊處理的資料格式
SecRule REQUEST_URI "@beginsWith /api/v1/" \
    "id:1101,\
    phase:1,\
    pass,\
    nolog,\
    ctl:ruleRemoveById=920420,\
    ctl:ruleRemoveById=921110"

# 檔案上傳端點排除
# 檔案內容檢查可能產生誤報
SecRule REQUEST_URI "@streq /upload" \
    "id:1102,\
    phase:1,\
    pass,\
    nolog,\
    ctl:requestBodyProcessor=URLENCODED,\
    ctl:ruleRemoveById=200002"

第三種調校策略是調整異常評分閾值。如果發現大量請求的異常評分接近但未超過阻擋閾值,可能表示閾值設定過高或過低。透過分析稽核日誌中的異常評分分布,可以找到最適合的閾值。

# 分析異常評分分布
# 從稽核日誌中提取所有異常評分
grep "Inbound Anomaly Score" /var/log/nginx/modsec_audit.log | \
    awk -F'=' '{print $NF}' | \
    sort -n | \
    uniq -c | \
    sort -rn

# 輸出範例:
#   1523 0      <- 大多數請求評分為 0(正常)
#    342 5      <- 觸發一個嚴重規則
#     89 10     <- 觸發兩個嚴重規則
#     12 15     <- 可能的攻擊
#      3 25     <- 明確的攻擊

增強 XSS 攻擊防護

跨站腳本攻擊(XSS)是最常見的 Web 應用程式攻擊之一。CRS 包含多條規則來檢測 XSS 攻擊,但預設配置可能未涵蓋所有攻擊向量。特別是,某些規則預設不檢查 URI 中的 XSS payload,因為這可能產生較多誤報。

要增強 XSS 防護,可以修改相關規則以檢查更多的請求元素:

# 修改 REQUEST-941-APPLICATION-ATTACK-XSS.conf
# 在規則變數列表中新增 REQUEST_URI

# 找到規則 941160(XSS 使用 script 標籤)
# 原始規則:
# SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|...

# 修改為包含 REQUEST_URI:
SecRule REQUEST_URI|REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|\
    REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* \
    "@rx (?i)<script[^>]*>[\s\S]*?" \
    "id:941160,\
    phase:2,\
    block,\
    capture,\
    t:none,t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls,\
    msg:'NoScript XSS InjectionChecker: HTML Injection',\
    logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\
    tag:'application-multi',\
    tag:'language-multi',\
    tag:'platform-multi',\
    tag:'attack-xss',\
    tag:'paranoia-level/1',\
    tag:'OWASP_CRS',\
    tag:'capec/1000/152/242',\
    ver:'OWASP_CRS/3.3.5',\
    severity:'CRITICAL',\
    setvar:'tx.xss_score=+%{tx.critical_anomaly_score}',\
    setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

# 同樣修改規則 941320(XSS 使用 IE 濾鏡)
# 新增 REQUEST_URI 到檢查變數列表

修改規則後,需要測試確保沒有產生新的誤報:

# 測試 XSS 防護
# 這些請求應該被阻擋

# 測試基本 script 標籤注入
curl -v "http://localhost/?q=<script>alert('xss')</script>"

# 測試編碼後的 XSS
curl -v "http://localhost/?q=%3Cscript%3Ealert('xss')%3C/script%3E"

# 測試事件處理器 XSS
curl -v "http://localhost/?q=<img src=x onerror=alert('xss')>"

# 測試 SVG XSS
curl -v "http://localhost/?q=<svg/onload=alert('xss')>"

以下是 XSS 攻擊檢測流程的示意圖:

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

start

:接收 HTTP 請求;

:Phase 1\n檢查請求標頭;

if (Request-URI\n包含 XSS 特徵?) then (是)
    :觸發規則 941160\n增加異常評分 +5;
else (否)
endif

if (Cookie\n包含 XSS 特徵?) then (是)
    :觸發規則 941100\n增加異常評分 +5;
else (否)
endif

:Phase 2\n檢查請求主體;

if (POST 參數\n包含 XSS 特徵?) then (是)
    :觸發多條 XSS 規則\n累加異常評分;
else (否)
endif

:計算總異常評分;

if (評分 >= 閾值?) then (是)
    :記錄攻擊詳情;
    :回傳 HTTP 403;
    stop
else (否)
    :允許請求通過;
    :轉發至後端;
    stop
endif

@enduml

Trustwave SpiderLabs 商業規則集整合

對於需要更專業防護的組織,Trustwave SpiderLabs 提供商業授權的 ModSecurity 規則集。這套規則集由專業安全研究團隊維護,提供以下優勢:針對特定應用程式(如 WordPress、Joomla、SharePoint)的專用規則、更快的威脅情報更新、以及專業技術支援。

商業規則集的一個重要特點是支援遠端規則載入(SecRemoteRules)。這意味著規則不需要手動下載和更新,ModSecurity 會自動從 Trustwave 伺服器獲取最新版本。這大大簡化了規則管理,確保系統始終具有最新的防護能力。

以下是整合 Trustwave SpiderLabs 規則集的配置方式:

# /etc/nginx/modsec/main.conf
# 整合 Trustwave SpiderLabs 商業規則集

# 包含 ModSecurity 推薦配置
Include /etc/nginx/modsec/modsecurity.conf

# 設定預設動作
SecDefaultAction "phase:1,log,auditlog,deny,status:403"
SecDefaultAction "phase:2,log,auditlog,deny,status:403"

# 遠端規則載入失敗時的處理方式
# Abort = 停止 NGINX 啟動
# Warn = 記錄警告但繼續運行
SecRemoteRulesFailAction Warn

# 載入 Trustwave SpiderLabs 規則
# license-key 從 ModSecurity Dashboard 取得
# URL 由 Trustwave 提供
SecRemoteRules your-license-key https://rules.trustwave.com/...

# 仍然可以同時使用 OWASP CRS
# 兩套規則可以互補
Include /usr/local/owasp-crs/crs-setup.conf
Include /usr/local/owasp-crs/rules/*.conf

使用 SecRemoteRules 時需要注意幾個重要事項。首先,每次 NGINX 重新載入或重啟時,都會從遠端伺服器下載規則,這會增加啟動時間。在高可用性環境中,這可能影響故障轉移的速度。其次,如果遠端伺服器無法連接,根據 SecRemoteRulesFailAction 的設定,可能導致 NGINX 無法啟動或在沒有規則保護的情況下運行。建議在生產環境中設定為 Warn,並實施監控以確保規則正確載入。

Trustwave 還提供 ModSecurity Dashboard,這是一個網頁管理介面,可以透過圖形化介面管理規則配置、查看安全事件、生成報表等。Dashboard 簡化了規則調校過程,不需要直接編輯配置檔案,適合需要集中管理多台 WAF 的環境。

Project Honeypot IP 信譽系統整合

Project Honeypot 是一個社群驅動的威脅情報專案,維護著一個龐大的惡意 IP 位址資料庫。這些 IP 位址透過部署在全球的「蜜罐」網路收集,記錄了發送垃圾郵件、進行掃描或執行其他惡意活動的來源。ModSecurity 可以與 Project Honeypot 整合,自動阻擋來自這些已知惡意 IP 的請求。

Project Honeypot 使用 DNS 查詢介面提供 IP 信譽資訊。當收到請求時,ModSecurity 會將來源 IP 位址轉換為特定格式的 DNS 查詢,向 Project Honeypot 的 DNS 伺服器查詢。回應包含該 IP 的威脅評分、類型(垃圾郵件發送者、爬蟲、可疑等)和上次活動時間。

以下是整合 Project Honeypot 的配置步驟:

# 首先,在 Project Honeypot 網站註冊並取得 API 金鑰
# https://www.projecthoneypot.org/

# /etc/nginx/modsec/main.conf
# 新增 Project Honeypot 整合配置

# 啟用 IP 信譽檢查
SecHttpBlKey your-project-honeypot-api-key

# CRS 中的 IP 信譽規則會使用此金鑰
# 確保載入 REQUEST-910-IP-REPUTATION.conf

# 在 crs-setup.conf 中啟用 IP 信譽檢查
# 找到以下區塊並取消註解:

# -- [[ HTTP Blacklist - BL checks ]] -----------------------------------------------
#
# 啟用 Project Honeypot 檢查
SecAction \
 "id:900600,\
  phase:1,\
  nolog,\
  pass,\
  t:none,\
  setvar:tx.block_search_ip=1,\
  setvar:tx.block_suspicious_ip=1,\
  setvar:tx.block_harvester_ip=1,\
  setvar:tx.block_spammer_ip=1"

IP 信譽規則的配置選項說明如下。block_search_ip 阻擋搜尋引擎爬蟲(通常應設為 0,除非您確定不需要搜尋引擎索引)。block_suspicious_ip 阻擋可疑 IP,這些 IP 有一些惡意活動但不確定是否為真正的威脅。block_harvester_ip 阻擋電子郵件收集器,這些 IP 被發現在收集網頁上的電子郵件地址。block_spammer_ip 阻擋垃圾郵件發送者。

以下是 Project Honeypot 整合流程的示意圖:

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

participant "客戶端\n203.0.113.50" as client
participant "NGINX\nModSecurity" as nginx
participant "Project\nHoneypot DNS" as honeypot
participant "後端\n應用程式" as backend

client -> nginx : HTTP 請求

nginx -> honeypot : DNS 查詢\n50.113.0.203.apikey.dnsbl.httpbl.org

honeypot -> nginx : DNS 回應\n127.3.5.1

nginx -> nginx : 解析回應\n天數=3, 威脅=5, 類型=1

alt 威脅評分 > 閾值
    nginx -> client : HTTP 403\n(IP 被列為惡意)
else 威脅評分 <= 閾值
    nginx -> backend : 轉發請求
    backend -> nginx : 回應
    nginx -> client : HTTP 200
end

note over honeypot
  回應格式: 127.A.B.C
  A = 上次活動天數
  B = 威脅評分 (0-255)
  C = 類型 (位元遮罩)
end note

@enduml

需要注意的是,每次 DNS 查詢都會增加請求延遲。在高流量環境中,這可能影響效能。建議啟用 DNS 快取,或使用本地 DNS 解析器來減少延遲。此外,Project Honeypot 的資料庫雖然龐大,但不可能涵蓋所有惡意 IP,因此應將其視為多層防禦的一部分,而非唯一的防護措施。

稽核日誌分析與安全監控

有效的安全監控依賴於良好的日誌管理。ModSecurity 提供詳細的稽核日誌功能,記錄所有安全事件的完整資訊,包括請求和回應的標頭、主體、觸發的規則等。這些日誌對於事件調查、規則調校和安全分析至關重要。

# /etc/nginx/modsec/modsecurity.conf
# 稽核日誌配置

# 稽核引擎模式
# On = 記錄所有交易
# Off = 停用稽核日誌
# RelevantOnly = 只記錄相關事件(推薦)
SecAuditEngine RelevantOnly

# 定義「相關」的狀態碼
# 記錄所有 5xx 錯誤和非 404 的 4xx 錯誤
SecAuditLogRelevantStatus "^(?:5|4(?!04))"

# 稽核日誌的組成部分
# A = 稽核日誌標頭
# B = 請求標頭
# C = 請求主體
# D = 保留
# E = 中間回應主體(目前不支援)
# F = 最終回應標頭
# G = 保留
# H = 稽核日誌尾端(包含觸發的規則)
# I = 請求主體(替代 C,尊重 SecUploadDir)
# J = 保留
# K = 符合規則的列表(完整的 SecRule 內容)
# Z = 最終分隔符
SecAuditLogParts ABIJDEFHZ

# 稽核日誌格式
# Serial = 單一檔案
# Concurrent = 每個交易一個檔案
SecAuditLogType Serial
SecAuditLog /var/log/nginx/modsec_audit.log

# 如果使用 Concurrent 模式,指定日誌目錄
# SecAuditLogStorageDir /var/log/nginx/modsec_audit/

分析稽核日誌可以獲得寶貴的安全洞察。以下是一些常用的分析命令:

# 查看最近被阻擋的請求
grep -A 20 "Action: Intercepted" /var/log/nginx/modsec_audit.log | tail -100

# 統計觸發最多的規則
grep "id \"" /var/log/nginx/modsec_audit.log | \
    sed 's/.*id "\([0-9]*\)".*/\1/' | \
    sort | uniq -c | sort -rn | head -20

# 找出攻擊來源 IP 排行
grep "\-A\-\-" /var/log/nginx/modsec_audit.log | \
    awk '{print $1}' | \
    sort | uniq -c | sort -rn | head -20

# 分析攻擊類型分布
grep "tag:" /var/log/nginx/modsec_audit.log | \
    grep -o 'attack-[a-z]*' | \
    sort | uniq -c | sort -rn

# 查看特定 IP 的所有請求
grep "203.0.113.50" /var/log/nginx/modsec_audit.log

為了更有效地管理和分析日誌,建議將 ModSecurity 日誌整合到集中式日誌管理系統,如 ELK Stack(Elasticsearch、Logstash、Kibana)或 Splunk。這樣可以實現即時監控、自動告警和歷史趨勢分析。

以下是將 ModSecurity 日誌送至 Elasticsearch 的 Logstash 配置範例:

# /etc/logstash/conf.d/modsecurity.conf
# Logstash 配置:解析 ModSecurity 稽核日誌

input {
  file {
    path => "/var/log/nginx/modsec_audit.log"
    start_position => "beginning"
    sincedb_path => "/var/lib/logstash/sincedb_modsec"

    # ModSecurity 稽核日誌使用 "--xxxxxxxx-Z--" 作為記錄分隔符
    # 這裡使用多行編解碼器來正確解析
    codec => multiline {
      pattern => "^--[a-fA-F0-9]{8}-A--$"
      negate => true
      what => "previous"
    }
  }
}

filter {
  # 解析 ModSecurity 稽核日誌格式
  grok {
    match => {
      "message" => [
        "--(?<transaction_id>[a-fA-F0-9]{8})-A--\n\[%{DATA:timestamp}\] %{DATA:unique_id} %{IP:source_ip} %{INT:source_port} %{IP:dest_ip} %{INT:dest_port}",
        "(?<section>[A-Z])--\n(?<content>[\s\S]*?)(?=\n--[a-fA-F0-9]{8}-)"
      ]
    }
  }

  # 解析時間戳
  date {
    match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
    target => "@timestamp"
  }

  # 新增地理位置資訊
  geoip {
    source => "source_ip"
    target => "geoip"
  }

  # 提取觸發的規則 ID
  if [content] =~ /id "/ {
    grok {
      match => { "content" => 'id "(?<rule_id>\d+)"' }
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "modsecurity-%{+YYYY.MM.dd}"
  }
}

效能最佳化與生產環境部署

在生產環境中部署 ModSecurity 和 CRS 需要注意效能影響。WAF 會對每個請求進行規則比對,這不可避免地會增加延遲和資源消耗。以下是一些最佳化建議。

首先,只啟用必要的規則。CRS 包含大量規則,但不是所有規則都適用於每個應用程式。分析您的應用程式需要防護的攻擊類型,停用不相關的規則類別。例如,如果您的應用程式不使用 PHP,就不需要啟用 PHP 特定的規則。

# 在 crs-setup.conf 中停用不需要的規則類別
# 透過設定警戒級別為 1 來減少規則數量

SecAction \
  "id:900000,\
  phase:1,\
  nolog,\
  pass,\
  t:none,\
  setvar:tx.paranoia_level=1"

# 完全停用特定規則檔案
# 在 main.conf 中使用 SecRuleRemoveById 或不載入檔案

# 例如,停用 PHP 注入規則(如果不使用 PHP)
SecRuleRemoveById 933100-933199

其次,最佳化正規表示式。CRS 中的許多規則使用複雜的正規表示式,這些表示式可能消耗大量 CPU。ModSecurity 3.0 引入了 Hyperscan 支援,這是 Intel 開發的高效能正規表示式引擎,可以顯著提升規則比對速度。如果您的伺服器使用 Intel 處理器,建議編譯 ModSecurity 時啟用 Hyperscan 支援。

# 編譯 ModSecurity 時啟用 Hyperscan
# 首先安裝 Hyperscan 函式庫
sudo apt-get install libhyperscan-dev

# 重新配置和編譯 ModSecurity
cd /opt/ModSecurity
./configure --with-pcre --with-lmdb --with-hyperscan
make -j$(nproc)
sudo make install

第三,適當配置請求主體處理。檢查請求主體會增加記憶體使用和處理時間。對於不需要檢查的端點(如靜態檔案、監控探針),可以停用主體處理。

# 針對特定位置停用 ModSecurity
location /health {
    modsecurity off;
    return 200 'OK';
}

# 針對靜態檔案停用詳細檢查
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    modsecurity_rules '
        SecRuleEngine DetectionOnly
        SecRequestBodyAccess Off
    ';
    expires 30d;
}

第四,使用適當的日誌級別。在生產環境中,將 SecAuditEngine 設為 RelevantOnly,避免記錄所有交易。同時確保 SecDebugLogLevel 設為 0,除非正在調試問題。

以下是效能監控的建議指標:

# 監控 ModSecurity 處理時間
# 在 NGINX 日誌格式中加入請求處理時間
log_format detailed '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    'rt=$request_time uct=$upstream_connect_time '
                    'uht=$upstream_header_time urt=$upstream_response_time';

# 使用 stub_status 監控 NGINX 效能
location /nginx_status {
    stub_status;
    allow 127.0.0.1;
    deny all;
}

# 監控系統資源使用
# 定期檢查 NGINX worker 進程的 CPU 和記憶體使用
ps aux | grep nginx | grep -v grep

持續維護與規則更新策略

WAF 的有效性取決於規則的及時更新。新的攻擊手法不斷出現,舊的規則可能無法檢測這些新威脅。因此,建立規則更新流程是維護安全防護的關鍵。

對於 OWASP CRS,建議至少每季度檢查一次是否有新版本發布。更新流程應包括:在測試環境驗證新版本、檢查是否有重大變更影響現有配置、確認沒有新增嚴重誤報,然後才部署到生產環境。

# CRS 更新腳本範例
#!/bin/bash
# update-crs.sh - 更新 OWASP CRS 規則集

# 設定變數
CRS_DIR="/usr/local/owasp-crs"
BACKUP_DIR="/usr/local/owasp-crs-backup"
CRS_VERSION="3.3.5"

# 建立備份
echo "建立現有規則備份..."
cp -r $CRS_DIR $BACKUP_DIR-$(date +%Y%m%d)

# 下載新版本
echo "下載 CRS $CRS_VERSION..."
cd /tmp
wget https://github.com/coreruleset/coreruleset/archive/v${CRS_VERSION}.tar.gz

# 解壓縮到暫存目錄
tar -xzf v${CRS_VERSION}.tar.gz

# 備份自訂配置
cp $CRS_DIR/crs-setup.conf /tmp/crs-setup.conf.bak
cp $CRS_DIR/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf \
   /tmp/exclusions.conf.bak

# 更新規則
rm -rf $CRS_DIR
mv coreruleset-${CRS_VERSION} $CRS_DIR

# 還原自訂配置
cp /tmp/crs-setup.conf.bak $CRS_DIR/crs-setup.conf
cp /tmp/exclusions.conf.bak \
   $CRS_DIR/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf

# 測試 NGINX 配置
echo "測試 NGINX 配置..."
nginx -t

if [ $? -eq 0 ]; then
    echo "配置測試通過,重新載入 NGINX..."
    nginx -s reload
    echo "CRS 更新完成"
else
    echo "配置測試失敗,還原備份..."
    rm -rf $CRS_DIR
    mv $BACKUP_DIR-$(date +%Y%m%d) $CRS_DIR
    echo "已還原備份"
    exit 1
fi

除了規則更新,也應定期審查稽核日誌和安全事件,識別可能的誤報或新的攻擊模式。建立異常基線,當事件數量異常增加時觸發告警。持續的監控和調校是維護有效 WAF 防護的關鍵。

總結而言,在 NGINX 上整合 OWASP 核心規則集是建立 Web 應用程式安全防護的有效方法。透過理解 CRS 的架構設計、異常評分機制和規則分類,可以進行適當的配置和調校,在安全性和可用性之間取得平衡。結合商業規則集和 IP 信譽系統,可以建立更全面的多層防禦。然而,WAF 只是整體安全架構的一部分,還需要配合安全的程式碼開發實踐、適當的存取控制、定期的安全評估等措施,才能有效保護 Web 應用程式免受各種威脅。