- 网络安全
- 网络
- IDS
【免费下载链接】zeek
Zeek is a powerful network analysis framework that is much different from the typical IDS you may know.
社区 ID(Community ID)是一种标准化的连接标识符生成规范,它把一条连接的五元组信息(源/目的 IP、源/目的端口、传输层协议)通过 SHA-1 哈希与固定格式编码,生成一个跨工具、跨网络位置都可复用的稳定字符串。当 Zeek 需要与 Suricata、Wireshark、Elastic Stack 等外部安全工具进行事件关联、数据去重或横向取证时,社区 ID 是天然的“连接主键”。本文以 Zeek 仓库中的 doc/scripts/policy/protocols/conn/community-id-logging.zeek.rst 文档为核心,结合脚本源码与 btest 测试用例,讲解如何在conn.log中启用并定制community_id字段,读完后你将掌握该脚本的加载方式、两个运行时选项的作用、底层哈希实现原理,以及如何把它延伸到 Notice 日志与自定义脚本中。
一、脚本定位:为 conn.log 增加社区 ID 字段
policy/protocols/conn/community-id-logging.zeek是 Zeek 官方 policy 脚本库中的一个“增强型”脚本,其唯一职责是:在每个连接建立时计算社区 ID,并将其写入conn.log的community_id字段。脚本的源码头注释与文档一致地描述了这一点:
##! Adds community hash IDs to conn.log.该脚本不属于base(基础脚本),需要显式加载:
@load protocols/conn/community-id-logging加载后,脚本通过redef向连接日志记录类型Conn::Info追加一个可选的、带&log属性的字符串字段:
redef record Conn::Info += { community_id: string &optional &log; };&optional表示该字段允许缺失(例如在计算失败、传输层未知等场景下,字段不会出现在日志中或为空);&log表示该字段会进入conn.log的日志输出列。
从 Zeek 类型系统看,Conn::Info是base/protocols/conn中定义的连接记录类型,脚本依赖@load base/protocols/conn才能访问它,这正是文档 “Imports: base/protocols/conn” 一节的含义。
二、运行时选项:seed 与 do_base64
文档的 “Runtime Options” 一节给出了脚本暴露的全部两个可调参数,二者均为&redef选项,可以在zeek命令行(-e或-O)或自定义脚本中覆写:
| 选项 | 类型 | 默认值 | 含义 |
|---|---|---|---|
CommunityID::seed | count(无符号 16 位整数) | 0 | 用于哈希计算的种子值,用于打散不同传感器上的 ID 空间 |
CommunityID::do_base64 | bool | T(开启) | 是否对 SHA-1 摘要做 Base64 编码以缩短输出 |
源码定义位于 scripts/policy/protocols/conn/community-id-logging.zeek:
module CommunityID; export { # An unsigned 16-bit number to seed our hashing option seed: count = 0; # Whether to add a base64 pass over the hash digest. # Enabled by default, since it shortens the output. option do_base64: bool = T; }2.1 seed:种子值的用途与限制
seed是一个无符号 16 位整数(count类型,实际取值被限制在 0–65535,超出部分在 C++ 实现中通过uint16_t hash_seed = htons(seed)截断并转为网络字节序)。默认0即社区 ID 规范中的标准种子,此时生成的 ID 与 Suricata、Wireshark 等工具默认输出的社区 ID完全一致,可直接跨工具比对。
如果需要让某个特定传感器或抓包点生成“私有化”的 ID 空间(例如多探针部署中希望本机 ID 不与外部工具默认值混淆),可覆写该值:
zeek -e 'redef CommunityID::seed = 42;' -r traffic.pcap protocols/conn/community-id-logging注意:修改种子后,同一连接生成的 ID 将不再与标准社区 ID 兼容,跨工具关联时需要同步修改所有参与方的种子配置。
2.2 do_base64:输出格式的取舍
社区 ID 规范规定输出格式为版本号:哈希值。当do_base64 = T(默认)时,哈希部分输出为 Base64 编码,长度更短;当设为F时,输出 SHA-1 的十六进制(ASCII)摘要,可读性更强但更长:
zeek -e 'redef CommunityID::do_base64 = F;' -r traffic.pcap protocols/conn/community-id-logging从 src/communityid.bif 的实现可以看到两种分支:Base64 模式走zeek::detail::Base64Converter编码器,非 Base64 模式走zeek::detail::sha1_digest_print打印摘要。二者输出的字符串都统一以1:版本前缀开头(目前实现中还没有真正的多版本/哈希配置逻辑,固定前缀1:)。
三、底层实现:community_id_v1 的哈希原理
社区 ID 字段的计算并不在 Zeek 脚本层完成,而是由内置函数(BiF)community_id_v1实现,其 BiF 声明在 src/communityid.bif:
function community_id_v1%(cid: conn_id, seed: count &default=0, do_base64: bool &default=T%): string也就是说,它接受一个conn_id记录(含orig_h、orig_p、resp_h、resp_p、proto等字段)以及上述两个选项,返回社区 ID 字符串。该函数被标记为ATTR_FOLDABLE(见 src/script_opt/FuncInfo.cc),在 Zeek 的 ZAM/CPP 优化编译器中可被折叠求值。
3.1 哈希输入构造
C++ 实现(src/communityid.bif)把conn_id记录拆解为哈希输入,顺序为:
- 2 字节种子:
htons(seed); - 源地址:IPv4 取 4 字节,IPv6 取 16 字节;
- 目的地址:同样按地址族取 4/16 字节;
- 1 字节协议号:TCP →
IPPROTO_TCP,UDP →IPPROTO_UDP,ICMP 按 IPv4/IPv6 分别映射为IPPROTO_ICMP/IPPROTO_ICMPV6; - 1 字节填充字节(
hash_padbyte = 0); - 若连接带端口:源端口与目的端口各 2 字节(网络字节序)。
对于没有端口概念的协议(如未知传输层协议),端口字段不参与哈希。遇到TRANSPORT_UNKNOWN且协议号为UNKNOWN_IP_PROTO时,函数会发出CommunityID: unknown transport layer错误并返回空字符串(该空字符串不会写入日志或写入为空值)。
3.2 端点规范化(canonicalization)
社区 ID 规范要求“对称性”:无论从哪一端观察连接,计算出的 ID 都必须相同。实现通过zeek::addr_port_canon_lt对(地址,端口)二元组做规范排序,必要时交换源/目的:
if (is_one_way || zeek::addr_port_canon_lt(orig_addr, hash_src_port, resp_addr, hash_dst_port)) { // All good, no need to flip } else { // Need to flip endpoints for hashing. std::swap(hash_src_addr, hash_dst_addr); std::swap(hash_src_port, hash_dst_port); }ICMP 流量的特殊情况由zeek::packet_analysis::ICMP::ICMP4_counterpart/ICMP6_counterpart处理:单向 ICMP 消息(例如仅有 Echo Request)会通过 ICMP 类型/代码的“对偶关系”(如 Echo Request ↔ Echo Reply)映射出对端端口,从而保证 ID 对称。btest 中的 community-id-logging-flipped.zeek 测试正是用handshake-reorder.pcap(TCP 握手乱序、两端视角互换)验证“即使连接被翻转,community_id依然正确记录”。
3.3 摘要与输出
规范排序后的输入交给zeek::detail::hash_init(Hash_SHA1)计算 SHA-1 摘要,最后拼接版本前缀:
std::string ver("1:");最终输出形如1:wCb3OG7yAFWelaUydu0D+125CLM=。在 testing/btest/bifs/community_id/v1.zeek 中固化了多组“输入 → 期望输出”的黄金用例,例如:
test_it([$orig_h=1.2.3.4, $orig_p=1122/tcp, $resp_h=5.6.7.8, $resp_p=3344/tcp, $proto=6, $ctx=[]], 0, "1:wCb3OG7yAFWelaUydu0D+125CLM="); test_it([$orig_h=1.2.3.4, $orig_p=1122/udp, $resp_h=5.6.7.8, $resp_p=3344/udp, $proto=17, $ctx=[]], 0, "1:0Mu9InQx6z4ZiCZM/7HXi2WMhOg="); test_it([$orig_h=1.2.3.4, $orig_p=8/icmp, $resp_h=5.6.7.8, $resp_p=0/icmp, $proto=1, $ctx=[]], 0, "1:crodRHL2FEsHjbv3UkRrfbs4bZ0=");同一组输入在seed=1时得到完全不同的输出(如1:HhA1B+6CoLbiKPEs5nhNYN4XWfk=),直观验证了种子对 ID 空间的分隔作用。另有 run-pcaps.zeek 使用 testing/btest/Traces/communityid 目录下的一组真实 pcap 逐条验证输入输出。
四、启用与验证:实战操作步骤
4.1 在 conn.log 中启用 community_id
最直接的启用方式是命令行加载脚本:
zeek -r traffic.pcap protocols/conn/community-id-logging或在站点脚本(如scripts/site/local.zeek)中统一加载。之后查看conn.log,可以看到新增的列:
#separator \x09 #path conn ... community_id 1:wCb3OG7yAFWelaUydu0D+125CLM=对应 btest 测试 community-id-logging.zeek 的做法:
# @TEST-EXEC: zeek -b -r $TRACES/http/get.pcap %INPUT # @TEST-EXEC: zeek-cut id.orig_h id.orig_p id.resp_h id.resp_p proto service community_id < conn.log > conn.log.cut即用zeek-cut工具抽取community_id列并与期望输出比对。
4.2 工作原理:new_connection 事件挂钩
字段的计算发生在脚本底部的new_connection事件处理器中,优先级为 5(高于默认优先级 0,保证在大多数其他处理器之前执行):
event new_connection(c: connection) &priority=5 { c$conn$community_id = community_id_v1(c$id, CommunityID::seed, CommunityID::do_base64); }连接建立瞬间,该事件即被触发,community_id_v1读取连接 IDc$id并写入记录字段。由于字段带&log属性,连接关闭写日志时自动输出。这也是为什么 community-id-logging-flipped.zeek 中可以在new_connection里直接print c$conn$community_id取到值。
4.3 自定义脚本中复用社区 ID
由于community_id_v1是全局内置函数,无需加载 policy 脚本即可在任何 Zeek 脚本中调用,例如为自定义日志或告警逻辑生成 ID:
event new_connection(c: connection) { local cid = community_id_v1(c$id); print fmt("connection %s -> community ID %s", c$uid, cid); }run-pcaps.zeek也展示了在connection_state_remove事件中打印c$id与 ID 的用法。
五、扩展:把社区 ID 带入 Notice 日志
文档虽然只覆盖conn.log,但仓库中配套的 scripts/policy/frameworks/notice/community-id.zeek 演示了同族扩展:为notice.log增加community_id字段,实现连接日志与告警日志的 ID 关联。该脚本自动加载community-id-logging:
@load base/protocols/conn @load base/frameworks/notice @load policy/protocols/conn/community-id-logging module CommunityID::Notice; export { # Turn notice support on/off at runtime. When disabled, # this still leaves the `community_id` string in the notice # log, just unset. option enabled: bool = T; redef record Notice::Info += { community_id: string &optional &log; }; } hook Notice::notice(n: Notice::Info) { if ( CommunityID::Notice::enabled && n?$conn && n$conn?$conn ) { local info = n$conn$conn; if ( info?$community_id ) n$community_id = info$community_id; } }要点:
- 额外提供
CommunityID::Notice::enabled选项(默认T),可在运行时关闭 Notice 侧的 ID 输出; - 通过
Notice::notice钩子从连接记录中拷贝已计算好的community_id,因此必须先加载community-id-logging才会产生值; - 加载方式:
@load policy/frameworks/notice/community-id。对应测试见 testing/btest/scripts/policy/frameworks/notice/community-id.zeek。
六、常见问题与注意事项
- ID 为空/缺失:当连接传输层协议为
TRANSPORT_UNKNOWN且协议号为未知值时,community_id_v1返回空字符串,字段因&optional而缺失或为空。这不是 bug,而是无法构造合法哈希输入时的安全回退。 - ICMP 双向一致性:社区 ID 对 ICMP 做了类型/代码对偶映射,Echo Request 与 Echo Reply 会得到同一个 ID;但纯单向、无对偶消息的 ICMP 连接,其端点规范化逻辑取决于 ICMP 对偶映射的判定结果,跨工具比对时以双方实现的规范为准。
- 种子影响互操作性:默认
seed=0才能与外部工具默认值互通;自定义种子后需同步配置所有比对方。 - 版本前缀固定为
1::当前实现没有版本选择逻辑,输出恒为 v1 格式;未来规范若推出 v2,字段格式会随内核更新,脚本侧无需改动。 - 不要在 base 中重复加载:该脚本属于 policy 层,显式加载一次即可;重复加载同一脚本会被 Zeek 的加载器去重。
七、小结
community-id-logging是 Zeek 生态中“低成本、高价值”的互操作性脚本:一条@load指令即可让conn.log携带符合社区 ID 规范的稳定标识,配合CommunityID::seed与CommunityID::do_base64两个&redef选项,可精确控制 ID 空间与输出格式。其底层由 src/communityid.bif 中的community_id_v1完成端点规范化、SHA-1 摘要与 Base64 编码,并有 testing/btest/bifs/community_id 与 testing/btest/scripts/policy/protocols/conn 两套测试固化了跨版本的正确性。配合 scripts/policy/frameworks/notice/community-id.zeek,可在连接日志与告警日志之间建立统一的关联键,为多传感器部署与跨工具取证提供坚实基础。
- 网络安全
- 网络
- IDS
【免费下载链接】zeek
Zeek is a powerful network analysis framework that is much different from the typical IDS you may know.
相关推荐
Zeek 的 community_id_v1() 完全指南:Community ID v1 哈希的计算原理与实战应用
Zeek 的 community_id_v1 完全指南:Community ID v1 哈希的计算原理与实战应用 Community ID 是一套为网络流/连接
网络安全网络IDSOpCore-Simplify 一键生成 OpenCore EFI:黑苹果从零配置到避坑完整指南
OpCore Simplify 一键生成 OpenCore EFI:黑苹果从零配置到避坑完整指南 手搓黑苹果的 EFI 是整条链路里最容易翻车的一环:confi
网络安全网络IDSZeek 协议违规包段日志(packet-segment-logging)原理与实战指南
Zeek 协议违规包段日志(packet segment logging)原理与实战指南 导读 本文围绕 Zeek 策略脚本 scripts/policy/fr
网络安全网络IDS
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考