Kusto 图语义
使用KQL图运算符从表格数据构建临时图和持久图。此技能将自然语言转换为边优先的图构建模式和图查询运算符。
激活触发器
当用户出现以下情况时,使用此技能:
- 想要从表格数据构建图(
make-graph) - 要求查找数据中的模式、路径或关系
- 提到
graph-match、graph-shortest-paths、graph-to-table、graph-mark-components - 想要创建持久图模型或快照
- 说“构建一个图”、“查找最短路径”、“查找连通分量”、“显示关系”
- 询问关于临时图与持久图的问题
并非自然语言到KQL的转换器。 输入通常应是一个工作正常的KQL查询,用户希望将其结果转换为图,并附上所需图结构的自然语言描述。仅当请求直接映射到具有明显列的已知表时,才支持基本的自然语言源请求。对于一般的NL-to-KQL转换,请使用专用的查询生成技能(可单独获取)。
补充技能:
azure-kusto-irql-- 可组合的安全查询原语,为图生成表格输入azure-kusto-irql-graph-- IRQL的Lift_To_GraphJSON映射系统,用于在Kusto Explorer中创建富类型、带图标装饰的图
边优先方法
在Kusto中构建图的基本模式:
1. 定义你的边 --> 源 -> 目标,带有关系类型/属性
2. 定义你的节点查找表 --> 每个节点ID的显示名称、类型、属性
3. 合并边类型 --> 如果有多个关系类型
4. 合并节点查找表 --> 如果有多个节点类型
5. 调用make-graph --> 边 | make-graph 源 --> 目标 with nodes on 节点Id
这就是用make-graph思考的方式。边是你关心的关系。节点是查找表,为这些ID赋予外观——显示名称、类型、属性。
图运算符参考
make-graph -- 从表构建图
边 | make-graph 源Id --> 目标Id with 节点 on 节点Id
边: 表格源,每行是一条边源Id --> 目标Id: 包含源和目标节点ID的列with 节点 on 节点Id: 可选的节点属性表,通过ID连接- 支持多个节点表:
with 节点1 on Id1, 节点2 on Id2 - 在边中出现但节点表中缺失的节点将获得空属性
graph-match -- 查找模式
G | graph-match (a)-[e]->(b) where <约束> project <输出>
模式符号:
| 元素 | 命名 | 匿名 |
|---|---|---|
| 节点 | (n) | () |
| 边 左->右 | -[e]-> | --> |
| 边 右->左 | <-[e]- | <-- |
| 任意方向 | -[e]- | -- |
| 可变长度 | -[e*1..5]-> | -[*1..5]-> |
多跳模式:(a)-[e1]->(b)-[e2]->(c)
星型模式:(a)--(center)--(b), (c)--(center)--(d)
循环控制:cycles = all | none | unique_edges(默认:unique_edges)
graph-shortest-paths -- 查找最短路径
G | graph-shortest-paths (start)-[e*1..20]->(end)
where start.name == "Alice" and end.name == "Server01"
project 路径 = e, 长度 = array_length(e)
- 至少需要一条可变长度边
output = any(默认,每对返回一条路径)或output = all(所有等长最短路径)- 可变长度边的属性作为动态数组返回
graph-to-table -- 将图导出为表
G | graph-to-table nodes // 导出节点
G | graph-to-table edges // 导出边
G | graph-to-table nodes as N, edges as E // 导出两者
G | graph-to-table nodes with_node_id=Id // 包含节点哈希ID
G | graph-to-table edges with_source_id=Src with_target_id=Tgt // 包含边端点ID
graph-mark-components -- 查找连通分量
G | graph-mark-components with_component_id=ComponentId
| graph-to-table nodes
| summarize 成员 = make_list(name) by ComponentId
为每个节点分配一个ComponentId。同一连通分量中的节点共享相同的ID。
graph() 函数 -- 查询持久图
graph("MyGraphModel") // 最新快照
graph("MyGraphModel", "Snapshot_2025_01") // 特定快照
graph("MyGraphModel", true) // 根据模型定义创建的临时图
临时图
在查询执行过程中动态创建。无需设置。非常适合临时分析、探索和原型设计。
模板:基本双实体图
// 1. 定义边
let edges = <源表>
| summarize <聚合> by 源列, 目标列;
// 2. 定义节点查找表
let source_nodes = edges
| distinct 源列
| project nodeId = 源列, label = 源列, nodeType = "<源类型>";
let target_nodes = edges
| distinct 目标列
| project nodeId = 目标列, label = 目标列, nodeType = "<目标类型>";
let all_nodes = union source_nodes, target_nodes;
// 3. 构建并查询图
edges
| make-graph 源列 --> 目标列 with all_nodes on nodeId
| graph-match (s)-[e]->(t)
where <约束>
project 源 = s.label, 目标 = t.label, <边属性>
模板:多关系图
// 多种边类型 -> 使用通用模式合并它们
let auth_edges = 认证事件
| project 源 = username, 目标 = hostname, edgeType = "authenticates", ts = timestamp;
let net_edges = 网络事件
| project 源 = src_ip, 目标 = url, edgeType = "connects", ts = timestamp;
let all_edges = union auth_edges, net_edges;
// 来自所有来源的节点查找表
let user_nodes = 员工 | project nodeId = username, label = name, nodeType = "用户";
let host_nodes = 认证事件 | distinct hostname | project nodeId = hostname, label = hostname, nodeType = "主机";
let all_nodes = union user_nodes, host_nodes;
all_edges
| make-graph 源 --> 目标 with all_nodes on nodeId
持久图
适用于大规模、可复用的图。存储在数据库元数据中。支持用于历史比较的快照。
安全: 创建或更改图模型和快照会修改数据库。在执行
.create-or-alter graph_model或.make graph_snapshot之前,始终显示确切命令并征得用户确认。
步骤1:创建图模型
.create-or-alter graph_model SecurityGraph
{
"Schema": {
"Nodes": {
"User": {"name": "string", "role": "string"},
"Host": {"hostname": "string"},
"IP": {"ip": "string"}
},
"Edges": {
"AuthenticatesTo": {"timestamp": "datetime", "result": "string"},
"ConnectsFrom": {"timestamp": "datetime"}
}
},
"Definition": {
"Steps": [
{
"Kind": "AddNodes",
"Query": "Employees | project name, role",
"NodeIdColumn": "name",
"Labels": ["用户"]
},
{
"Kind": "AddNodes",
"Query": "AuthenticationEvents | distinct hostname | project hostname",
"NodeIdColumn": "hostname",
"Labels": ["主机"]
},
{
"Kind": "AddEdges",
"Query": "AuthenticationEvents | project username, hostname, timestamp, result",
"SourceColumn": "username",
"TargetColumn": "hostname",
"Labels": ["AuthenticatesTo"]
}
]
}
}
步骤2:创建快照
.make graph_snapshot SecurityGraph Snapshot_2025_07
步骤3:查询快照
graph("SecurityGraph")
| graph-match (user)-[auth]->(host)
where user.role == "Admin" and auth.result == "Failed Login"
project 用户 = user.name, 主机 = host.hostname, 时间 = auth.timestamp
管理命令
安全: 以下所有控制命令都会修改或删除数据库对象。切勿自动执行
.drop、.create-or-alter graph_model或.make graph_snapshot。始终显示确切的命令、集群、数据库和受影响的对象的, 然后要求用户明确确认后再执行。
.show graph_models // 列出所有模型
.show graph_model SecurityGraph // 显示模型详情
.show graph_snapshots SecurityGraph // 列出快照
.drop graph_snapshot SecurityGraph Snapshot_2025_07 // 删除快照(先确认)
.drop graph_model SecurityGraph // 删除模型及所有快照(先确认)
临时图 vs 持久图:何时使用哪种
| 因素 | 临时图 (make-graph) | 持久图 (graph()) |
|---|---|---|
| 设置 | 无 -- 查询内联 | 创建模型 + 快照 |
| 生命周期 | 仅查询执行期间 | 存储在数据库元数据中 |
| 数据新鲜度 | 始终最新 | 创建快照时的数据 |
| 规模 | 受查询内存限制 | 企业级规模 |
| 复用 | 每次查询重建 | 跨用户/查询共享 |
| 最适合 | 临时调查、原型设计 | 生产工作流、仪表板 |
安全与威胁猎杀示例
认证图:谁从哪里登录了什么
let auth_edges = AuthenticationEvents
| summarize
logins = count(),
fails = countif(result == "Failed Login")
by src_ip, username, hostname;
let ip_nodes = auth_edges | distinct src_ip
| project nodeId = src_ip, label = src_ip, nodeType = "IP";
let user_nodes = auth_edges | distinct username
| project nodeId = username, label = username, nodeType = "用户";
let host_nodes = auth_edges | distinct hostname
| project nodeId = hostname, label = hostname, nodeType = "主机";
let all_nodes = union ip_nodes, user_nodes, host_nodes;
// IP -> 用户 边
let ip_user = auth_edges
| project 源 = src_ip, 目标 = username, logins, fails;
// 用户 -> 主机 边
let user_host = auth_edges
| project 源 = username, 目标 = hostname, logins, fails;
union ip_user, user_host
| make-graph 源 --> 目标 with all_nodes on nodeId
| graph-match (ip)-[e1]->(user)-[e2]->(host)
where e2.fails > 20
project
IP = ip.label,
用户 = user.label,
主机 = host.label,
失败次数 = e2.fails
| order by 失败次数 desc
横向移动检测:共享受损主机的用户
// 模式:(user1)-[auth1]->(host)<-[auth2]-(user2)
// 两个用户在同一主机上同时失败 = 可能的凭据喷射
let edges = AuthenticationEvents
| summarize fails = countif(result == "Failed Login"), logins = count()
by username, hostname;
let nodes = union
(edges | distinct username | project nodeId = username, nodeType = "用户"),
(edges | distinct hostname | project nodeId = hostname, nodeType = "主机");
edges
| make-graph username --> hostname with nodes on nodeId
| graph-match (u1)-[e1]->(h)<-[e2]-(u2)
where u1.nodeId != u2.nodeId and e1.fails > 10 and e2.fails > 10
project
用户1 = u1.nodeId, 用户2 = u2.nodeId,
共享主机 = h.nodeId,
用户1失败次数 = e1.fails, 用户2失败次数 = e2.fails
| distinct 用户1, 共享主机, 用户2, 用户1失败次数, 用户2失败次数
| order by 用户1失败次数 + 用户2失败次数 desc
最短攻击路径
let edges = SecurityEvents
| project 源 = source_entity, 目标 = target_entity, action, timestamp;
let nodes = union
(edges | distinct 源 | project nodeId = 源),
(edges | distinct 目标 | project nodeId = 目标);
edges
| make-graph 源 --> 目标 with nodes on nodeId
| graph-shortest-paths (start)-[e*1..10]->(end)
where start.nodeId == "ExternalIP_1.2.3.4" and end.nodeId == "DatabaseServer"
project
路径长度 = array_length(e),
动作 = e.action,
跳数 = e.目标
连通分量:查找孤立集群
let edges = NetworkFlows
| project 源 = src_ip, 目标 = dst_ip;
let nodes = union
(edges | distinct 源 | project nodeId = 源),
(edges | distinct 目标 | project nodeId = 目标);
edges
| make-graph 源 --> 目标 with nodes on nodeId
| graph-mark-components with_component_id = ComponentId
| graph-to-table nodes
| summarize 成员 = make_list(nodeId), 大小 = count() by ComponentId
| order by 大小 desc
在Kusto Explorer中可视化
在make-graph处结束查询(不通过管道传递给graph-match)可触发Kusto Explorer的交互式图可视化窗口:
edges
| make-graph 源 --> 目标 with all_nodes on nodeId
// <- 停在这里。Kusto Explorer会以可视方式呈现图。
要将其展平为表用于仪表板或导出,请通过管道传递给graph-match | project或graph-to-table。
与IRQL配合使用
在处理安全数据时,考虑使用azure-kusto-irql技能中的IRQL选择器(Get_*)作为数据源。IRQL为您提供统一的模式,无需记忆原始表名或列映射。对于带有图标和节点折叠的丰富可视化,azure-kusto-irql-graph技能的Lift_To_Graph是更快的路径。
| 方法 | 最适合 |
|---|---|
原始make-graph(本技能) | 完全控制、持久模型、最短路径、连通分量、自定义模式 |
Lift_To_Graph(azure-kusto-irql-graph) | 在Kusto Explorer中快速创建带图标装饰的可视化、节点折叠 |
IRQL Get_* -> make-graph | 以IRQL的统一模式作为输入,然后使用原始图运算符进行分析 |
IRQL Get_* -> Lift_To_Graph -> Graph_Render_View | 从问题到可视图形的最快路径 |
注意:
Lift_To_Graph、Graph_Render_View和Graph_Fold_By_Property是存储函数,而非内置运算符。它们已预部署在kc7001示例集群上,但可能需要部署到其他集群。有关函数定义和部署说明,请参阅azure-kusto-irql-graph/references/DEPLOY_IRQL_FUNCTIONS.md。
示例:IRQL选择器 -> make-graph -> 最短路径
IRQL处理数据检索;make-graph处理图结构。
...(由于原文已截断,后续内容无法完整翻译)
此翻译由AI生成,旨在提供参考。对于官方或关键用途,请参照原始英文文档。