完整性保护的基本概念与原理

聊审计日志完整性,得先搞清楚「完整性」到底指什么。

我入行头两年,总把完整性和机密性混为一谈。后来在金融项目里吃过亏才明白——完整性保证的是数据没被改过,机密性保证的是数据没被别人看到。你想想看,日志被人偷看了,顶多算泄密;但如果日志被人改了,那整个审计链条就断了,谁也说不清发生了什么。

2.1 完整性的核心定义

说白了,完整性就是三个字——没动过。具体到审计日志:

  • 内容没被篡改(新增、删除、修改都算)
  • 顺序没被打乱(时间戳和序号必须匹配)
  • 来源可追溯(谁写的、什么时候写的、用哪个设备写的)

嗯,这里要注意:日志完整性不是二进制层面的一模一样。比如日志轮转、格式转换这些合法操作,只要保留了原始记录的摘要,完整性就没丢。

我在项目中遇到过一个问题:客户要求保留日志90天,但存储不够。运维就偷偷把旧日志压缩了。压缩本身没改内容,但解压后时间戳乱掉了。后来我加入了一条规则:任何存储格式转换都必须保留原始哈希摘要。

2.2 审计日志完整性为什么重要?

原因其实就一个——你没法信任一个可能被改过的证据。

我记得有一次做安全事件复盘,攻击者删掉了自己的登录记录。如果没有完整性保护,那日志就成了废纸。合规审计里常说「非否认性」,其实就是建立在完整性之上的。

下面这张表可以快速对比:

属性 含义 日志场景举例
完整性 数据未被未授权修改 日志条目哈希值匹配
机密性 数据未泄露给未授权方 日志传输或存储加密
可用性 数据在需要时可访问 日志备份、容灾恢复
真实性 数据来源可验证 数字签名验证日志来源

2.3 核心保护原理

完整性保护不是一种技术,而是一套组合拳。我建议你从三个层面理解:

2.3.1 检测层:哈希与数字签名

每次写入日志时,计算整条记录的哈希值,再用私钥签名。验证时你拿公钥验签,哈希一比对就知道有没有被改过。

避坑指南:我曾经在项目中直接用MD5做完整性校验,后来发现MD5冲突攻击已经足够威胁日志安全。现在强制用SHA-256及以上。

2.3.2 防删除层:链式结构与时间戳

单条日志的哈希好做,但攻击者把整条日志删掉怎么办?链式结构就解决了这个问题——前一条日志的哈希会混入后一条日志的计算。你删其中一条,整个链条对不上,立马暴露。

// 伪代码示例:日志链哈希计算
prevHash := getPreviousLogHash()
currentLog := {timestamp, event, user, prevHash, nonce}
currentHash := SHA256(currentLog)
// 存储 currentLog + currentHash

2.3.3 不可抵赖层:可信时间戳与持久化

光有链还不够,时间戳必须是可信的(比如NTP同步或硬件安全模块)。另外,日志要持久化到不可更改的介质,比如WORM存储(一次写入多次读取)。

2.4 威胁模型

知道原理后,咱们得想想攻击者会从哪下手。我总结了几种常见场景:

  1. 篡改现有记录 —— 直接改内容、改时间
  2. 删除整条记录 —— 尤其是不法操作前后的日志
  3. 插入虚假记录 —— 比如伪造一条“系统故障”掩盖入侵
  4. 重放旧记录 —— 用过去合法的日志覆盖现在的
  5. 破坏链式完整性 —— 找到链式结构的弱点,比如哈希碰撞

重要提醒:攻击者不一定直接碰日志文件。他们可能先拿到日志服务器的root权限,然后修改完整性校验程序本身。所以保护日志完整性的同时,也要保护保护日志的系统。

2.5 常见保护机制一览

下面这张图展示了审计日志完整性保护的核心流程,也是我个人习惯用的设计框架:

审计日志完整性保护核心流程 日志源 哈希计算 数字签名 链式存储 完整性验证 常见威胁 • 篡改日志内容 • 删除整条记录 • 插入虚假记录 • 重放旧记录 保护原则 • 检测:哈希+签名 • 防删除:链式结构 • 不可抵赖:可信时间戳 原则与威胁对应,才能设计出完整的保护方案

我自己的习惯是,先画清楚上面这个流程,再考虑每个环节的技术选型。比如哈希可以用SHA-256,签名可以用RSA-2048,存储层可以考虑用区块链的思路(但别搞复杂了)。

特别说一下:签名和链式存储不是互斥的。我建议两个都用——签名保证单条记录没被改,链式保证删除或重排序立刻暴露。你问我为什么?有一次项目里只用链式没做签名,结果攻击者通过日志服务器漏洞替换了整个日志文件。链还在,但根哈希被改了,我根本发现不了。后来我加了一层签名,才彻底堵住这个坑。

2.6 你思考一下

为什么很多审计框架(比如PCI-DSS、SOX)都要求「日志不可更改」?其实就是因为我们上面讲的这些东西——没有完整性保护,审计就是自欺欺人。