不从现有字段表开始归纳,而从「日志的本质是什么」出发做第一性原理推演。 分类的第一原则不是「业务上发生了什么」——那是开放清单,无限增长; 而是「这次记录的本质是什么」(有限闭集)——本体分类立在前者,分类学清单陷在后者;SDM 要立的是本体。
网络层 / 系统层 / 应用层。层次不只是标签——它约束主体、客体、载体能填哪些实体类型(见主体与客体区)。
出现 / 读取 / 变更 / 消失 / 流动——五类闭集。任何厂商动作都能归入其一,新动作不扩模型。
对象层的事实不真不假、只是发生了;观察层的判断可能错。这条轴直接推出 observation 的顶层地位。
| 层 | 例子 | 性质 |
|---|---|---|
| 对象层(事实) | 「A 访问 B(发送了信息)」 | 只是发生了,无所谓真假——它是研判的原料,永远成立、不可推翻 |
| 观察层(判断) | 检测器说「这次访问是恶意的」 | 是观察者的一次行为,天然可对可错——需证据支撑,也可被复核推翻 |
误报 = 观察者对事实做了一次错误的定性,不是事实错了。事实无所谓对错,可能错的永远是判断——所以判断必须与事实分层存放。
观察层的每一个判断都必须能回溯到对象层的哪几条事实(evidence_refs),否则判断是无根的观点——误报无法归因、无法治理。这条直接支撑 ADR-002:AI 只追加分析,verdict 由策略或人写。
| # | 问题 | 落位 | 省略后果 |
|---|---|---|---|
| 1 | 一条事件如何被唯一定位、回查和重放? | 元数据 = 身份 + 时间 + 数据来源 + 原始引用 + 映射契约 | 无法判断何时、何处、由哪条映射生成,也无法对账 |
| 2 | 这究竟是什么行为? | 先 event_kind,再 behavior = 层次 + 五类 + 动作 + 结果 + 事实描述 | 数据只能按厂商字段查询,无法跨来源比较 |
| 3 | 谁通过什么,对谁做了什么? | 主体 / 客体 / 载体 = 谁发起 + 作用于谁 + 通过什么(16 值实体类型 × 对应属性对象) | IP、主机、账号与进程被固化为攻击者 / 受害者字段,方向反转时语义失真 |
| 4 | 行为究竟怎么发生? | 行为维度 = 网络与流量 + Web 与解析 + 认证 + 进程 + …(15 候选域) | 领域细节被塞进顶层或厂商私有区,公共模型持续膨胀 |
| 5 | 哪些是观察到的事实,哪些是来源做出的判断? | 观察 = 观察者 + 观察动作 + 断言 + 证据引用 | 设备的「检测到攻击」会被写成已确认的客观事实,误报污染下游研判 |
| 6 | 标准尚未理解或不适合公共化的信息放哪? | 扩展 = 来源私有 + 画像 + 富化 | 只能丢数据,或为每个厂商不断扩张公共 Schema |
观察者不参与行为,它只是看、记、判——把「看的人」和「做的人」并列为事件参与者,等于让设备的猜测冒充现场。判断归观察层,事实归对象层,互不改写,真伪留给下游判定。
落点:observation 是与行为内容平齐的顶层结构;观察者判断入 observation.assertion,行为事实入 subject/object/carriers / facets。
主体 = 发起者、客体 = 承受者是稳定的 agency 语义;数据流向是行为的属性,由五类行为类型表达(读取:客体→主体;写入/流动:主体→客体),不靠交换主体客体来凑方向。
落点:subject / object 只表达观测到的行为主体与客体,不自动等于攻击者 / 受害者;攻防定性只在 assertion.attackers/victims。
五类行为 × 三个层次是本体,厂商动作清单是分类学——新动作归入闭集,不扩模型;同一事实在不同层次落成不同类型(系统层读取、网络层流动),由层次裁决。落位一旦重复,映射无从评审、查询无从预期。
落点:同一实体建一次、以 ref_id 引用;身份未采集时留空,不构造占位对象;装不下的进 extensions 受治理,不另立标准位。
一句话概括:某主体,经载体,对客体做了一次行为,发生在某切面里,被某观察者记录。
顶层五个节点:meta、event_kind、内容(当前仅实现行为,其下是行为五件)、observation、extensions——主体/客体/载体不是与观察并列的独立层,而是内容区的组成部分。
如何存储落表属于物理绑定,见手册 · 物理投影。
先判根字段 event_kind(当前唯一值 behavior;观察依附于行为,不独立成事件;state 暂缓),再由 behavior 五个字段说清「这是什么行为」:发生在哪一层(layer)、什么性质的行为(type)、具体做了什么动作(operation)、结果如何(outcome)、事实是什么(message)。
三条轴各有着落:层次与行为类型落字段,认识地位落结构(事实在内容区、判断在 observation);「许可/拒绝」的处置决策是所有行为共有的维度,由 outcome 的三段语义承载。
| 字段 | 取值 | 判别 |
|---|---|---|
层次behavior.layer | network / system / application | 三轴之一:发生在哪一层;按实际观测层次判别,不按厂商产品名称推断 |
行为类型behavior.type | appear / read / change / disappear / flow | 三轴之一:发生了什么性质的行为——五类闭集,判定依据见下方「行为五类与结果」 |
动作behavior.operation | 开放动作名 | 类型的具体化:login、query、write、connect 等具体动作,必须归属五类之一(login→read、spawn→appear);新动作不扩模型 |
结果behavior.outcome | 处置段:allowed / denied 执行段:success / failed 记录段:observed / unknown | 共有维度:每个行为都经过一次「放行还是阻断」的决策(处置段);无守门人时看行为自身成败(执行段);仅被记录或来源无结果(记录段)——三段不得混用 |
事实描述behavior.message | 文本 | 描述行为事实本身;状态描述留待 state 模型 |
行为分五类闭集——出现 / 读取 / 变更 / 消失 / 流动,每类有明确判定依据;结果不是第六类,每个行为都经过一次「放行还是阻断」的决策,「许可 / 拒绝」是五类共有的维度:
| 行为类型 | 判定依据 | 例子 | 许可allowed | 拒绝denied |
|---|---|---|---|---|
出现appear | 新实体开始存在:从无到有 | 进程启动、文件创建、连接建立 | 进程启动成功 | 启动被 EDR 阻断 |
读取read | 取——数据原地被访问,不换位置、不跨边界 | 读文件、查库、认证 | 读成功 | 认证失败、读被拒 |
变更change | 已有实体的内容或状态被修改 | 写文件、改配置、提权 | 修改生效 | 写入被拒 |
消失disappear | 实体终止存在:从有到无 | 进程退出、文件删除、连接断开 | 正常终止 | 会话被强制切断 |
流动flow | 移——数据跨边界、改变位置 | 网络传输、下载、外传 | 数据放行 | 防火墙丢包 |
其中读取与流动最易混淆,判据是数据是否跨边界、改变位置(取与移)——安全语义不同必须分开:读取指向越权访问(读敏感数据、查库),流动指向数据外传、C2 通信、横向移动。边界模糊处(如「下载文件」)由层次裁决:系统层看到的是读取,网络层看到的是流动——同一事实,层次决定落成哪个类型。
认证是控制点判定(处置结果);端口连不通无守门人,是行为本身 failed(执行结果);流量镜像只是观察到,记 observed。
原始日志等级写 meta.source_record.log_level;来源检测判断的严重度写 observation.assertion.severity;行为层不再有 severity 字段。
behavior.outcome 不保存阻断动作字面量:来源写 blocked 一律按语义归一为 denied,词表保持闭合。
行为句式 = 主体(subject)通过载体(carriers[])作用于客体(object)。 每个角色是 16 值 entity_type + 对应 typed object;身份未采集时留空,不构造占位对象。 谁对谁清楚了,下一节看「怎么发生的」。
| 角色 | 定位 | 结构 | 要点 |
|---|---|---|---|
主体subject | 行为发起者 · 施事 | ref_id 事件内引用 entity_type ← 16 值闭集 └─ typed object 与 entity_type 一致 | 单一实体;身份未采集时留空——没有身份 ≠ 没有主体;不自动等于攻击者 |
客体object | 直接作用客体 · 受事 | ref_id 事件内引用 entity_type ← 16 值闭集 └─ typed object 与 entity_type 一致 | 单一实体;多个同等客体优先拆事件;不自动等于受害者 |
载体carriers[] | 工具 · 载体 | ref_id 事件内引用 entity_type ← 9 种载体类型 carrier_role 载体关系 └─ typed object | 多值数组,每个成员一套左述结构;parent_process、script_engine 等载体关系;协议、方向、会话进 facets,不进载体 |
观察者不在此表——它不参与行为,只记录、检测、判断;结构在 observation.observer,见观察与断言一节。 | |||
每个角色对象由一对字段构成:entity_type 从 16 值闭集中选一,typed object 是与之同名的属性对象——类型决定哪个对象字段被填充,其余 15 个不出现:
entity_type = "endpoint"
从 16 值闭集中选定一个,声明这个角色「是什么」。点击下方类型牌查看对应属性对象。
subject.endpoint = { ip, port, mac, asset_id, asset_name, asset_type }
只有与 entity_type 同名的对象字段被填充;其余 15 个字段不出现。
属性与手册「对象类型」同一份登记表(object-fields.v1,M2 已登记);三条配对规则:只能选一——一个角色最多一个 entity_type 与一个 typed object;geo、hashes 只是属性——挂在 host、endpoint、file 等对象里,不是独立类型;未知不构造——身份未采集时整个角色留空。
日志里的 src/source 语义是动作发起者,不是「数据来源」。「进程读文件」时,主体仍是读取的进程(发起者),客体是被读的文件(承受者)——不因为数据从文件流向读者,就把文件当 src。数据流向由行为类型表达,不靠交换主体客体;主体/客体是稳定的 agency 语义(谁发起 / 谁承受),两者不混。
| 行为类型 | 数据流向 | 主体(发起者) |
|---|---|---|
读取read | 客体 → 主体 | 读的人(发起者) |
写入 / 流动change · flow | 主体 → 客体 | 写 / 发的人(发起者) |
层次不只是标签,它约束了主体 / 客体 / 载体能填哪些实体类型——这正好把实体清单按三个层次自然分区。层次与行为类型正交:同一个「读取」在网络 / 系统 / 应用三层各自成立。
| 层次 | 在哪观测到 | 主体/客体/载体的典型实体域 |
|---|---|---|
网络层network | 网络流量 | endpoint(IP、端口、协议、流)、domain |
系统层system | 操作系统内 | process、file、script、certificate、registry 上下文 |
应用层application | 人与应用 | user、account、service、application、url |
实体是 endpoint(IP / 端口),行为落在网络流量观测点。
实体是 process / file,观测点在操作系统内部。
实体是 user / service / resource,观测点在应用与数据库之间。
行为细节按维度拆放:契约登记 – 个候选 domain,典型路径已定义 – 个,页面按主题组展开; 全量字段见手册 · 字段目录。如果这条日志是设备检测出来的,判断放哪?下一节回答。
observation 回答「谁在什么位置、以什么方式观察,做出了什么判断」。 一条被判定为攻击的行为事件,事实层只有观测到的 subject 与 object——断言不改写事实。 最后一块:事件的身份证。
| 字段 | 内容 |
|---|---|
| observation_id / observer | 观察记录 ID 与观察者实体(typed object;地址统一用 ip) |
| action | record(记录)/ detect(检测)/ assess(研判) |
| observed_at | 观察时间 |
| assertion | 观察者判断——仅 detect/assess 时必须有,不反写事实层;内部字段见下方「断言」小节 |
| evidence_refs[] | 指向事实事件、原始日志或证据对象的引用 |
当前每条行为事件最多一个 observation;多观察者场景未来经 Schema 版本升级为 observations[],但不建立独立 observation 事件。
原始日志等级写 meta.source_record.log_level;检测判断严重度写 assertion.severity;行为层没有 severity 字段。
meta.data_source.instance_id 只表示采集器/接入节点;实际产生日志或作出判断的设备实例(如防火墙、NGSOC)进 observation.observer——不能仅按字段名判断。
旧 source_finding 的十一类字段一行不丢,全部收进 observation.assertion;断言不反写事实层——attackers[] / victims[] 是来源的主张,不是 subject / object 的第二份。
| 断言字段 已登记 · object-fields.v1 | 收什么 | 旧来源 |
|---|
断言只表达「来源认为谁攻击谁」;方向反转或误报时,事实层观测到的主体客体不被污染——这是误报可归因、可治理的前提。
07 Schema 只约束「detect/assess 必有、不反写事实层」;内部字段登记于 object-fields.v1(M2),扩展经模型评审。
断言投影为 SOURCE_ALERT 告警:标题成快照、severity 换算 INFO–CRITICAL、event_id 成 Evidence 指针;只消费 record_kind=finding 的检出类记录。详见告警标准。
原 source_finding 是来源设备这个第一个观察者的判断,不是事件的第五个分区;severity 同理,是设备对事件重要性的自我评估,归观察者层。整条观察链共享统一骨架:「观察者 + 动作(判断/处置)+ 被观察对象 + 结论」。
| 观察层对象 | 观察者是谁 | 判断什么 |
|---|---|---|
observation.assertion(原 source_finding) | 来源设备 | 某条事件 |
sdm_alert | 平台检测引擎 | 某条 / 某组事件 |
sdm_analysis | AI / 人 | 某个 alert / case |
sdm_workflow_action | 运营者 | 某个 alert / case |
meta 给事件发身份证——身份、时间、数据来源与原始引用,一条事件何时、何处、由哪条映射生成,全程可对账。模型到此定型,接下来看真实日志怎么进来。
event_id 是标准化事件唯一标识(平台分配,与 raw_log 1:1);log_id 是原始日志身份——来源有稳定 ID 填来源 ID,否则与 event_id 相同。
07 逻辑信封为 UTC date-time:occur_time 发生、ingest_time 接入、parse_time 解析。Kafka 物理编码才是 unix 毫秒,见物理投影。
每条事件携带映射契约编号与逻辑模型版本,管道全程不丢;mapping_id 不可变,语义变化必须创建新值;schema_version 为字符串 2.0。07 已删除 mapping_revision / projection_version / quality_status。
meta.data_source.instance_id 只收采集器 / 连接器 / 接入节点;实际产生日志或作出判断的设备 / 产品实例(如防火墙管理地址)归 observation.observer——不能只按字段名判断,见观察与断言。
log_id、record_kind(兼容路由值,不改变 event_kind)、log_type、log_level(原文等级 ≠ 断言严重度)、raw_ref 回查引用——原文留在 raw_log,事件只持引用。
auth / network / audit / system / alert / other,按日志内容性质判别(非设备类型):防火墙攻击告警 = alert,纯流量 = network;同一产品不同 log_type 可落不同值。物理列 data_source_category。
tenant_id 必填——多租户隔离与物理分区键之一;无可信租户上下文时用空串,不发明 tenant01。物理唯一键 tenant_id + occur_time + event_id 见物理投影。
信封字段是结构位;实体内部在「对象类型」,切面叶子在「行为维度」,断言与画像在「断言与扩展」。
物理列来自 031_sdm_event_behavior.sql:33 标量 + 6 VARIANT(共 39 列)。逻辑字段是否存在与是否物化为标量列是两个独立决策。
| # | 层 | 逻辑路径 | 类型 | 基数 | 必填 | 样例 | 含义 |
|---|
16 个 entity_type 各一卡,属性只写一次;角色标签标明能出现在 subject / object / carriers[] / observer。
字段已登记于 object-fields.v1(M2);07 Schema 仍只约束角色结构,字段扩展经模型评审。
15 个 facets.* 候选域,不是实体类型。07 只钉域名,内部开放;典型路径来自字段目录 2.8。禁止 facets.related。身份仍在 subject / object / carriers。
observation.assertion 来自 object-fields.v1(detect/assess 必填,不反写事实层)。
extensions 三容器来自字段目录 2.11;endpoint_asset 来自 Profile registry,不计入信封字段。
| # | 物理列 | Doris 类型 | 逻辑路径 | 样例 | 说明 |
|---|
路径含 [] 表示数组;<type> 是与 entity_type 同名的属性对象槽,16 选 1。点槽位或类型牌打开「对象类型」。必填依据当前 JSON Schema;对象内部字段以 object-fields.v1 登记为准。
extensions.profiles.endpoint_asset 由 Profile registry v2 单独治理(19 条路径),表达终端资产相对 host 的增量快照,不计入信封字段。
闭合枚举只能写入目录列出的值;开放字段推荐值经评审扩展。
现行以 07 为准:behavior.layer / type / outcome、observation.action、16 值 entity_type。
behavior.operation 为开放动作名,必须归属五类。
标准枚举统一小写;behavior.outcome 不保存阻断动作(来源 blocked 按语义映射为 denied);behavior.operation 必须能归入五类,未归属则不写;开放字段新增推荐值须更新契约,不允许单个接入规则自由造词。
三个 tab 对应三类典型日志:设备检出(behavior + detect 观察)、网络流量(behavior)、终端审计(behavior)。 每条按接入 8 步展开填了什么、为什么;JSON 四阶段收在表下,按需展开。
原始日志经 WPL 解析 → OML 富化组合 → Kafka 行为信封 → Routine Load 落入 sdm_event_behavior;
原文独立进 raw_log。映射身份由 mapping_id + schema_version 全程携带。
每条事件携带映射契约编号与逻辑模型版本(2.0)。07 已删除 mapping_revision / projection_version / quality_status,不得再写入。
IP 地理、资产、系统、组织和方向富化按通用映射规则落位,见 docs/mappings/21。装不下的富化进 extensions.enrichments。
逻辑 Schema(07)决定字段含义。Kafka 消息与逻辑信封同树,只把 meta 三个时间编成 unix 毫秒;
032 再按 jsonpaths 投影到 sdm_event_behavior。Kafka 层没有第二套投影注册表。
当前 Doris 为 39 列(33 标量 + 6 VARIANT),DDL 031,装载 032。
subject.endpoint.ip
完整 typed object 存在 subject_detail / object_detail / carriers / facets / observation_detail / extensions,保存未投影的多值和详细字段。
subject_ref_id
身份与枚举标量。来源于本条 Kafka 消息的 07 树,由 032 jsonpaths 抽取,不能另开一套映射。
| 投影方式 | 规则 |
|---|---|
| 身份 / 枚举标量 | 032 jsonpaths 从 07 树抽取(subject_ref_id、behavior_type、assertion_severity 等) |
| typed object VARIANT | subject_detail / object_detail / carriers / facets / observation_detail / extensions 保存权威细节 |
| 时间编码 | Kafka 为 unix 毫秒;032 FROM_UNIXTIME 秒/毫秒自适应,缺 ingest/parse 填 NOW(3) |
carrier_role | D6 非权威查询投影,jsonpaths 直取 $.carriers[0].carrier_role;多承载者检索读 carriers VARIANT |
唯一键 tenant_id + occur_time + event_id,按 occur_time 日分区,HASH 8 bucket。标量是检索锚点,不替代 typed object。
身份与枚举列建 INVERTED;VARIANT 不建无约束全路径索引。carrier_role 因 VARIANT 路径过滤超时才加标量倒排。
行为表不存原文;经 event_id 关联 raw_log(DDL 006)。
本阶段物理上只落 sdm_event_behavior;实体索引仍是 S4 概念(entity_id / event_ref / event_kind / event_time),sdm_event_state 暂缓。旧统一主表 sdm_event 冻结,不为状态预留宽表。
实时流 → 行为流直接输出;「最近事件」看板 → 直接查行为表;带实体的时间线 → 实体索引解决。状态模型定稿后再评估统一视图。
source_ip / roles_obj / source_finding_obj)。meta.occur_time / ingest_time / parse_time 必须是 unix 毫秒整数;写 ISO 字符串会装载失败。carriers[] 顺序;多值检索读 VARIANT。schema_version 为字符串 2.0。| raw_log 列 | 类型 | 说明 |
|---|---|---|
tenant_id | VARCHAR(128) · NN | 租户编号:与 sdm_event_behavior.tenant_id 一致 |
occur_time | DATETIME(3) · NN | 事件发生时间:与行为表一致,作分区与主键组成 |
event_id | VARCHAR(128) · NN | 与 sdm_event_behavior.event_id 一致,1:1 关联键 |
raw_msg | STRING | 原始日志:未经标准化的源日志原文 |
raw_msg_digest | VARCHAR(64) · NN | 原文摘要(十六进制),同文检测与变更判定 |
digest_algo | VARCHAR(16) · NN | 摘要算法标识,默认 sha256 |
idx_raw_msg | INVERTED | 原文全文检索(unicode parser) |
idx_raw_msg_digest | INVERTED | 同文检测 / 去重回查 |
tenant_id + occur_time + event_id 唯一键与日分区与 sdm_event_behavior 同构;HASH 列相同、bucket 数独立(原文 16 / 行为 8)。同一 event_id 重灌时两表 upsert 行为一致。
WHERE digest_algo = ? AND raw_msg_digest = ? 判定原文是否同文;digest 由上游 wparse 计算后随消息写入,落库不重算。
raw_msg 全文倒排、raw_msg_digest 倒排;event_id / raw_msg_digest 建 Bloom 过滤。保留期由 apply 脚本按 DORIS_RETENTION_DAYS 替换(生产默认 30 天)。
examples/ 下每个类型目录保存四件套:raw-log → wpl-output → platform-context → expected-sdm-event.behavior,以及机器可读映射 wpl-to-sdm-event。
| 厂商 | 类型数 | 日志类型(点击进入样例目录) |
|---|
按日期倒序。每条标明修订类型与影响面;权威条文以链接文档为准。
设计推演定稿后冻结的结论;变更需经模型评审,不做映射层变通。
| 规则 | 冻结结论 |
|---|---|
| 记录形态 | 当前仅有 event_kind=behavior;观察不能脱离行为单独成事件。state 作为后续保留值 |
| 主体/客体空值 | 不设置 status 字段;主体身份未采集、客体未识别或不适用时留空——留空只表示身份缺席,不表示行为不存在 |
| 观察基数 | 每条事件最多一个 observation 对象,不使用 observations[] |
| 版本字段 | 保留 meta.mapping_id;删除 mapping_revision、projection_version |
| 诊断信息 | 不进入事件模型,由独立质量对象治理 |
| 行为分类 | appear/read/change/disappear/flow 五类;behavior.layer 取 network/system/application |
| 结果枚举 | 暂保留 allowed/denied/success/failed/observed/unknown 兼容值,语义上区分处置结果、执行结果和记录状态 |
| 关联键 | 本阶段暂不定义统一结构;具有明确领域语义的值留在对应 facet,其余待后续定义 |
| 词 | 说明 |
|---|---|
| 行为(behavior) | 事件的谓语。已弃用「跃迁」——太物理 / 学术。类型五类:出现 / 读取 / 变更 / 消失 / 流动;处置结果:许可 / 拒绝 |
| 主体 / 客体 / 载体 | 事件参与者,agency 语义(谁对谁做、通过什么),不是传输语义(source/target) |
| 观察者 | 不参与事件、只观察 / 记录 / 判断的一方(设备、引擎、AI、人),承载于单对象 observation.observer |
| 事实 / 判断 | 对象层(发生了什么)与观察层(观察者怎么看)的区分——判断是观察者的行为 |
| 关联键值 | 非实体关联键的统一结构暂缓定义;实体键由主体、客体、载体承担 |
权威字段以逻辑契约字段目录与 07 行为 Schema 为准(已冻结 2.0);现行机器契约 object-registry.v2 / object-fields.v1;DDL 与装载在 schema/ 031·032。