← 返回目录

第 3 卷

第 3 / 4 卷日期:

图 7-1
图 7-1

环的前半段(长出候选 → 评测、确认 → 上线)前面已经讲过,本节从“漏拦”接着走:漏掉的请求怎么被记下来、怎么喂回证据库、下一轮提案怎么长出新候选;环之外,再说状态怎么

落盘、重启后不失效。

  • 漏掉的请求怎么被记下来。先看漏拦到底漏了什么。为了演示,这里我们故意造一条比评

测那节更窄的候选 block admin only,它用 eq /admin(精确相等)而不是评测节的 starts_with /admin,于是只命中 /admin 这个路径。

拿它回放:9 条异常里只拦到 2 条 /admin,剩下 7 条(/wpadmin、/etc/passwd、/phpmyadmin 这些)一条没拦住,这 7 条就是漏拦。

漏拦不是只记个数就完了。observe 在“放过”时,把漏掉的原始日志行本身留存下来,而不只是计数。为什么非留住原始行不可?计数只够回答“漏了几次”,要让下一轮知道“漏的是什

么”,才能长出补它的规则,必须留住请求本身。

  • 漏掉的请求怎么喂回证据库。① 记下来的那些原始行不能白记:把它们脱敏后喂回第 20

讲装证据的那个池子,下一轮提案生成时才能用到。这一步由 feedMissedToEvidence 完

有两处要留意一下:

只喂漏拦、不喂误伤。误伤(拦了正常请求)已由自动回退当场止损;若把误拦的正常请求当异常证据塞回去,模型反而会照着它长出朝正常请求开火的规则。

喂回是 drain(取出并清空)式的。observe 漏拦时收下原始行,drainMissed() 取出后立刻清空,防的是同一批漏拦每轮都被重复喂回,证据库里这一条越积越多,把别的信号

全挤掉,那正是第 20 讲抗挤占采样要防的事。

整件事的实现很短:

复制代码

1// feedback.ts(注释为讲解所加):漏掉的异常请求脱敏后喂回证据库,返回喂回的证据本身*
2*exportfunctionfeedMissedToEvidence(sink: EvidenceSink, lc: RuleLifecycle):San
3* constlines:NginxLine[] = lc.drainMissed() // drain:取出并清空*
4* constfed:SanitizedEvidence[] = []*
5* for(constlineoflines) {*
6* constkind =detect(line) //重新detect,带上类别*
7* if(!kind)continue //判不出类别:宁可少一条,也不要k
8* constev =sanitize({ kind, line }) //脱敏复用ch20的sanitize,口径
9* sink.injectEvidence(ev)*
10* fed.push(ev) //返回喂回的证据本身,供下一轮合并
11* }*
12* returnfed*
13*}
  • 下一轮提案怎么长出新候选。② 喂回的证据不是终点,下一轮把它和原有证据合并,再跑

一次 proposeFromEvidence。模型这次看见了上一轮漏掉的 /wpadmin、/etc/passwd,下一轮就长出覆盖它们的候选(输出里的 cand-8 ~ cand-

e)。这些新候选再过一遍评测、候选确认、生命周期,上一轮漏掉的就被补上了,接着它们上线,又出现新的漏拦,于是再喂回、再提案,环就这样一圈圈转起来。

心跳把误伤率冲淡,noteIdle 不碰误伤率分⺟),都不需要模型判断,也不需要人审批,这是和第 19、20 讲“人审批”最大的不同。

正因为它全自动、没人盯着,状态不能只活在内存:进入终态(回退 / 老化)的规则,会写成 lifecycle.rollback / lifecycle.aged_out 事实并落盘,复用第 19 讲账本那套机制

(同一套 user/message 载体 + JSONL 持久化)。

重启后 replayRetired 把“已判死”的规则重放出来,同一条规则想再走一遍灰度会被直接拒绝;否则,一旦“已回退的规则”被忘掉,自动回退这张安全网就会在重启后失效。示例

demo 直接调 observe / noteIdle 两个入口演示、不挂 loop-task 也不落盘;真实运行才由 loop-task.ts 定时驱动、loop-log.ts 落盘。

以下是 npm run start:ch21 的真实运行输出。其中每次一致的部分:历史流量样本固定,漏拦喂回演示用的窄候选只拦 /admin,也写死在示例里。每次不同的部分:评测、候选

确认、生命周期本身是确定性代码,不受运行时机影响;但输入是模型生成的候选,候选一变,相关数字就变;证据里的 ipHash 用进程内随机盐,每次不同;候选由 ch20 真实模型

生成,名称、条件、条数每次都可能不同。

以下为一次代表性运行:

复制代码 1=== 历史流量(回放样本)===

2请求数=19该拦(bad)=9正常(good)=10

3[firewall-observe]未设置FIREWALL_IP_HASH_SECRET:改用进程内随机密钥。假名化仍不可反推,
4(ch20真实模型提案回复节选: [
5{"name":"block admin probe","when": {"field":"path","op":"starts_with",
6{"name":"b…;token in=453 out=265)

7

8===评测三件事(覆盖率/精确率/误伤率)+事实校验===
9cand-1 [block admin probe]覆盖率=22.2%精确率=100.0%误伤率=0.0%逆推=[path-probe]
10...(cand-2 ~ cand-5是/wp-login、/wp-admin、/phpmyadmin、/etc/passwd四条路径候选
11cand-6 [block sqli]覆盖率=0.0%精确率=0.0%误伤率=0.0%逆推=[sqli]事实校验=ok ⚠未
12cand-7 [block ua scanner]覆盖率=0.0%精确率=0.0%误伤率=0.0%逆推=[bad-ua]事实校验
全站分卷索引 · 4 卷
  1. 1第 1 卷
  2. 2第 2 卷
  3. 3第 3 卷
  4. 4第 4 卷