4. 审计日志格式与字段详解

拿到一条 KMS 密钥审计日志,你到底该看什么?说实话,大部分人会先被那一长串 JSON 吓住。但其实它的结构很固定。字段翻来覆去就那十几个。你只要把常见字段摸熟,后面解析告警、回溯事件就轻松多了。

我记得刚接手一个合规项目时,客户丢了一堆 CloudTrail 日志给我,说“查一下谁在哪些时间点调用了 KMS 解密”。当时我盯着日志里那个 userIdentity 字段看了半天——里面嵌套了好几层,最后才找到真正的 arn。嗯,这玩意儿你要是没提前理清结构,排查时很容易绕晕。

4.1 常见字段一览

下面这张表,是我平时排查问题最常看的字段。你最好直接记住它。

字段名含义示例值
eventTime事件发生时间,精度到毫秒,UTC2025-04-10T08:23:17Z
userIdentity发起请求的身份信息(IAM 用户、角色、联邦用户等){"type":"IAMUser","arn":"arn:aws:iam::123456789012:user/Alice"}
eventName操作类型,比如 Decrypt、GenerateDataKey、ScheduleKeyDeletionDecrypt
resources被操作的资源 ARN 列表,通常只有一个 KMS 密钥[{"ARN":"arn:aws:kms:us-east-1:123456789012:key/abc123-..."}]
sourceIPAddress调用来源 IP203.0.113.5
userAgent客户端工具或 SDK 版本aws-sdk-java/2.20.0
errorCode如果操作失败,这里会写明错误类型AccessDenied
requestParameters具体请求参数,例如加密上下文、密钥 ID 等{"keyId":"abc123-...","encryptionContext":{"env":"prod"}}
responseElements返回结果,成功时包含密文或明文密钥信息(部分脱敏){"plaintext":"AQIDAHj...(截断)"}
我的小习惯: 每次排查可疑调用,我会先锁定 eventTime、sourceIPAddress 和 userIdentity.arn 三个字段。这三样能直接定位——“谁、在什么时候、从哪来”。剩下就是看操作类型了。

4.2 JSON 格式解析

CloudTrail 的日志是标准 JSON。每条记录一个 Records 数组。你打开一条长这样:

{
  "eventVersion": "1.08",
  "userIdentity": {
    "type": "AssumedRole",
    "principalId": "AROAEXAMPLE:my-role-session",
    "arn": "arn:aws:sts::123456789012:assumed-role/MyRole/my-role-session",
    "accountId": "123456789012"
  },
  "eventTime": "2025-04-10T08:23:17Z",
  "eventSource": "kms.amazonaws.com",
  "eventName": "Decrypt",
  "awsRegion": "us-east-1",
  "sourceIPAddress": "203.0.113.5",
  "userAgent": "aws-cli/2.13.0",
  "requestParameters": {
    "keyId": "arn:aws:kms:us-east-1:123456789012:key/abc123-...",
    "encryptionContext": {},
    "encryptionAlgorithm": "SYMMETRIC_DEFAULT"
  },
  "responseElements": {
    "keyId": "arn:aws:kms:us-east-1:123456789012:key/abc123-...",
    "plaintext": "AQIDAHj...(日志中会截断)"
  },
  "resources": [{
    "ARN": "arn:aws:kms:us-east-1:123456789012:key/abc123-...",
    "accountId": "123456789012"
  }],
  "errorCode": null,
  "errorMessage": null
}

注意几个容易看走眼的地方。

  • userIdentity 的 arn 不一定是 IAM 用户的 arn。如果是角色,它是 sts::...assumed-role/...;如果是联邦用户,它可能是 sts::...federated-user/...。我在排查时吃过亏——只看了 type 是 IAMUser 就以为没问题,结果那是一条跨账号角色调用,身份字段完全不一样。
  • plaintext 字段 在日志里默认截断。别指望能从日志直接拿到完整明文密钥,那是安全管控。你只能看到前 20 个左右的 base64 字符。
  • errorCode 为 null 表示成功。如果失败,要同时看 errorMessage,比如 InvalidKeyId 或 IncorrectKeyMaterialState。
我曾经遇到过: 有次分析一条 Decrypt 失败日志,一直以为是权限问题。后来仔细看了 requestParameters.encryptionContext,发现调用方传的 context 和加密时用的 context 对不上。这种错在自助排查时特别容易忽略。

4.3 实战:用 Python 快速解析字段

你如果在做自动化日志分析,下面这段代码可以直接拿来用:

import json

def parse_kms_event(log_record):
    event = json.loads(log_record)
    identity = event.get("userIdentity", {})
    return {
        "time": event["eventTime"],
        "user_arn": identity.get("arn"),
        "user_type": identity.get("type"),
        "action": event["eventName"],
        "key_arn": event["resources"][0]["ARN"] if event.get("resources") else None,
        "source_ip": event.get("sourceIPAddress"),
        "error": event.get("errorCode"),
        "region": event.get("awsRegion")
    }

这个函数把最常用的字段提出来了。你把它塞到日志处理管道里,每天凌晨跑一次,把可疑操作标记出来。我个人习惯还会加一个时间窗口判断——比如在非工作时间大量 Decrypt 调用,大概率有问题。

4.4 知识结构图:KMS 审计日志字段分类

下面这张图帮你按逻辑分组记忆这些字段。一眼就能看出哪些属于“谁发的”、哪些是“做了什么”、哪些是“环境信息”。

KMS 审计日志字段分类 身份信息 • eventTime • userIdentity.type • userIdentity.arn • userIdentity.accountId • userAgent 操作信息 • eventName • eventSource • requestParameters • responseElements • errorCode / errorMessage 资源信息 • resources[0].ARN • resources[0].accountId • awsRegion 网络信息 • sourceIPAddress • vpcEndpointId (部分日志) 操作关联 💡 字段分组后,排查思路就清晰了:先锁定身份信息确定“谁”,再看操作信息确定“做什么”, 然后结合网络信息定位来源,最后用资源信息确认影响的密钥。
核心记忆法: 四组字段——身份、操作、资源、网络。以后看到日志,先在心里套这个框架。不出半年,你闭着眼都能写出解析逻辑。

4.5 解析时容易踩的坑

  • resources 可能为空。 有些管理操作(如 ListKeys)不涉及具体密钥 ARN,你要加判空逻辑。
  • timestamp 格式是 UTC。 如果你在本地时区查,记得做转换。我见过有人把 UTC 8 点当成本地 8 点,错失了攻击时间线索。
  • 加密上下文在日志里可能被省略。 只有 encryptionContext 非空时才出现在 requestParameters 中。别依赖它一定存在。
  • role 的 session name 藏在 principalId 里。 比如 AROAEXAMPLE:my-role-session,冒号后面那个才是 session 名。需要用 principalId.split(":")[1] 提取。
写个检查清单: 每次解析一条 KMS 日志,我至少确认:1) 时间戳是 UTC?2) ARN 是否完整?3) errorCode 有没有非 null?4) sourceIPAddress 是不是 VPC 内网 IP?这几步走完,基本的隐患就排除了。