数据收集与预处理:日志源、标准化、清洗与存储

日志是整个异常访问检测的“原材料”。原材料质量不行,后面再怎么分析也白搭。我在好几个项目里都见过团队把大量精力花在检测算法上,结果日志字段缺失、时间戳格式混乱,最后告警根本没法用。所以这一章咱们把底子打牢。

3.1 日志源:网络、主机、应用

先说说日志从哪来。我习惯把日志源分成三层:网络层、主机层、应用层。每层关注的东西不一样,采集方式也不同。

日志源层级 典型设备/组件 关键信息 常见协议/格式
网络层 防火墙、路由器、交换机、IDS/IPS 源/目的IP、端口、协议、流量方向、威胁签名 NetFlow、sFlow、syslog、PCAP
主机层 服务器操作系统、容器宿主机 进程启动、文件变更、用户登录、系统调用、资源占用 Syslog (auth.log, secure), Windows Event Log, auditd
应用层 Web服务器、数据库、中间件、业务系统 URL请求、SQL语句、API调用、用户行为、异常堆栈 Apache/Nginx access log, JSON/XML 应用日志, 自定义格式

网络层日志反映的是流量行为。举个例子,内部一台机器突然对外发起大量SSH暴力破解,防火墙日志里会出现连续的一对一登录失败记录。主机层日志能看到更底层的操作:谁在用什么权限执行了什么命令。应用层日志最贴近业务,比如同一账号在短时间内从两个不同地点登录,这就有横向挪动的嫌疑。

我曾经踩过的坑: 只接入了网络层和主机层,忽略了应用层的API调用日志。结果攻击者利用合法凭证绕过防火墙,在业务接口里拖数据,检测基线完全没反应。从那以后我坚持三层全采,哪怕应用日志量大、格式烂,也得想办法收进来。

3.2 日志标准化:把“方言”翻译成“普通话”

不同设备、不同应用输出的日志格式五花八门。有的用key=value,有的用JSON,有的干脆是自定义字符串。标准化就是要统一字段名、时间格式、严重级别等核心属性。

核心原则: 标准化只在采集侧做一次,后面的清洗、存库、分析全部依赖标准字段。

我个人习惯定义一个标准事件模型(CEM,Common Event Model),至少包含下面这些字段:

  • timestamp – 统一为 UTC 毫秒级时间戳,格式 yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
  • event_type – 例如:network_connection、file_creation、user_login、sql_query
  • source_ip / dest_ip – 字符串,带掩码时保留原始值
  • user / process – 关联主体,没有时填 “-”
  • severity – 数字 1-5,1 最严重
  • raw_message – 原始日志全文(便于回溯查证)
# 标准化示例:将 Apache 访问日志解析为标准事件
# 原始日志:
# 192.168.1.10 - - [10/May/2025:14:23:05 +0000] "GET /admin HTTP/1.1" 200 1234
#
# 标准化后:
{
  "timestamp": "2025-05-10T14:23:05.000Z",
  "event_type": "http_request",
  "source_ip": "192.168.1.10",
  "dest_port": 80,
  "user": "-",
  "severity": 3,
  "raw_message": "192.168.1.10 - - [10/May/2025:14:23:05 +0000] \"GET /admin HTTP/1.1\" 200 1234"
}
避坑提醒: 时间戳格式化最容易被忽略。有的设备用本地时间,有的用UTC,甚至同一个设备在不同时段因为夏令时还会切换。我规范里要求所有解析器输出前必须转为UTC,同时对原始时间戳做冗余保留,方便事后校对。

3.3 日志清洗:去杂质,提纯度

标准化之后,日志还是脏的。清洗是做什么?说白了就是剔除垃圾、修正错误、填充空白。

3.3.1 去重

同一个告警事件可能在短时间内被多个采集器重复上报。我记得有一次防火墙规则配置有误,同一时间外网扫描触发了上万条相同的日志记录,直接把存储写爆了。去重我常用的策略是:按照 source_ip + event_type + timestamp(秒级) 作为指纹,相同指纹只保留第一条。

3.3.2 过滤白噪声

不是所有日志对异常检测都有价值。比如内部健康检查的轮询、系统定时任务、管理员常规运维操作,这些高频低风险的事件过滤器应该提前干掉的。我在项目中维护了一个“白名单规则库”,里面包含常见的内部IP段、服务探针User-Agent、正常计划任务模式。

3.3.3 补全上下文

很多日志本身只有IP没有主机名,或者只有UID没有用户名。清洗阶段可以通过关联资产表、用户表补全这些字段。比如把内网IP转换成服务器名称,把数字UID映射成业务账号。补全后异常分析时能直接看到“哪个员工在哪台机器上干了什么”,少翻一次数据库。

清洗任务 处理逻辑 示例
去重 基于指纹窗口化去重,窗口默认 5 秒 同一 IP 连续 10 次相同 HTTP 200 → 保留 1 条
过滤 匹配白名单规则后标记 drop 并丢弃 发往内网 DNS 的查询(目的IP 10.0.0.53)→ 丢弃
补全 通过资产表查主机名、责任人、部署环境 source_ip=10.0.1.50 → hostname=web-prod-03
注意: 清洗时的过滤规则要配置告警,如果某类规则在短期内匹配了异常多的日志(例如正常探针突然被攻击改变了User-Agent),可能意味着规则失效或存在绕过行为。我通常会在清洗管道末端放一个“规则命中统计”流,偏离基线就自动阻塞并通知管理员。

3.4 日志存储:选对地方,分好层

清洗完的日志需要存到能快速查询的地方。我之前用传统关系库存海量日志,结果一张表每天几亿条,索引重建就几个小时。后来改成了分层存储方案。

3.4.1 热存储:时序数据库(TSDB)

用于最近 7 天的实时分析。我主推 Elasticsearch 或 InfluxDB,按时间+事件类型建索引。主要优点是聚合查询快,支持秒级检索。

# Elasticsearch 索引模板片段
{
  "template": "logs-*",
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "timestamp": {"type": "date"},
      "event_type": {"type": "keyword"},
      "source_ip": {"type": "ip"},
      "dest_ip":   {"type": "ip"},
      "severity":  {"type": "byte"}
    }
  }
}

3.4.2 温存储:对象存储 + 压缩

超过 7 天但小于 90 天的日志,我会从 TSDB 导出为 Parquet 格式,存到对象存储(S3/MinIO)。查询频率明显降低,但需要做回溯调查时还能通过 Spark 或 Presto 做批处理。

3.4.3 冷存储:归档

90 天以前的日志只保留摘要和原始文件压缩包,放在低成本存储上,一般用于审计合规。冷数据支持极少查询,所以不做结构化。

日志数据管道分层架构 网络日志源 syslog / NetFlow / PCAP 主机日志源 auditd / Windows Event 应用日志源 JSON / 自定义格式 标准化 + 清洗管道 统一时间戳 · 字段映射 · 指纹去重 · 白名单过滤 · 上下文补全 热存储 (7天) Elasticsearch / InfluxDB 温存储 (7~90天) Parquet + 对象存储 冷存储 (>90天) 归档压缩 + 合规审计

上面这张图把整个流程串起来了。从三层日志源出发,汇入标准化清洗管道,再根据时效要求落地到不同存储层。注意看,清洗管道是一个“集中处理”的模块,我建议把解析、去重、过滤、补全都放在同一个流式管道里(比如 Flink 或 Logstash),避免多次网络IO。

关键指标参考: 在日均日志量 30 亿条的生产环境中,标准化+清洗的吞吐量不应低于 50 万条/秒,清洗后的冗余去除率约在 15%~30% 之间(视白名单覆盖而定)。我在优化管道时,把 CPU 密集型操作(正则解析、JSON 反序列化)放到了并行度更高的槽位上,IO 密集型操作(关联资产表)则用内存缓存加速——嗯,效果不错。

3.5 小结

到这里,“数据收集与预处理”的底子算是打完了。你想想看,如果日志源不全、字段不标准、数据有重复或者缺上下文,后续的基线计算和异常检测就像在沙子上建楼。我个人的经验是:哪怕前期花再多时间设计标准化模型和清洗规则都值得,因为这些工作一旦做好,后面的分析工作就能直接用标准化字段做聚合,不用再反复应对各种“小问题”。

记一笔: 每次上线新日志源,我都会先在测试环境跑一遍标准化+清洗验证,跑出几张统计表看看字段覆盖率、重复率、白名单命中率。只有这些指标达标,才允许它进入生产管道。别问我为什么这么较真——因为我在线上吃过亏。