审计日志核心三要素:谁、何时、做了什么?

审计日志到底要记什么?说白了,三个要素缺一不可:谁(主体)、何时(时间)、做了什么(操作)。我做过好几个 KMS 的合规项目,发现很多团队一开始只记了“操作成功”或“失败”,结果取证时根本对不上号。嗯,这期我们就聊聊这三个要素该怎么落地。

1. 谁(Who)—— 主体身份

“谁”不只是一个用户名。真正的生产环境里,操作来源可能五花八门:

  • IAM 用户(AK/SK 或密码登录)
  • 服务角色(EC2 实例扮演的 Role)
  • 应用程序(通过 SDK 调用的 Client ID)
  • 匿名访问(某些公开接口,但要慎用)

我个人习惯在审计日志里记录三个维度:user_id、user_type、source_ip。为什么不只记一个 ID?因为我遇到过这样的情况:同一个用户换了 IP,事后排查发现是 被盗用。你想想看,如果只记了用户名,根本看不出“谁从哪来”。

加分项: 如果 KMS 支持,把 session_id 或 request_id 也带上,能串联出完整的操作链路。

2. 何时(When)—— 时间戳

时间戳看着简单,坑却不少。我建议至少记录两个时间:

  • 服务端接收时间(KMS 收到请求的时刻)
  • 操作完成时间(实际执行完的时刻)

为什么?我曾经遇到一个场景:客户端和服务器时钟差了 20 秒,用户坚持说自己在 10:00:00 发起了密钥轮换,但日志显示 10:00:20 才收到请求。扯皮了很久。最后我们强制所有审计日志使用 UTC 时间,并且在字段里标明时区偏移。

小心: 很多 KMS 默认用 Unix 时间戳(毫秒/微秒),但你做离线分析时最好提前转成易读格式。别问我是怎么知道的——有一次我把微秒时间戳当秒来解析,整整查错了一下午。

3. 做了什么(What)—— 操作详情

“做了什么”不止是操作名称。一串完整的操作记录应该包括:

  1. 操作类型(Encrypt、Decrypt、GenerateDataKey……)
  2. 资源标识(密钥 ID、别名、CMK ARN)
  3. 请求参数(加密上下文、密钥规格等关键入参)
  4. 结果状态(成功 / 失败 / 部分成功,以及错误码)
  5. 返回对象摘要(比如仅记录密文的哈希,不记录明文)

这里要特别提醒:千万不要把明文密钥或数据记录进审计日志。我在 code review 时见过一个 SDK 踩了雷——开发图方便,直接把 Encrypt 明文给打进了日志。幸好是测试环境,否则就是严重安全事故。

典型审计日志结构

下面是我在项目里常用的 JSON 结构(基于 AWS CloudTrail 格式改编):

{
  "version": "1.0",
  "eventTime": "2025-03-15T08:12:00Z",
  "userIdentity": {
    "type": "IAMUser",
    "principalId": "AIDA123456789",
    "arn": "arn:aws:iam::123456789012:user/admin",
    "accountId": "123456789012",
    "accessKeyId": "AKIAIOSFODNN7EXAMPLE"
  },
  "eventSource": "kms.amazonaws.com",
  "eventName": "Encrypt",
  "awsRegion": "us-east-1",
  "sourceIPAddress": "203.0.113.10",
  "userAgent": "aws-cli/2.0",
  "requestParameters": {
    "keyId": "arn:aws:kms:us-east-1:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab",
    "encryptionContext": {"department": "finance"}
  },
  "responseElements": {
    "keyId": "arn:aws:kms:us-east-1:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab",
    "ciphertextBlob": "AQICAHh7...(仅展示哈希,非完整明文)"
  },
  "eventType": "AwsApiCall",
  "recipientAccountId": "123456789012"
}

这个结构把“谁、何时、做了什么”都囊括了。注意我特意省略了 ciphertextBlob 的完整值,只放个哈希标识。这是安全红线。

三要素对比表

要素 关键字段示例 常见错误 最佳实践
Who userIdentity.type, sourceIPAddress 只记用户名,忽略角色/来源 IP 记录 IAM 类型 + 身份 ARN + IP
When eventTime (ISO 8601 UTC), requestDuration 使用本地时间,无时区 强制 UTC,记录精度到毫秒
What eventName, requestParameters, responseElements 记录明文密钥或敏感数据 只记录元数据,永远不要记录明文

三要素的联动关系

上面三个要素不是孤立的。举个例子:一个解密操作(What)发生在午夜(When),发起方却是一个本该禁用 IAM 用户(Who)——这就是典型的安全告警信号。我建议在审计日志分析时,把这三点做成一个“三元组”,方便做关联分析。

审计日志核心三要素关系图 审计日志核心三要素联动模型 Who(主体) 用户 / 角色 / IP When(时间) UTC 时间戳 + 时区 What(操作) 动作 + 资源 + 结果 三元组联合分析示例 Who: 禁用用户 → When: 凌晨 3 点 → What: 批量解密密钥 → 告警!

这个图展示了三要素如何串联成一个完整的审计事件。实际做日志聚合时,我通常会用 userIdentity.arn + eventTime + eventName 做 联合主键,方便去重和排序。

常见的审计日志“四不像”

下面是我遇到过的两个典型反面教材,你对比着看:

  1. 只记功能,不记身份。 有次客户说“我们 KMS 审计日志相当详细”,结果一看,全部是 {"action":"encrypt","time":"...","status":"ok"} —— 没 IP、没用户、没资源 ID。等于白记。
  2. 时间戳格式混乱。 同一个日志文件里,既有 "2025-03-15T08:00:00Z",又有 "03/15/2025 08:00:00 UTC+8"。解析脚本天天报错。
我的经验公式: 审计日志记录的详尽程度,直接决定了后续安全事件的响应速度。如果一个季度内的日志能支撑你快速定位“是谁在什么时候对哪个密钥做了什么”,那这日志就是合格的。

选型时需要关注的要素

当你在对比不同 KMS(AWS KMS、Azure Key Vault、HashiCorp Vault、自建等)时,可以按以下清单逐项检查:

避坑指南: 我曾经因为相信“供应商文档说完全支持审计”,结果发现在批量操作场景下,某个云 KMS 的审计日志只记录第一个操作,后面的链路全部丢失。所以一定要用你真实的生产场景做冒烟测试。
  • 是否支持记录 完整的身份信息(包括 IAM 角色、服务账户)?
  • 时间戳的精度和格式是否可以自定义?
  • 操作详情中是否包含 请求参数(密钥 ID、加密上下文等)?
  • 失败操作是否有独立的错误码和失败原因?
  • 是否支持审计日志的 不可篡改(如写入 S3 Object Lock 或日志存储加密)?

嗯,这部分内容比较多,但理解了三要素之后,后面的章节我们看各家产品对比时会轻松很多。记住:谁、何时、做了什么——这是审计日志的“黄金三角”,缺哪个角,你就等着事后头痛吧。