ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

使用KQL审计Azure Synapse Analytics数据库账号操作全流程

使用KQL审计Azure Synapse Analytics数据库账号操作全流程 1. 从一次紧急排查说起为什么我们需要审计数据库操作上个月我们团队负责的一个核心数据仓库突然出现了一个数据异常。一个关键业务报表的汇总数字在凌晨时段发生了不预期的跳变直接影响了当天晨会的决策。业务方第一时间找到我们问题很明确数据在某个时间点被修改了但没人承认执行过相关操作。我们手头有Azure Synapse Analytics当时还叫Azure SQL DW的监控告警能知道有大量写入但具体是谁、通过什么账号、执行了哪条SQL语句却无从查起。那一刻我们深刻体会到在云原生数据平台中仅有资源层面的监控是远远不够的操作层面的审计Auditing才是厘清责任、追溯根因的生命线。传统的自建数据库我们可能会去翻数据库日志文件或者依赖第三方审计工具。但在Azure Synapse Analytics这样的PaaS服务里日志的存储、格式和查询方式都发生了变化。幸运的是Azure提供了一套强大且原生的日志与分析方案将诊断日志发送到Log Analytics工作区并使用Kusto查询语言Kusto Query Language, KQL进行查询分析。KQL正是为这类大规模日志数据实时分析而生的语言其表达能力和性能在面对海量审计日志时优势尽显。本文就将围绕这个真实场景手把手带你走通整个审计链路。我们将聚焦于一个非常具体且高频的需求如何审计某个特定数据库账号在Azure Synapse Analytics中的所有操作记录。无论是为了安全合规、故障排查还是单纯的权限复核这都是数据工程师和运维人员必须掌握的技能。你会发现用KQL来做这件事不仅高效而且能挖掘出许多隐藏在简单操作背后的深层信息。2. 审计基石理解Azure Synapse Analytics的日志架构与启用在开始写查询之前我们必须先理解数据从哪里来。Azure Synapse Analytics专指专用SQL池即以前的Azure SQL DW的审计日志并非默认开启也并非存储在数据库内部。它的核心机制是诊断设置Diagnostic Settings。2.1 诊断日志审计数据的源头Azure Synapse Analytics工作区包含专用SQL池会生成多种类型的日志和指标。对于我们审计数据库操作这个目标最关键的一类日志就是SQLSecurityAuditEvents。这类日志记录了所有与SQL安全相关的事件主要包括登录成功/失败SQL批处理Batch的执行这是我们审计操作的核心权限变更GRANT, DENY, REVOKE数据库对象的创建、修改、删除这些日志事件在生成后可以被路由到不同的目的地最常用于分析和长期存储的目的地就是Log Analytics工作区。Log Analytics工作区是Azure Monitor的一部分它使用基于Kusto的引擎来存储和查询日志数据。2.2 配置步骤开启审计并指向Log Analytics如果你的Synapse工作区还没有开启审计你需要进行以下配置。这个过程通常在Azure门户完成是一次性的设置创建或选定Log Analytics工作区在Azure门户中你需要先有一个Log Analytics工作区。如果是为了审计新建的建议取一个易于识别的名字如law-synapse-audit-prod。配置诊断设置导航到你的Azure Synapse Analytics工作区资源。在左侧菜单的“监控”部分找到“诊断设置”。点击“ 添加诊断设置”。在设置页面中诊断设置名称例如SendAuditToLogAnalytics。类别详情勾选SQLSecurityAuditEvents。这是必须勾选的其他日志类别如请求、SQL请求可能对性能分析有用但非审计必需。目标详细信息选择“发送到Log Analytics工作区”然后在下拉列表中选择你创建或指定的工作区。点击“保存”。注意启用诊断设置并开始流式传输日志后数据出现在Log Analytics中会有几分钟的延迟通常5-10分钟。此外向Log Analytics发送数据会产生相应的数据引入和保留费用在规划时需要根据日志量评估成本。完成这个配置后所有在专用SQL池上发生的安全审计事件都会自动、持续地流入指定的Log Analytics工作区中的特定表里。我们的KQL查询舞台就此搭建完毕。2.3 目标数据表AzureDiagnostics还是SynapseSQLLogs这里有一个关键的细节需要注意。根据Azure的更新和资源创建方式审计日志可能出现在两个不同的表中AzureDiagnostics这是一个通用的诊断日志表许多Azure服务的日志都会汇集于此。资源类型ResourceType为SQLDATABASES且类别Category为SQLSecurityAuditEvents的记录就是我们要找的。这是较早期或通过某些方式创建的资源常见的路径。SynapseSQLLogs这是为Azure Synapse Analytics的SQL相关日志量身定制的专用表结构更清晰。如果能看到这个表通常优先使用它。在我们的查询中需要先确认日志存在于哪个表。可以通过一个简单的探测查询来确认// 探测 SynapseSQLLogs 表 SynapseSQLLogs | where Category SQLSecurityAuditEvents | take 1 // 探测 AzureDiagnostics 表 AzureDiagnostics | where ResourceType SQLDATABASES and Category SQLSecurityAuditEvents | take 1哪个查询能返回结果就说明审计日志流向了哪个表。下文的示例将主要使用SynapseSQLLogs因为它更直观。如果您的环境是AzureDiagnostics只需替换表名并调整where条件即可核心KQL逻辑完全一致。3. KQL核心实战定位与解析特定账号的操作流水假设我们需要审计的数据库账号是app_user。我们的目标是从海量日志中精准筛选出与该账号相关的所有操作并以清晰、可读的方式呈现。3.1 基础查询筛选与关键字段提取首先我们构建一个最基础的查询获取app_user的所有安全审计事件。SynapseSQLLogs | where Category SQLSecurityAuditEvents | where SessionServerPrincipalName contains app_user // 包含匹配避免因域名等前缀导致遗漏 | project TimeGenerated, SessionServerPrincipalName, ServerPrincipalName, DatabaseName, Statement, Succeeded, ClientIp, ApplicationName | order by TimeGenerated desc这个查询做了以下几件事where Category SQLSecurityAuditEvents过滤出安全审计事件。where SessionServerPrincipalName contains app_user在SessionServerPrincipalName字段中查找包含app_user的记录。这是登录时使用的账号名是关联操作最可靠的字段之一。使用contains而非是为了兼容可能带域名的账号如domain\app_user。project选择我们需要关注的字段。这是提高查询效率和可读性的关键步骤。TimeGenerated操作发生的时间UTC。SessionServerPrincipalName发起会话的登录名。ServerPrincipalName语句执行上下文中的用户名在EXECUTE AS等场景下可能与登录名不同。DatabaseName操作发生的数据库。Statement执行的SQL语句文本这是审计的核心内容。Succeeded操作是否成功True/False。审计失败登录尝试非常重要。ClientIp发起连接的客户端IP地址。ApplicationName客户端应用程序名称如SSMS、Power BI、自定义应用。order by TimeGenerated desc按时间倒序排列最新的操作在最前面。3.2 深度过滤精准匹配与上下文还原基础查询可能带回很多结果包括其他包含“app_user”字符串的账号虽然概率低。我们可以更精确并丰富查询维度。场景一精确审计app_user账号的所有DML操作INSERT, UPDATE, DELETE, SELECTSynapseSQLLogs | where Category SQLSecurityAuditEvents | where SessionServerPrincipalName has app_user // has 运算符用于全字匹配比 contains 更精确 | where Statement has_any (INSERT, UPDATE, DELETE) or Statement startswith SELECT // 过滤DML语句 | extend StatementSummary substring(Statement, 0, 200) // 只截取语句前200字符便于预览 | project TimeGenerated, SessionServerPrincipalName, DatabaseName, StatementSummary, Succeeded, ClientIp, ApplicationName | order by TimeGenerated desc这里引入了has运算符进行更精确的匹配并使用has_any和startswith来筛选特定的SQL操作类型。extend创建了新列StatementSummary因为完整的Statement可能非常长在结果预览时截取部分内容更友好。场景二审计特定时间段内针对某张关键表如dbo.Sales的所有操作let targetUser app_user; let targetTable Sales; SynapseSQLLogs | where Category SQLSecurityAuditEvents | where TimeGenerated between (datetime(2023-10-27) .. datetime(2023-10-28)) // 指定时间范围 | where SessionServerPrincipalName has targetUser | where Statement contains targetTable // 语句中包含目标表名 | project TimeGenerated, SessionServerPrincipalName, DatabaseName, Statement, ClientIp, ApplicationName | order by TimeGenerated asc // 按时间正序排列方便看操作序列这个查询使用了KQL的let语句定义变量使查询更清晰、易于修改。between运算符用于限定时间范围这对于追溯某个时间段内的操作至关重要。按时间正序asc排列可以像看剧本一样还原操作序列。3.3 聚合分析与洞察不止于列表KQL的强大之处在于其聚合分析能力。我们不仅能列出操作还能生成洞察。分析一统计app_user账号每日执行各类操作的数量SynapseSQLLogs | where Category SQLSecurityAuditEvents | where SessionServerPrincipalName has app_user | extend OperationType case( Statement has INSERT, INSERT, Statement has UPDATE, UPDATE, Statement has DELETE, DELETE, Statement startswith SELECT, SELECT, OTHER // 其他类型操作 ) | summarize OperationCount count() by bin(TimeGenerated, 1d), OperationType | order by TimeGenerated desc, OperationType这个查询使用extend配合case()函数将原始的Statement文本分类成INSERT、UPDATE、DELETE、SELECT、OTHER等操作类型。然后使用summarize和count()按天bin(TimeGenerated, 1d)和操作类型进行聚合计数。结果是一张清晰的统计表可以快速了解该账号的活动模式。分析二找出app_user从非常用IP或应用发起的敏感操作如DROP, ALTERlet commonIPs dynamic([10.0.1.100, 10.0.1.101]); // 定义常用IP白名单 let commonApps dynamic([Microsoft SQL Server Management Studio, PowerBI]); // 定义常用应用白名单 SynapseSQLLogs | where Category SQLSecurityAuditEvents | where SessionServerPrincipalName has app_user | where Statement has_any (DROP, ALTER TABLE, TRUNCATE TABLE) // 敏感操作 | where (ClientIp !in (commonIPs)) or (ApplicationName !in (commonApps)) // 不在白名单内 | project TimeGenerated, SessionServerPrincipalName, Statement, ClientIp, ApplicationName, DatabaseName | order by TimeGenerated desc这是一个安全审计的典型用例。通过定义“正常”行为基线常用IP、常用应用利用KQL的!in不在集合内运算符可以快速筛选出异常或高风险的敏感操作记录用于安全告警或深入调查。4. 高级排查与避坑指南从日志到真相有了查询能力但在真实的故障排查中直接套用模板往往不够。以下是我在实际工作中总结的几个关键点和常见“坑”。4.1 字段辨析SessionServerPrincipalNamevsServerPrincipalName这是最容易混淆的一对字段理解错误可能导致审计遗漏。SessionServerPrincipalName登录名Login Name。用户连接SQL池时使用的身份。例如你用app_user这个登录名连接服务器这个字段就是app_user。绝大多数情况下审计应该基于这个字段。ServerPrincipalName执行上下文中的用户名。在使用了EXECUTE AS USER ‘some_user’或EXECUTE AS LOGIN ‘some_login’的存储过程或动态SQL内部这个字段会变成被模拟的用户而SessionServerPrincipalName仍然是原始的登录名。实战案例有一次排查数据删除问题我们用登录名过滤一无所获。后来发现操作是在一个存储过程中执行的该存储过程使用了EXECUTE AS OWNER。最终在日志中SessionServerPrincipalName是调用存储过程的应用程序账号而ServerPrincipalName是存储过程的所有者一个高权限账号。因此在审计存储过程调用等复杂场景时需要同时关注这两个字段。4.2 语句截断与参数化查询审计日志中的Statement字段可能被截断。对于非常长的SQL批处理你可能看不到完整的语句。这时需要结合其他字段判断。 另外应用程序使用参数化查询如SELECT * FROM t WHERE id id时日志中记录的是带参数的语句而不是具体的值。这保护了敏感数据但也意味着你无法直接从日志看到操作的具体数据内容。排查时需要结合应用程序日志或数据库时间点还原功能。4.3 时间范围与性能优化审计日志量可能非常庞大。一个不加时间范围限制的全表扫描查询可能会超时或消耗极高的查询资源。最佳实践始终在查询开始时使用时间过滤器。Log Analytics默认会利用时间索引。// 好的查询 SynapseSQLLogs | where TimeGenerated ago(7d) // 只看最近7天 | where Category SQLSecurityAuditEvents ... // 避免的查询性能差 SynapseSQLLogs | where Category SQLSecurityAuditEvents | where TimeGenerated ago(7d) // 时间过滤应尽量前置 ...ago()函数非常方便如ago(1h)、ago(7d)。对于长期审计分析可以结合bin()函数进行分时段聚合避免直接查询原始海量数据。4.4 失败操作审计被忽略的安全盲区我们通常关注成功的操作但失败的操作Succeeded False同样重要尤其是登录失败和权限错误。// 监控 app_user 近期的所有失败操作 SynapseSQLLogs | where TimeGenerated ago(1d) | where Category SQLSecurityAuditEvents | where SessionServerPrincipalName has app_user | where Succeeded False | project TimeGenerated, SessionServerPrincipalName, ErrorCode, Statement, ClientIp | order by TimeGenerated desc频繁的登录失败可能意味着密码爆破尝试。特定的权限错误如The SELECT permission was denied可能表明应用程序的权限配置有问题或者有异常访问企图。将这些查询保存并定期检查是安全运营的基础。5. 构建持续监控与自动化审计方案手动查询只能应对事后排查。一个成熟的运维体系需要将审计自动化。5.1 保存查询与仪表板在Log Analytics中你可以将上面任何有用的查询保存。保存的查询可以快速复用也可以被固定到Azure仪表板上。例如创建一个名为“关键账号操作监控”的仪表板磁贴显示app_user过去24小时的操作数量统计、失败操作列表、敏感操作列表等。这样每天打开仪表板就能一目了然。5.2 日志告警实时响应风险操作这是自动化审计的核心。我们可以基于KQL查询创建日志告警规则。场景当app_user账号执行DROP TABLE或TRUNCATE TABLE操作时立即触发告警。在Log Analytics中使用类似下面的查询SynapseSQLLogs | where TimeGenerated ago(5m) // 告警规则通常评估最近5-15分钟的数据 | where Category SQLSecurityAuditEvents | where SessionServerPrincipalName has app_user | where Statement has_any (DROP TABLE, TRUNCATE TABLE) | count点击“新建警报规则”。配置逻辑当查询结果“计数”大于0时触发。配置操作组触发后可以发送邮件、短信、调用Webhook如触发Azure Logic App运行审批流程、或在Teams/Slack中发送通知甚至启动Azure自动化Runbook来执行临时性的安全加固脚本。通过告警审计从事后追溯变成了事中响应极大地提升了安全水位。5.3 定期合规报告对于合规要求如SOX, GDPR可能需要定期出具审计报告。你可以利用Azure 数据资源管理器ADX的连续导出功能或Logic Apps定期如每周运行一个汇总KQL查询将app_user账号的所有关键操作如数据修改、权限变更汇总并自动发送到指定的存储账户或生成邮件报告。例如一个每周运行的报告查询可能长这样SynapseSQLLogs | where TimeGenerated between (startofweek(ago(7d)) .. endofweek(ago(1d))) // 上周整周数据 | where Category SQLSecurityAuditEvents | where SessionServerPrincipalName has app_user | where Statement has_any (INSERT, UPDATE, DELETE, GRANT, REVOKE, CREATE, ALTER, DROP) | summarize StartTime min(TimeGenerated), EndTime max(TimeGenerated), Operations count(), DistinctTablesApprox dcountif(Statement, Statement contains FROM or Statement contains INTO) // 近似统计涉及的表 by SessionServerPrincipalName, DatabaseName | order by DatabaseName从被动地“救火”排查到主动地监控仪表板再到实时的告警响应和自动化的合规报告KQL赋予我们的能力是层层递进的。它不仅仅是一个查询工具更是构建云原生数据平台可观测性与安全体系的核心组件。掌握它意味着你能真正“看见”数据平台内部发生的每一件事并为数据的安全与稳定保驾护航。
返回列表