FurCDN 文檔

WAF 完整規則手冊

從零開始寫 WAF 規則,涵蓋全部運算子 / 動作 / 常見場景配方。

從零開始寫 WAF 規則,涵蓋全部運算子 / 動作 / 常見場景配方。

TL;DR 三條黃金原則:

  1. 白名單放最前面(優先級小數字),保證信任流量永遠通過
  2. 新規則先 logblock,看一週命中再決定是否升級
  3. 預設 6 條規則保留,涵蓋了 80% 攻擊面

一、規則結構

每條 WAF 規則 5 個欄位:

{
  "name": "規則名稱(顯示用)",
  "conditions": [/* 條件群組陣列,見下文 */],
  "action": "block",
  "blockStatus": 403,
  "blockBody": "Blocked by FurCDN WAF",
  "priority": 10,
  "enabled": true
}

評估流程

  1. 規則按 priority 數字小的先評估
  2. 條件命中 → 執行 action
  3. action = allow → 立即放行,跳過所有後續規則
  4. action = block / challenge → 立即阻斷,跳過所有後續
  5. action = log / rateLimit 不命中 → 繼續評估下一條規則
  6. 全部規則都沒命中 → 預設放行

二、5 種 Action

1. allow(白名單)

命中即放行,跳過後續所有規則。常見用途:

  • 公司辦公室 IP 永遠通過
  • 監控掃描器 IP 不被誤封
  • 內部 API 客戶端跳過 WAF
{
  "name": "公司白名單",
  "priority": 1,
  "action": "allow",
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "ip", "operator": "cidr", "value": "203.0.113.0/24"}
    ]
  }]
}

⚠️ allow 是「徹底信任」,放行後連 rate limit、challenge、預設 block 都跳過。要謹慎用。

2. log(記錄)

命中只寫日誌不影響流量。新規則上線前先 log 看誤判率

{
  "name": "可疑 UA(觀察)",
  "priority": 100,
  "action": "log",
  "conditions": [{
    "logic": "OR",
    "conditions": [
      {"field": "header:user-agent", "operator": "contains", "value": "curl"},
      {"field": "header:user-agent", "operator": "contains", "value": "wget"}
    ]
  }]
}

跑一週後到「訪問日誌」搜命中,確認沒誤判再改 block

3. block(阻斷)

最常用。命中回 blockStatus(預設 403)+ blockBody

{
  "name": "禁止 .git 訪問",
  "priority": 5,
  "action": "block",
  "blockStatus": 404,
  "blockBody": "Not Found",
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "uri", "operator": "starts_with", "value": "/.git"}
    ]
  }]
}

用 404 而非 403 可以「裝沒這個資源」,讓掃描器以為路徑不存在。

4. rateLimit(速率限制)

命中後計數,超過閾值在窗口內封鎖。3 個額外參數:

參數說明
rateLimitCount視窗內最大請求數
rateLimitWindow視窗長度(秒)
rateLimitBlock觸發後封鎖時長(秒)

範例:登入端點 1 分鐘 5 次,超過封 10 分鐘:

{
  "name": "登入速率限制",
  "priority": 8,
  "action": "rateLimit",
  "rateLimitCount": 5,
  "rateLimitWindow": 60,
  "rateLimitBlock": 600,
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "uri", "operator": "starts_with", "value": "/api/login"},
      {"field": "method", "operator": "equals", "value": "POST"}
    ]
  }]
}

計數 key 是 clientIP + rule.id,每條規則獨立計數。

5. challenge(挑戰閘道)

命中 IP 進入「JS 挑戰頁」,通過後簽 cookie 30 分鐘有效:

  • 真實使用者幾乎無感(<1 秒 解 PoW + 自動跳轉)
  • 機器人/爬蟲無法通過(沒有 JS runtime)

最佳場景:輕度 CC 攻擊(高頻但 IP 分散),或「對全站可疑流量加道牆」。

{
  "name": "海外可疑流量挑戰",
  "priority": 50,
  "action": "challenge",
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "country", "operator": "in", "value": ["RU", "KP"]},
      {"field": "uri", "operator": "starts_with", "value": "/api/"}
    ]
  }]
}

三、10 種 Field

uri 請求路徑

不含 query string。範例 /api/login / /admin/users

query 查詢字串

不含 ?。範例 id=1&token=abc

header:<name> 指定 HTTP header

name 大小寫不敏感。常用:

  • header:user-agent
  • header:referer
  • header:cookie
  • header:host
  • header:x-forwarded-for(若使用者開了 realIpHeader,這個是真實 IP 之前的鏈路)

method

GET / POST / PUT / DELETE / OPTIONS / HEAD / PATCH

ip

已套用 realIpHeader 的真實客戶端 IP。

referer

Referer header(header:referer 的快捷方式,單獨拿出來方便用)。

body

請求 body。有大小限制:

  • 預設只讀前 64 KB
  • 大檔上傳(超過上限)的 body 不被讀,規則自動視為不命中

country

ISO 3166-1 alpha-2 國家碼:CN / US / JP / RU / KP 等。 透過 ipgeo 查得(海外用 ipinfo,國內用 qqwry)。

asn

ASN 編號(數字),如 15169(Google)/ 13335(Cloudflare)。

範例:綜合多 field

{"field": "uri", "operator": "starts_with", "value": "/api/admin"}
{"field": "header:user-agent", "operator": "contains", "value": "Bot"}
{"field": "ip", "operator": "cidr", "value": "10.0.0.0/8", "negate": true}
{"field": "country", "operator": "equals", "value": "CN"}
{"field": "asn", "operator": "in", "value": [15169, 13335]}

四、10 種 Operator + Negate

contains

子字串包含。大小寫不敏感

{"field": "header:user-agent", "operator": "contains", "value": "sqlmap"}

equals

完全相等。大小寫敏感(field 為 ASCII 字串時)。

regex

RE2 正則(Go 標準庫,不支援 backreference / lookahead)。

{"field": "query", "operator": "regex", "value": "(?i)union\\s+select"}

⚠️ 正則表達式效能低於字串比對。能用 contains / starts_with 就不要用 regex。

not_contains / not_equals

否定版本。等同 contains / equals + negate: true,擇一使用。

starts_with / ends_with

前綴 / 後綴匹配。

{"field": "uri", "operator": "starts_with", "value": "/api/v1/"}
{"field": "uri", "operator": "ends_with", "value": ".php"}

in

值在列表中。value 為陣列。

{"field": "country", "operator": "in", "value": ["RU", "KP", "IR"]}
{"field": "method", "operator": "in", "value": ["DELETE", "PATCH"]}

cidr

IP 落在 CIDR 範圍。value 為單一 CIDR 字串或字串陣列。

{"field": "ip", "operator": "cidr", "value": "192.168.0.0/16"}
{"field": "ip", "operator": "cidr", "value": ["1.2.3.0/24", "5.6.7.8/32"]}

支援 IPv4 + IPv6。

threat

命中威脅情報庫(無需 value)。

{"field": "ip", "operator": "threat"}

聚合 4 個來源:Spamhaus DROP/EDROP、FireHOL Level 1、Tor 出口、本平台自反饋。

negate: true

所有 operator 都可加 negate: true 取反。一個 flag 通用,不需另外學「not_xxx」。

{"field": "ip", "operator": "cidr", "value": "1.2.3.0/24", "negate": true}
// 等同「IP 不在 1.2.3.0/24」

五、條件群組與邏輯連接

群組(Group)

一條規則可有多個 group,group 之間是 OR(任一命中即規則命中)。

群組內條件(Conditions)

每個 condition 帶 connector(AND / OR),從左到右逐條合併:

{
  "logic": "AND",
  "conditions": [
    {"field": "uri", "operator": "starts_with", "value": "/admin/"},
    {"field": "ip", "operator": "cidr", "value": "1.2.3.0/24", "negate": true, "connector": "AND"},
    {"field": "header:authorization", "operator": "equals", "value": "", "connector": "OR"}
  ]
}

評估順序:

  1. (uri starts /admin/)
  2. ... AND (ip not in 1.2.3.0/24)
  3. ... OR (no auth header)

無運算子優先級,完全是左到右合併。要強制邏輯就用多個 group 拆。

第一條的 connector 忽略

第一條 condition 的 connector 不被讀(沒有「前一條」可合併)。fallback 到 group.logic(舊資料相容)。

多 group 範例:「管理路徑 AND 不在白名單」OR「敏感 query」

{
  "conditions": [
    {
      "logic": "AND",
      "conditions": [
        {"field": "uri", "operator": "starts_with", "value": "/admin/"},
        {"field": "ip", "operator": "cidr", "value": "1.2.3.0/24", "negate": true}
      ]
    },
    {
      "logic": "AND",
      "conditions": [
        {"field": "query", "operator": "regex", "value": "(?i)password|secret|token"}
      ]
    }
  ]
}

兩個 group 之間是 OR:管理路徑非白名單 query 含敏感關鍵字 → 命中。


六、預設 6 條規則(新建域名自動產生)

規則條件動作
掃描器攔截header:user-agent contains nmap / sqlmap / nikto / acunetix / nessusblock 403
空 User-Agentheader:user-agent equals ""block 403
SQL 注入query/body regex (?i)(union\s+select|drop\s+table|insert\s+into)block 403
XSSquery/body regex (?i)<script|javascript:|onerror=block 403
路徑穿越uri contains ../block 403
惡意 Refererreferer contains 已知惡意網站列表block 403

保留預設規則(可細調但別全砍),這些是最低標準防護。


七、常見場景配方

1. 公司辦公室白名單

{
  "name": "辦公室白名單",
  "priority": 1,
  "action": "allow",
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "ip", "operator": "cidr", "value": ["203.0.113.0/24", "198.51.100.0/24"]}
    ]
  }]
}

2. 封鎖整個國家

{
  "name": "封鎖俄朝伊",
  "priority": 10,
  "action": "block",
  "blockStatus": 403,
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "country", "operator": "in", "value": ["RU", "KP", "IR"]}
    ]
  }]
}

3. 命中威脅情報庫即封

{
  "name": "已知惡意 IP",
  "priority": 5,
  "action": "block",
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "ip", "operator": "threat"}
    ]
  }]
}

4. API 速率限制

{
  "name": "API 速率限制",
  "priority": 20,
  "action": "rateLimit",
  "rateLimitCount": 60,
  "rateLimitWindow": 60,
  "rateLimitBlock": 300,
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "uri", "operator": "starts_with", "value": "/api/"}
    ]
  }]
}

每 IP 1 分鐘 60 次,超過封 5 分鐘。

5. 登入端點嚴格限速

{
  "name": "登入嚴格限速",
  "priority": 15,
  "action": "rateLimit",
  "rateLimitCount": 5,
  "rateLimitWindow": 60,
  "rateLimitBlock": 1800,
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "uri", "operator": "equals", "value": "/api/auth/login"},
      {"field": "method", "operator": "equals", "value": "POST"}
    ]
  }]
}

每 IP 1 分鐘 5 次,觸發封 30 分鐘。

{
  "name": "圖片防盜鏈",
  "priority": 30,
  "action": "block",
  "blockStatus": 403,
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "uri", "operator": "regex", "value": "\\.(jpg|jpeg|png|gif|webp)$"},
      {"field": "referer", "operator": "starts_with", "value": "https://example.com", "negate": true},
      {"field": "referer", "operator": "equals", "value": "", "negate": true}
    ]
  }]
}

只允許 https://example.com 來源訪問圖片(空 Referer 也擋)。

7. 海外可疑流量加挑戰

{
  "name": "海外加挑戰",
  "priority": 40,
  "action": "challenge",
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "country", "operator": "equals", "value": "CN", "negate": true}
    ]
  }]
}

8. WordPress / phpMyAdmin 路徑掃描器

{
  "name": "PHP 掃描器擋",
  "priority": 5,
  "action": "block",
  "blockStatus": 404,
  "conditions": [{
    "logic": "OR",
    "conditions": [
      {"field": "uri", "operator": "ends_with", "value": ".php"},
      {"field": "uri", "operator": "contains", "value": "/wp-admin/"},
      {"field": "uri", "operator": "contains", "value": "/wp-login.php"},
      {"field": "uri", "operator": "contains", "value": "/phpmyadmin/"}
    ]
  }]
}

如果你用的不是 PHP / WordPress,所有這些 URL 都是掃描器探測。404 偽裝路徑不存在。

9. 「只允許特定 ASN」

{
  "name": "只允許 ISP 訪問",
  "priority": 100,
  "action": "block",
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "asn", "operator": "in", "value": [4134, 4837, 9808], "negate": true}
    ]
  }]
}

4134 = 中國電信,4837 = 中國聯通,9808 = 中國移動。其他 ASN 全擋。

{
  "name": "需登入",
  "priority": 25,
  "action": "block",
  "blockStatus": 401,
  "conditions": [{
    "logic": "AND",
    "conditions": [
      {"field": "uri", "operator": "starts_with", "value": "/dashboard/"},
      {"field": "header:cookie", "operator": "contains", "value": "session_id=", "negate": true}
    ]
  }]
}

八、威脅情報庫

來源

來源內容更新頻率
spamhaus_dropSpamhaus 全球已知惡意網段4 小時
spamhaus_edropExtended DROP(更廣)4 小時
firehol_level1FireHOL 一級惡意 IP(最嚴格)4 小時
tor_exitTor 出口節點4 小時
self_feedback本平台 24h 高頻失敗 IP(自反饋)即時

在 WAF 中使用

{"field": "ip", "operator": "threat"}

任何來源命中即視為惡意。

使用者唯讀瀏覽

儀表板 > 威脅情報庫:

  • 搜尋 IP / CIDR
  • 過濾來源
  • 包含過期項(可選)
  • 限速 60 次 / 分鐘(防爬)

管理員額外能做的

  • 手動加白(刪除某條惡意紀錄)
  • 立即觸發刷新拉所有來源
  • self_feedback 來源不會自動重加(避免循環)

九、故障排查

規則寫了但不生效

  1. 域名 WAF 開了嗎?域名編輯 > 基本設定 > WAF 開關
  2. 規則的 enabled=true?
  3. 優先級對嗎?可能被前面的 allow 跳過了
  4. 看訪問日誌:該請求的「WAF 命中規則」欄位,有就是命中,沒就是真的沒匹配上
  5. field 拼寫:header:User-Agent vs header:user-agent(後者才對,小寫)

誤封正常使用者

  1. 看訪問日誌篩選 status 403 / 429 / 503
  2. 看哪條規則命中
  3. 改成 log 觀察一週,或加 allow 白名單
  4. 規則可能太嚴(如 SQL 注入規則誤判含 select 關鍵字的正常 query)

Rate limit 觸發後永遠進不來

  • rateLimitBlock 是封鎖時長(秒),設太大就會封很久
  • 計數 key 是 clientIP + rule.id,所以用戶換 IP 即可繞
  • 短時間想解封:暫停規則 → 觸發過的封鎖會自動 expire

Challenge 通過後又被擋

  • challenge cookie 30 分鐘有效;30 分鐘後再來會重新挑戰
  • 若使用者瀏覽器禁 cookie,會永遠在挑戰循環

規則太多影響性能

  • 預設規則跑全部 6 條 + 自訂規則:單次 WAF 評估 <1ms,通常不是瓶頸
  • 真有性能問題:減少 regex 規則,改用 contains / starts_with
  • body field 規則只在 POST/PUT 觸發,GET 不影響

看不到想要的場景?開工單講具體需求,我們幫你寫規則。

© 2023-2026 SLOWSPEED NETWORK LLC. 版權所有

langya.io 驅動

On this page