mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 05:56:38 +08:00
[优化] 白名单优先级高于黑名单
This commit is contained in:
@@ -22,6 +22,8 @@ sidebar: false
|
||||
|
||||
### 变更
|
||||
|
||||
- WAF 白名单调整为准入名单语义:存在白名单规则时,未命中白名单的请求会被拦截
|
||||
|
||||
---
|
||||
|
||||
## [v2.3.2] - 2026-06-04
|
||||
|
||||
@@ -110,7 +110,8 @@ flowchart TD
|
||||
D -- 是 (匹配成功) --> E[放行请求 - ALLOW]
|
||||
D -- 否 --> F{匹配到国家/地区地域白名单?}
|
||||
F -- 是 (匹配成功) --> E
|
||||
F -- 否 --> G{匹配到 IP 黑名单 / 黑名单 IP 组?}
|
||||
F -- 否且已配置任意白名单 --> H
|
||||
F -- 否且未配置白名单 --> G{匹配到 IP 黑名单 / 黑名单 IP 组?}
|
||||
G -- 是 (匹配成功) --> H[阻断请求 - BLOCK]
|
||||
G -- 否 --> I{匹配到国家/地区地域黑名单?}
|
||||
I -- 是 (匹配成功) --> H
|
||||
@@ -123,8 +124,8 @@ flowchart TD
|
||||
|
||||
### 2. 判决步骤细则
|
||||
1. **白名单前置**:
|
||||
为了防止误杀以及保障核心回源流量(如搜索引擎蜘蛛、CDN 回源 IP、办公区出口)的顺畅,WAF **优先匹配 IP 白名单与地域白名单**。一旦白名单匹配成功,直接绕过后续的所有黑名单检测和 CC 挑战,立刻放行。
|
||||
为了防止误杀以及保障核心回源流量(如搜索引擎蜘蛛、CDN 回源 IP、办公区出口)的顺畅,WAF **优先匹配 IP 白名单与地域白名单**。一旦白名单匹配成功,直接绕过后续的所有黑名单检测和 CC 挑战,立刻放行。只要当前生效规则组配置了任意白名单,请求未命中全部白名单时会被拦截,白名单在这种情况下表现为准入名单。
|
||||
2. **黑名单强力阻断**:
|
||||
如果在白名单判定中未被捕获,请求将进入黑名单漏斗。一旦请求源 IP 命中 IP 黑名单、命中引用的黑名单 IP 组、或是处于被禁止的国家/地区范围内,Lua 引擎立即将 `ngx.ctx.openflare_waf_blocked` 标记设为 `true`。
|
||||
3. **输出响应**:
|
||||
命中黑名单后,Lua 提取匹配到规则组的 `block_status_code`(默认返回 418 / 403)和 `block_response_body`(拦截页面 HTML),通过 `ngx.say()` 输出响应体并执行 `ngx.exit(status)` 平滑退出请求,防止请求继续向后透传。
|
||||
命中黑名单后,Lua 提取匹配到规则组的 `block_status_code`(默认返回 418 / 403)和 `block_response_body`(拦截页面 HTML),通过 `ngx.say()` 输出响应体并执行 `ngx.exit(status)` 平滑退出请求,防止请求继续向后透传。
|
||||
|
||||
@@ -130,7 +130,8 @@ IP 组是进行大批量 IP 过滤的基石。OpenFlare 提供了极富弹性的
|
||||
│ (否)
|
||||
▼
|
||||
2. 匹配国家 / 省份地域白名单? ────────(是)─────► [ 放行 (ALLOW) ]
|
||||
│ (否)
|
||||
│ (否,且已配置任意白名单) ─► [ 拦截 (BLOCK) ]
|
||||
│ (否,且未配置白名单)
|
||||
▼
|
||||
3. 匹配 IP 黑名单 / 黑名单 IP 组? ──────(是)─────► [ 拦截 (BLOCK) ] ──► 返回自定义状态码与HTML拦截页
|
||||
│ (否)
|
||||
@@ -152,7 +153,8 @@ IP 组是进行大批量 IP 过滤的基石。OpenFlare 提供了极富弹性的
|
||||
|
||||
## 最佳实践与调优建议
|
||||
|
||||
* **白名单前置与保护**:在部署高强度黑名单或地域屏蔽前,建议首先创建一个「受信任 IP 组」,放入你团队的办公室出口 IP、本地开发 IP 以及可能访问你的第三方回调源站 IP(如微信、支付宝支付回调地址),并在规则组的**白名单**中优先引入。这可以有效防止误杀。
|
||||
* **白名单准入语义**:一旦某个生效规则组配置了 IP 白名单、白名单 IP 组或地域白名单,请求必须命中其中至少一条白名单规则才会继续放行;未命中白名单的请求会被拦截。白名单命中后仍会优先绕过后续黑名单与 PoW 检查。
|
||||
* **白名单前置与保护**:在部署高强度黑名单或地域屏蔽前,建议首先创建一个「受信任 IP 组」,放入你团队的办公室出口 IP、本地开发 IP 以及可能访问你的第三方回调源站 IP(如微信、支付宝支付回调地址),并在规则组的**白名单**中优先引入。这可以有效防止误杀,但也会让未在白名单内的来源无法访问。
|
||||
* **合理微调 PoW 难度**:人机 CC 挑战的哈希碰撞计算(`challenge_difficulty`)是一把双刃剑。
|
||||
* 难度值 `3`:几乎瞬间完成计算,防 CC 强度低。
|
||||
* 难度值 `4`:普通手机/低端浏览器在 100~300ms 内完成计算,防护性能良好。
|
||||
|
||||
@@ -699,6 +699,18 @@ func TestManagedPowLuaFilesUseInternalChallengeFlow(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagedWAFLuaTreatsWhitelistAsAllowlist(t *testing.T) {
|
||||
if !strings.Contains(openRestyWAFRuntimeLua, "local function first_allowlist_group(groups)") {
|
||||
t.Fatal("expected waf runtime to detect allowlist rule groups")
|
||||
}
|
||||
if !strings.Contains(openRestyWAFRuntimeLua, "local allowlist_group = first_allowlist_group(groups)") {
|
||||
t.Fatal("expected waf runtime to enter allowlist mode when whitelist rules exist")
|
||||
}
|
||||
if !strings.Contains(openRestyWAFRuntimeLua, "return exit_with_group(allowlist_group)") {
|
||||
t.Fatal("expected waf runtime to block requests that miss configured whitelists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerRollbackRestoresCertFiles(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
routePath := filepath.Join(tempDir, "routes.conf")
|
||||
|
||||
@@ -91,6 +91,10 @@ local function list_contains(items, value)
|
||||
return false
|
||||
end
|
||||
|
||||
local function table_has_items(items)
|
||||
return type(items) == "table" and #items > 0
|
||||
end
|
||||
|
||||
local function parse_ipv4(value)
|
||||
local a, b, c, d = string.match(value or "", "^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
|
||||
if not a then
|
||||
@@ -215,6 +219,17 @@ local function exit_with_group(group)
|
||||
return ngx.exit(ngx.status)
|
||||
end
|
||||
|
||||
local function first_allowlist_group(groups)
|
||||
for _, group in ipairs(groups) do
|
||||
if table_has_items(group.ip_whitelist)
|
||||
or table_has_items(group.ip_whitelist_group_ids)
|
||||
or table_has_items(group.country_whitelist) then
|
||||
return group
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local config = load_config()
|
||||
if not config then
|
||||
if config_dict:add("_missing_config_logged", true, 60) then
|
||||
@@ -249,6 +264,11 @@ for _, group in ipairs(groups) do
|
||||
end
|
||||
end
|
||||
|
||||
local allowlist_group = first_allowlist_group(groups)
|
||||
if allowlist_group then
|
||||
return exit_with_group(allowlist_group)
|
||||
end
|
||||
|
||||
for _, group in ipairs(groups) do
|
||||
if ip_matches(group.ip_blacklist, ip) or ip_matches_group_ids(group.ip_blacklist_group_ids, ip, ip_groups_config) then
|
||||
return exit_with_group(group)
|
||||
|
||||
Reference in New Issue
Block a user