☰
Zeek 社区 ID(Community ID)连接日志字段:community-id-logging 脚本原理与实战指南
2026/10/10 1:29:10 网站建设 项目流程
  • 网络安全
  • 网络
  • IDS

【免费下载链接】zeek

Zeek is a powerful network analysis framework that is much different from the typical IDS you may know.

项目地址:https://gitcode.com/gh_mirrors/ze/zeek
点击查看免费下载

社区 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::seedcount(无符号 16 位整数)0用于哈希计算的种子值,用于打散不同传感器上的 ID 空间
CommunityID::do_base64boolT(开启)是否对 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记录拆解为哈希输入,顺序为:

  1. 2 字节种子:htons(seed);
  2. 源地址:IPv4 取 4 字节,IPv6 取 16 字节;
  3. 目的地址:同样按地址族取 4/16 字节;
  4. 1 字节协议号:TCP →IPPROTO_TCP,UDP →IPPROTO_UDP,ICMP 按 IPv4/IPv6 分别映射为IPPROTO_ICMP/IPPROTO_ICMPV6;
  5. 1 字节填充字节(hash_padbyte = 0);
  6. 若连接带端口:源端口与目的端口各 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.

项目地址:https://gitcode.com/gh_mirrors/ze/zeek
点击查看免费下载
上一篇:Symfony Translation转储器系统:12种格式一键导出多语言资源
下一篇:革命性Linux面部认证:Howdy CLI工具完全指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询