2. 日志存储与格式

聊到日志篡改和注入攻击,先得把底层的“食材”搞明白——日志到底长什么样,存在哪儿。我刚开始做安全运维那会儿,接过一个客户现场:告警系统天天报“日志格式异常”,查了半天才发现,他们同一台服务器上混着三种不同的日志格式,解析器直接崩溃了。嗯,从那以后我养成了一个习惯——先统一格式,再谈分析。

说白了,日志格式决定了你能做什么。是结构化还是半结构化,字段固定还是自由文本,这些会直接影响你后面的检测算法能不能跑得动。这一节我们就把主流的几种格式和存储策略捋一遍。

2.1 常见日志格式一览

目前生产环境中碰得最多的就三种:Syslog、JSON、CSV。还有少数用 XML、Protobuf 的,但那是大厂的定制玩法,咱们先放一放。下面这张表能帮你快速看清它们的差异:

格式 结构化程度 可读性 机器解析效率 常见场景
Syslog 半结构化 高 中等 网络设备、Linux 系统日志
JSON 全结构化 中等 高 Web 应用、微服务、API 网关
CSV 结构化(平表) 高 高 历史分析、批量导入
我个人的建议: 新项目尽量用 JSON,因为字段可扩展,而且很多安全分析工具(如 Elasticsearch、Splunk)原生支持 JSON 嵌套解析。Syslog 适合网络设备,但字段太死板。

2.2 Syslog——老将依然能打

Syslog 是日志界的老大哥了。RFC 3164 定义了传统格式,后来 RFC 5424 做了结构化扩充。你想想看,一台路由器每天能喷几十万条 syslog,格式不规范的话根本没法处理。

传统 Syslog 长这样:

<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8

前面的数字 <34> 打包了 facility 和 severity。我记得有一次线上事故,防火墙的日志等级被人误配成了 DEBUG,结果一天刷了 200GB 的 syslog,直接把集中存储撑爆了。所以这里的 facility 和 severity 很重要——我建议在接收端就做过滤,不要把垃圾全吃掉。

RFC 5424 引入了结构化数据字段:

<165>1 2003-10-11T22:14:15.003Z mymachine.example.com su - - [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] BOM'su root' failed for lonvick on /dev/pts/8

这个版本多了时间戳(带时区)、hostname、app-name、结构化数据。好处是提取字段方便了,坏处是——很多老旧设备根本不支持,你得到处适配。

注意: 传统 Syslog 不保证顺序,也不保证可靠传输(UDP 默认)。如果日志用于审计或取证,建议配合 RSyslog 的 RELP 协议或者直接走 TCP/TLS。

2.3 JSON——现代日志的默认选择

JSON 日志现在基本是 Web 应用的标配。每个请求一条 JSON,字段包括 timestamp、level、caller、message,还有业务上下文。一个典型的 JSON 日志行:

{
  "timestamp": "2025-04-11T14:23:00.123Z",
  "level": "ERROR",
  "logger": "com.example.payment.Service",
  "thread": "http-nio-8080-exec-5",
  "message": "Payment timeout for order #12345",
  "extra": {
    "orderId": "12345",
    "amount": 299.99,
    "retryCount": 3
  }
}

为什么要强调 JSON?因为处理攻击日志时,字段名就是检测规则的锚点。比如我检测 SQL 注入,直接看 request.body 或者 queryString 字段,正则匹配效率高得多。CSV 想做同样的事,你还得记列号,非常容易搞混。

不过 JSON 也有坑:日志量大的时候,序列化和反序列化开销不小。我经历过一个系统,每条 JSON 日志里都嵌了完整堆栈,结果一天日志 500GB,ES 集群直接写崩了。后来我们规定:错误日志才保留堆栈,INFO 级别只留 message。

避坑指南: 统一日志输出格式,用 Logback/Log4j2 的 JSON 布局,配置里禁止输出换行符。我曾经遇到一个团队,日志里混了多行 JSON,导致每行日志被切割成了几十条,解析直接乱了。

2.4 CSV——轻量但容易篡改

CSV 简单,也真的很脆弱。我见过有人直接用 Excel 打开 CSV 日志,保存后引号、逗号全变了,整个结构就毁了。而且 CSV 没有转义标准,如果日志内容里带逗号或换行,解析器很容易栽。

一个规范 CSV 日志示例:

timestamp,level,source,message
2025-04-11 14:23:00,ERROR,payment,"Payment timeout for order #12345"
2025-04-11 14:25:12,WARN,gateway,"Rate limit exceeded for IP: 10.0.0.1"

CSV 的最大问题是“字段顺序依赖”——如果你加了一个新字段,所有下游解析脚本都要改。而且字段没有自描述性,你拿到一个 CSV 文件,不知道第 3 列是什么。说难听点,攻击者要伪造 CSV 日志成本极低,因为缺乏结构校验。

所以我的建议是:CSV 只用于导出和分析,别当主存储格式。

2.5 日志存储策略

格式定好了,接下来就是怎么存。我见过最糟的案例:某公司把所有日志都打到同一个文件里,不轮转,不压缩,结果一个日志文件 200GB,vim 都打不开。存储策略一般从这几个维度考虑:

  • 存储位置: 本地磁盘、NFS、集中式日志系统(ELK、Graylog、Splunk)、对象存储(S3、OSS)
  • 保留周期: 按合规要求(我是金融行业客户一般保留 180 天~1 年),热数据 7 天、温数据 30 天、冷数据归档
  • 轮转策略: 按大小(如 500MB)或按时间(每天/每小时)切分,配合压缩(gzip)
  • 写一致性: 使用 append-only 模式,避免随机写损坏

下表是我总结的几种存储方式适用场景:

存储方式 延迟 吞吐 防篡改性 推荐场景
本地文件(ext4/XFS) 低 高 低(易被本地修改) 临时调试、边缘节点
集中式日志系统 中 中 高(可审计、只读) 生产环境、安全分析
对象存储(写一次读多次) 高 低~中 极高(不可变对象) 归档、合规保留
小技巧: 对于高吞吐的场景,我习惯在本地先写一个缓冲区(比如 /var/log/buffer/),再异步上传到集中存储。这样既保证写入速度,又避免网络抖动丢日志。

2.6 安全视角下的格式与存储

日志篡改检测的核心之一,就是“能不能发现日志被改了”。如果日志是 CSV 纯文本,攻击者可以直接改文件内容,你很难追踪。如果日志是 JSON 带签名(比如每条日志末尾加 SHA256 值),或者存储在 WORM(Write Once Read Many)存储里,那篡改的成本就高很多。

我个人实践中,推荐 JSON 格式 + 集中存储 + 哈希链(Hash Chain)的方案。每条日志包含前一条的哈希,形成链式验证。这我们在后面第 5 章会详细讲。

一个必须留意的点: 日志文件权限设置。很多开发者图省事,把 /var/log 设成 777,攻击者直接追加恶意日志。我建议生产环境日志目录权限设为 755 或 750,日志文件 640,并配置 logrotate 后自动压缩加密。

下面这张图能帮你把格式和存储策略串起来看:

日志格式与存储策略关系图 应用程序 / 设备 Syslog 半结构化 JSON 全结构化 CSV 平表结构 存储策略 本地文件 集中式日志系统 对象存储 格式选择影响解析效率,存储策略决定安全性与可审计性

你看,从源头到格式再到存储,每个环节都有安全取舍。选 JSON 配合集中式日志系统,是目前最容易实现篡改检测的组合。当然,如果业务系统太老旧不能改格式,也可以通过日志采集层(如 Filebeat、Fluentd)做格式转换——但这又会引入“采集点是否可信”的问题,我们后面章节再聊。

总结一下我个人的习惯:
  • 新系统一律 JSON 格式,字段用 snake_case 统一命名
  • Syslog 只保留网络设备原始日志,接收后立即转成 JSON
  • CSV 只用于导出报表,不做实时分析
  • 存储策略按 7-30-180 天分层,对象存储做写后不可变
  • 日志目录权限收紧,配置 logrotate 后置压缩加密