Suricata 规则编写实战:从零构建入侵检测规则

适用场景

企业内网或云上服务器需要在不改造业务的前提下,实时监测网络流量中的入侵行为:扫描探测、SQL 注入、暴力破解、恶意软件通信等。Suricata 作为开源 IDS/IPS 引擎,支持多线程、协议解析和自定义规则,适合部署在网关旁路(镜像口)或服务器入口,输出告警供安全团队响应。本文从零讲解 Suricata 规则语法与编写实战,帮助你独立编写可用的入侵检测规则。

前置条件

  • Linux 服务器(Ubuntu/Debian 或 CentOS),2 核 4G 内存以上
  • 已安装 Suricata(本文以 6.x/7.x 为例):sudo apt install suricata(Ubuntu 22.04+)
  • 能访问互联网(规则更新需要),了解 TCP/IP 与 HTTP 基础

原理说明

Suricata 规则(Signature)描述「匹配什么流量、触发什么动作」。一条规则由三部分组成:动作 + 头部 + 选项,例如:

alert tcp $HOME_NET any -> $EXTERNAL_NET any \
    (msg:"示例规则"; content:"test"; sid:1000001; rev:1;)

其中 alert 是动作(告警/丢弃/拒绝),tcp $HOME_NET any -> $EXTERNAL_NET any 是头部(协议、源、目的),括号内是选项:content 做内容匹配、msg 是告警消息、sid 是唯一规则编号(自定义规则建议从 1000000 起,避免与官方规则冲突)、rev 是版本号。Suricata 还内置了针对 HTTP、DNS、TLS 等协议的应用层关键字(如 http.uridns.query),比单纯抓包字节匹配更精准。

操作步骤

1. 确认安装与版本

suricata --build-info | head -n 5
sudo suricata --list-keywords > /dev/null 2>&1; echo "keyword list ok"

2. 创建本地规则文件

sudo mkdir -p /etc/suricata/rules
sudo nano /etc/suricata/rules/local.rules

3. 编写三类典型规则

规则一:检测 HTTP 请求中的 SQL 注入特征(union select / 常见注入函数):

alert http $EXTERNAL_NET any -> $HOME_NET any \
    (msg:"ET LOCAL SQLi - union select in URI"; \
     flow:established,to_server; \
     http.uri; content:"union"; nocase; \
     http.uri; content:"select"; nocase; distance:1; \
     classtype:attempted-recon; sid:1000001; rev:1;)

规则要求 URI 中同时出现 unionselect(忽略大小写、间距在 1 字节内),有效降低误报。

规则二:检测 SSH 暴力破解特征(短时间内多次认证失败的典型流量难以单包判定,这里检测畸形 SSH 版本串):

alert tcp $EXTERNAL_NET any -> $HOME_NET 22 \
    (msg:"ET LOCAL SSH - abnormal version string"; \
     flow:established,to_server; \
     content:"SSH-"; offset:0; \
     content:"!"; \
     classtype:attempted-admin; sid:1000002; rev:1;)

规则三:检测常见后门/恶意软件通信域名(DNS 查询命中黑名单域名):

alert dns $HOME_NET any -> any any \
    (msg:"ET LOCAL Malware - known C2 domain query"; \
     dns.query; content:"malware-c2.example.com"; nocase; \
     classtype:trojan-activity; sid:1000003; rev:1;)

更实用的做法是把 C2 域名列表放在 suricata-update 管理的规则集中,或使用 dns.query; content:!; 配合文件规则(--rule 之外可配置 datasets),本文保持单规则写法便于理解。

4. 在配置中启用本地规则

sudo nano /etc/suricata/suricata.yaml

rules-files: 一节追加:

rules-files:
  - suricata.rules
  - local.rules

5. 校验规则语法并启动

# 语法与配置校验(-T 测试模式)
sudo suricata -T -c /etc/suricata/suricata.yaml \
    -S /etc/suricata/rules/local.rules

# 正式启动(systemd)
sudo systemctl enable --now suricata

# 查看运行状态
sudo journalctl -u suricata -n 20 --no-pager

注意:监听流量需抓包权限,默认使用 af-packetpcap 模式,按 /etc/suricata/suricata.yaml 中的网卡配置(af-packet.interface)修改为实际网卡名。

6. 用回放流量触发规则验证

# 手动触发一次 SQL 注入特征请求
curl -s "http://example.com/index.php?id=1%20union%20select%201" > /dev/null

# 查看告警(JSON 日志)
sudo tail -n 5 /var/log/suricata/eve.json | jq -r \
    'select(.alert) | .timestamp, .alert.signature, .alert.signature_id'

预期输出包含 ET LOCAL SQLi - union select in URI 与 sid 1000001

配置验证

# 规则集统计(确认 local.rules 已加载)
sudo suricata -c /etc/suricata/suricata.yaml -T 2>&1 | grep -i "local"
# 应出现 local.rules 相关行,无语法错误

# 查看已加载规则数
sudo suricata --dump-rule-counts -c /etc/suricata/suricata.yaml 2>/dev/null | head

常见问题

FAQ 1:sid 冲突或重复怎么办?

规则编号全局唯一,冲突会导致规则加载失败。官方规则库使用 1-1000000 区间,自定义规则请使用 1000000 以上的编号;多条规则可在 suricata-update list-sources / 规则文件头部用 # sid 说明 注释管理。若启动日志提示 duplicate sid,定位到对应文件删除重复项后重新加载。

FAQ 2:告警太多(误报高)怎么收敛?

按以下顺序收敛:① 收紧匹配条件——组合多个 content 并配合 nocasedistancewithin 限定位置;② 限定协议上下文(如只在 http.uri 而非全文匹配);③ 使用 threshold 选项做时间窗口计数(如 60 秒内同 sid 超过 5 次才告警);④ 在 eve.json 接入 SIEM 后按源 IP 聚合分析,将高置信告警升级,其余降级为观察。

总结

本文从 Suricata 规则的三段式结构出发,编写了 SQL 注入、SSH 异常与恶意域名三类可落地的检测规则,并完成语法校验、加载与回放验证。核心要点:自定义 sid 从 1000000 起、用应用层关键字(http.uri/dns.query)提升精度、先 suricata -T 校验再重启服务、告警接入 eve.json 便于后续分析。建议下一步将 Suricata 告警接入 ELK 或现有日志平台,实现攻击事件的可视化与告警联动。