4. 审计日志格式与字段详解
拿到一条 KMS 密钥审计日志,你到底该看什么?说实话,大部分人会先被那一长串 JSON 吓住。但其实它的结构很固定。字段翻来覆去就那十几个。你只要把常见字段摸熟,后面解析告警、回溯事件就轻松多了。
我记得刚接手一个合规项目时,客户丢了一堆 CloudTrail 日志给我,说“查一下谁在哪些时间点调用了 KMS 解密”。当时我盯着日志里那个 userIdentity 字段看了半天——里面嵌套了好几层,最后才找到真正的 arn。嗯,这玩意儿你要是没提前理清结构,排查时很容易绕晕。
4.1 常见字段一览
下面这张表,是我平时排查问题最常看的字段。你最好直接记住它。
| 字段名 | 含义 | 示例值 |
|---|---|---|
eventTime | 事件发生时间,精度到毫秒,UTC | 2025-04-10T08:23:17Z |
userIdentity | 发起请求的身份信息(IAM 用户、角色、联邦用户等) | {"type":"IAMUser","arn":"arn:aws:iam::123456789012:user/Alice"} |
eventName | 操作类型,比如 Decrypt、GenerateDataKey、ScheduleKeyDeletion | Decrypt |
resources | 被操作的资源 ARN 列表,通常只有一个 KMS 密钥 | [{"ARN":"arn:aws:kms:us-east-1:123456789012:key/abc123-..."}] |
sourceIPAddress | 调用来源 IP | 203.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 审计日志字段分类
下面这张图帮你按逻辑分组记忆这些字段。一眼就能看出哪些属于“谁发的”、哪些是“做了什么”、哪些是“环境信息”。
核心记忆法: 四组字段——身份、操作、资源、网络。以后看到日志,先在心里套这个框架。不出半年,你闭着眼都能写出解析逻辑。
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?这几步走完,基本的隐患就排除了。