能使用 threshold 关键字控制告警频次,并用 suppress 抑制指定条件下的噪声告警。
核心概念
threshold:按次数与时间窗口聚合告警
上一课结束时,你已经在 fast.log 里核对到了 SYN+FIN 告警,并用正常会话验证了不误报——规则本身是可靠的。但再往前想一步:如果攻击者发起扫描,几秒内就有上百个带异常标志组合的包经过,你的规则会为每一个都写一条告警,fast.log 瞬间被刷屏,真正有价值的信号反而被淹没。类比:想象办公室的烟雾报警器。火警持续时,它不会每秒响一次,而是响几下就停,等下一轮再评估。你在乎的是"是否着火",不是"第 137 次触发"。threshold 就是给告警装的节流阀。
定义:threshold 是写在规则括号内的选项,按"次数 + 时间窗口"决定何时真正输出告警。基本形式:
threshold: type <threshold|limit|both>, track <by_src|by_dst>, count <N>, seconds <S>;
type threshold:窗口 S 秒内匹配次数达到 N 才告警,滤掉零星触发。 type limit:窗口 S 秒内最多输出 N 次告警,其余静默到窗口结束。 type both:先要求达到 N 次,再限制重复输出,把风暴压成少量告警。 track by_src:每个源地址独立计数;track by_dst:按目的地址计数。
threshold 只控制告警输出,不改变规则匹配逻辑。
把 threshold 加在一条"重复探测"规则上。下面的规则匹配来自任意源地址的 TCP SYN(tcp.flags 已在第 4 章覆盖),同源在 60 秒内达到 3 次匹配后,窗口内只输出一次告警:
alert tcp any any -> any any (msg:"同源高频 SYN-阈值演示"; tcp.flags:S; threshold: type both, track by_src, count 3, seconds 60; sid:5000001; rev:1;)读法:count 3, seconds 60 是"60 秒内 3 次";type both 是"达到 3 次才告警,并限制窗口内重复输出";track by_src 让每个源地址各自计数。若改成 type limit, count 1, seconds 60,每个源地址每 60 秒最多输出 1 条告警。
threshold 适合单条规则短时间大量重复命中的场景,例如扫描工具对同一网段的连续探测。它不降低单次命中的误报;要降低单次误报,回到 content 与协议关键字收紧条件,这些你已覆盖。
核心概念
suppress:按 sid 与地址静音已知噪声
类比:病房呼叫器坏了,每隔几分钟自动呼叫一次。护士不会拆掉它——急救时还要用——而是把总机上这个呼叫器的提示音关掉。suppress 就是这样的静音开关:规则还在,只是不响。
定义:suppress 是写在规则括号内的选项,对指定规则的告警做定向静音。常见写法:
suppress: gen_id 1, sig_id <sid>; suppress: gen_id 1, sig_id <sid>, track by_src, ip <网段>;
第一行静音该规则全部告警;第二行加上 track 与 ip 后,只静音来自该网段的命中(track by_dst 则按目的地址)。gen_id 1 是默认分组号,sig_id 填这条规则的 sid。
关键点:suppress 不关闭检测。引擎仍执行匹配,只是不产生告警记录。需要按端口收窄时,在规则头部用端口条件限定范围,再配合 suppress 或 threshold。
两者分工不同。threshold 处理"短时间风暴":同类命中太多时聚合为更少、更有意义的告警。suppress 处理"确定是噪声":某 sid 或来源网段确认不必告警时直接静音。二者可写在同一条规则里:
alert tcp any any -> any any (msg:"端口探测-聚合与静音演示"; tcp.flags:S; threshold: type limit, track by_src, count 1, seconds 60; suppress: gen_id 1, sig_id 5000002, track by_src, ip 10.10.0.0/16; sid:5000002; rev:1;)告警疲劳的代价很实际:告警过多时分析师会麻木,真实威胁被淹没。threshold 与 suppress 是降噪工具,但它们不修复规则本身的误报根因。
suppress 与 threshold 只改变告警是否输出,不改变规则是否匹配。规则本身写错时(content 过宽、方向或端口没限),抑制只是把错误藏起来,检测仍在空转。count 调得过高、seconds 调得过长,真实攻击也会被聚合掉。每次调优后,用已有的 PCAP 回放回归验证(-r 与 fast.log 用法已在第 1 章覆盖),确认压噪没有压掉该看到的告警。
用 threshold 与 suppress 压噪并验证
- 1
在 /var/lib/suricata/rules 下新建 threshold.rules,写入带 threshold 的规则,用已覆盖的 tcp.flags 匹配 SYN。
suricataalert tcp any any -> any any (msg:"同源高频 SYN-阈值演示"; tcp.flags:S; threshold: type both, track by_src, count 3, seconds 60; sid:5000001; rev:1;) - 2
用测试模式校验语法:-T 已在第 1 章覆盖,-S 指定规则文件;配置文件沿用环境的 /etc/suricata/suricata.yaml。
bashsudo suricata -T -c /etc/suricata/suricata.yaml -S /var/lib/suricata/rules/threshold.rules - 3
离线回放授权实验流量:-r 用法见资料说明,PCAP 用第 4 章那份或任何含多次同源 SYN 的本地文件。
bashsudo suricata -r /path/to/lab.pcap -S /var/lib/suricata/rules/threshold.rules - 4
统计 fast.log 中该规则的告警条数(fast.log 查看方式已在第 1 章覆盖)。
bashsudo grep "5000001" /var/log/suricata/fast.log | wc -l - 5
删掉规则里的 threshold 行,或把 count 改成 100,重新回放并再次统计,比较两次条数。
你应该看到
两次回放都正常结束。带 threshold 的版本对同一 PCAP 输出的告警数明显少于去掉 threshold 的版本:同源 60 秒内的多次命中被聚合为一次。若 PCAP 中同源 SYN 不足 3 次,带 threshold 版本可能一条告警都没有,这同样验证了"窗口内达到 count 才告警"。两次 grep 统计的数字应可对比。
遇到问题:加上 threshold 后,fast.log 里一条该规则的告警都没有。
可能原因
- count 高于窗口内实际匹配次数,如 60 秒内只有 2 次命中却设了 count 3。
- 命中间隔大于 seconds,窗口不断重置,计数到不了 count。
- track 方向不符:噪声来自同一源,却用了 track by_dst。
- 规则没被加载:-T 未通过,或 -S 指向的文件名不对;fast.log 输出未启用(资料提醒过要在 suricata.yaml 里开启)。
排查步骤
- 去掉 threshold 回放一次,用 grep 统计条数,把 count 设成低于该数。
- 增大 seconds 或降低 count,重新回放对比。
- 把 track 从 by_dst 改为 by_src,与流量方向一致。
- 先跑 -T 确认规则加载正常,再确认 -r 指向的 PCAP 真实存在且含要匹配的 SYN 流量。
自检:一条规则能在 1 分钟内对同一来源产生上百条告警。你已确认该来源确实在探测,需要保留检测、只压噪声。写出:1) 让每个源地址每 60 秒最多输出 1 条告警的 threshold 写法;2) 若确认 10.10.0.0/16 全是已知扫描器,只对该网段静音、其他地址照常告警的 suppress 写法(该规则 sid 为 5000003)。
- threshold: type limit, track by_src, count 1, seconds 60; 每个源地址 60 秒窗口内最多输出 1 条告警,其余静默到窗口结束。也可用 type both 先聚合再限流。2) suppress: gen_id 1, sig_id 5000003, track by_src, ip 10.10.0.0/16; 只静音来自该网段的命中,其他来源照常告警。两者都写在规则括号内,不改变规则的匹配逻辑。