ES|QL LOOKUP JOIN实现日志去重现代方案

哈希、存储、关联:基于ES|QL LOOKUP JOIN的现代日志去重解决方案

数据保真度的高成本:当更多数据并非更好时

在网络安全和可观测性领域,领导层面临一个持续困境:对完全可见性的非协商要求与有限预算的严峻现实之间的冲突。这种冲突在PowerShell日志记录中表现得最为明显。

对于任何拥有大量Windows环境现代企业而言,PowerShell是管理和自动化的引擎。然而,其强大功能和普遍性使其成为攻击者的主要目标,他们利用它进行无文件恶意软件执行、横向移动和凭据盗窃。

为了应对这种威胁,安全框架要求进行全面日志记录,特别是PowerShell脚本块日志记录(事件ID 4104)。这些日志对于威胁狩猎至关重要,因为它们捕获了攻击者运行的确切去混淆代码。然而,这种安全要求可能造成数据管理危机。启用完整脚本块日志记录会生成大量事件,可能产生TB级数据,导致显著的存储和索引成本。

潜力:大幅降低存储

让我们量化潜在收益。考虑一个现实企业场景:标准的9KB PowerShell健康检查脚本在10,000台服务器上每10分钟运行一次。这每天生成1,440,000个事件。该技术对存储消耗的影响是变革性的。

指标 优化前 优化后 影响
脚本文本存储/天 12.96GB 9KB 减少99.99%
记录的总事件数 144万 144万(精简)+ 1(完整) 事件元数据无损失
分析能力 完整脚本文本可用 按需提供完整脚本文本 保真度零损失
安全规则覆盖 完整 完整(通过查找索引扫描进行调查) 零盲点创建

策略:哈希、存储一次、按需查找

该解决方案的核心原则是智能数据去重。在任何大型企业中,同一组用于健康检查、应用程序监控和管理任务的PowerShell脚本每天执行数百万次。传统方法在每次执行时捕获这些脚本的完整多KB文本,导致大量数据冗余。

考虑您的日常通勤。您不需要每次旅行都有新的详细地图;只需要一个说明:走了通常路线。我们的日志记录策略应用相同的逻辑。我们不每次都存储完整的"地图"(脚本文本),而是存储一次,然后简单地为每次执行记录一个轻量级引用——哈希。

该架构建立在三个支柱上:

事件克隆(Logstash):作为多功能服务器端数据处理管道,Logstash适用于复杂路由。我们将使用其克隆过滤器为每个PowerShell脚本块日志事件创建飞行中副本。

通过哈希去重(摄取管道):Elasticsearch摄取管道将处理原始事件和克隆事件。它生成脚本文本的唯一哈希,作为去重键。管道从原始事件中剥离庞大的脚本文本,将克隆转换为最小化的"查找"文档,仅包含脚本文本、相关元数据和安全检测规则及其哈希。"查找"文档存储在单分片查找索引中,重复项会自动覆盖。

按需丰富(ES|QL):ES|QL是支持原生联接的强大管道查询引擎。在查询时,分析师可以使用LOOKUP JOIN命令无缝地将精简事件元数据(如时间戳、主机和用户)与查找索引中的完整脚本文本重新结合,按需提供完整上下文。

实施展示

先决条件

开始前,确保环境满足以下要求:

  • Elastic Stack版本8.18/9.0及更新版本
  • 启用PowerShell日志记录
  • Elastic Windows集成
  • Logstash部署
  • 了解数据特征

步骤1:设置查找索引

首先为查找索引创建模板,包括具有最小映射的组件模板和将index.mode设置为lookup的索引模板。

PUT _component_template/logs-windows.powershell_operational_lookup@package
{
  "template": {
    "settings": {
      "index": {
        "lifecycle": {
          "name": "logs-powershell_script_block"
        },
        "default_pipeline": "logs-windows.powershell_operational-3.0.0",
        "mapping": {
          "total_fields": {
            "limit": "1000"
          }
        }
      }
    },
    "mappings": {
      "properties": {
        "@timestamp": {
          "ignore_malformed": false,
          "type": "date"
        },
        "powershell": {
          "type": "object",
          "properties": {
            "file": {
              "type": "object",
              "properties": {
                "script_block_text": {
                  "analyzer": "powershell_script_analyzer",
                  "type": "text"
                },
                "script_block_hash": {
                  "ignore_above": 1024,
                  "type": "keyword"
                }
              }
            }
          }
        }
      }
    }
  }
}

步骤2:Logstash中的事件处理

在Logstash管道中拦截PowerShell脚本块日志(事件ID 4104),并使用克隆过滤器创建副本。

filter {
  if ([event][module] == "powershell" or [event][provider] == "Microsoft-Windows-PowerShell") and [event][code] == "4104" and [winlog][event_data][MessageTotal] == "1" {
    clone {
      clones => ["powershell_scriptblock_reduction"]
      ecs_compatibility => v8
    }
    if !("powershell_scriptblock_reduction" in [tags]) {
      mutate {
        update => { "[data_stream][namespace]" => "default_reduced" }
        add_field => {
          "[fields][duplicated]" => true
        }
      }
    }
  }
}

步骤3:路由到不同目的地

使用Logstash输出配置将事件路由到不同目的地。

output {
  if "powershell_scriptblock_reduction" in [tags]{
    elasticsearch {
      cloud_id => "..."
      index => "logs-windows.powershell_operational_lookup-default"
      action => "index"
      api_key => "${ES_API}"
      ssl_enabled => true
    }
  }
  else {
    elasticsearch {
      cloud_id => "..."
      data_stream => "true"
      api_key => "${ES_API}"
      ssl_enabled => true
    }
  }
}

步骤4:使用摄取管道处理

基于Elastic集成的摄取处理流程,两个事件都由自定义摄取管道处理,该管道使用条件逻辑根据在Logstash中添加的标志应用不同的处理器。

{
  "processors": [
    {
      "remove": {
        "ignore_missing": true,
        "field": "message",
        "if": "ctx.powershell?.file?.script_block_hash!= null && ctx.fields?.duplicated!= null && (ctx.fields?.duplicated == true || ctx.fields?.duplicated == 'true')"
      }
    },
    {
      "script": {
        "if": "ctx.tags!= null && ctx.tags.contains('powershell_scriptblock_reduction') && ctx.powershell?.file?.script_block_hash!= null",
        "source": "ctx._id = ctx.powershell.file.script_block_hash"
      }
    }
  ]
}

步骤5:使用ES|QL重建上下文

实施后,主数据流包含精简事件,查找索引保存唯一的非分段PowerShell脚本集。最后一步是在分析期间将这些上下文结合在一起。

FROM logs-windows.powershell_operational-default_reduced* 
| WHERE powershell.file.script_block_hash IS NOT NULL
| RENAME @timestamp AS original_timestamp, user.id AS original_user.id
| LOOKUP JOIN logs-windows.powershell_operational_lookup-default-2025.06.26-000004 ON powershell.file.script_block_hash
| RENAME original_timestamp AS @timestamp, original_user.id AS user.id

更智能日志记录的一瞥

这种"哈希、存储、关联"模式展示了一种打破安全可见性和数据成本之间僵局的强大方法。它应被视为任何具有庞大或重复数据的高容量日志源(如API有效负载或应用程序堆栈跟踪)的可重用蓝图。

对于希望将数据优化提升到新水平的团队,该技术可以与其他功能分层使用。在使用此方法移除高容量、重复内容后,可以在主数据流上启用logsdb索引模式(在9.0及更高版本的集群上默认启用)。
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)或者 我的个人博客 https://blog.qife122.com/
公众号二维码

posted @ 2025-08-31 14:06  CodeShare  阅读(9)  评论(0)    收藏  举报