后台审计日志:谁在什么时候改了什么
本文目录
应用日志帮助排障,审计轨迹帮助回答“谁在什么时候对哪个对象做了什么,结果如何”。两者目的、保留期、读取权限和检索方式不同。审计日志不是把整个请求 body 原样存一份:那样会把密码、隐私和上传内容长期复制到更多系统。
定义稳定事件契约
建议字段包括 eventId、occurredAt、actorId/role、tenantId、action、resourceType/resourceId、结果、拒绝/失败分类、requestId/traceId、受影响字段名、脱敏后的变化摘要、来源服务与 schemaVersion。时间统一 UTC;actor ID 使用稳定内部标识,不放展示名或邮箱等多余个人信息。
变化记录优先写字段名和安全摘要,例如 visibility: PUBLIC -> PRIVATE。对密码、token、密钥、认证头、书籍正文、个人留言和支付资料绝不记录完整值。字符串要限制长度并清洗 CR/LF,避免日志注入。客户端提交的 actor、时间和权限信息不可信,只从服务端认证上下文读取。
{
"eventId": "evt_01J...",
"occurredAt": "2026-10-10T02:00:00.000Z",
"actorId": "user_123",
"actorRole": "EDITOR",
"action": "document.publish",
"resourceType": "document",
"resourceId": "doc_456",
"result": "success",
"requestId": "req_789",
"changedFields": ["isPublished", "publishedAt"],
"redaction": "values-omitted",
"schemaVersion": 1
}
不要把真实账号信息直接复制本文示例;线上记录按最小必要原则收集并做访问控制。
与业务变更保持一致
如果审计事件是合规或安全所需,不应出现“业务提交成功但审计记录丢失”的静默窗口。对同一数据库内的变更和审计记录,可在同一事务写入;若事件要发布到独立日志/消息系统,采用 outbox:事务内保存事件,再由后台 publisher 重试投递。消费者按 eventId 幂等去重。
审计写入本身也要有故障策略。关键敏感操作可以在审计不可用时拒绝变更或进入待处理状态;普通偏好设置可能允许业务继续但触发高优先级告警。不要默默 catch 后忽略。策略需要按操作风险明确,并测试数据库/队列不可用时的行为。
如果业务变更和审计行在同一数据库,可用一个事务保证二者一起提交;若事件要发往远端日志平台,则不要先提交业务再同步发送消息。transactional outbox 把业务数据和待发事件写在同一事务,由 publisher 使用租约读取并重复投递;消费者按稳定 eventId 幂等。投递失败必须可观测,不能靠请求线程无限等待日志平台恢复。
CREATE TABLE audit_events (
event_id uuid PRIMARY KEY,
occurred_at timestamptz NOT NULL DEFAULT now(),
actor_id text NOT NULL,
action text NOT NULL,
resource_type text NOT NULL,
resource_id text NOT NULL,
outcome text NOT NULL CHECK (outcome IN ('success', 'denied', 'failed')),
request_id text,
changed_fields jsonb NOT NULL DEFAULT '[]'::jsonb,
schema_version integer NOT NULL DEFAULT 1
);
CREATE INDEX audit_events_resource_time_idx
ON audit_events (resource_type, resource_id, occurred_at DESC);
这是最小模型而非通用 schema。changed_fields 只应包含允许的字段名/脱敏摘要,不接收任意请求 JSON;actor/资源引用的删除策略要明确,避免外键级联抹掉审计证据。生产还需确认索引写入成本、分区/归档策略、时钟来源和查询授权。
完整性、查询与保留
应用数据库里的审计表并非天然不可篡改:有高权限的管理员仍可能更新或删除记录。限制应用账号只追加、不允许 UPDATE/DELETE;另行限制读权限,按需要将副本导出到访问受控、具备保留锁或不可变策略的存储。哈希链可帮助发现局部改动,但若密钥和整条链都由同一管理员控制,不能单独证明不可抵赖。
按 tenant/time、resource/time、actor/time 建查询索引,支持查“某资源历史”和“某账号近期敏感操作”。保留和删除期限需结合合规、隐私和业务用途;过期归档应可核验,超过期限的个人数据要按规则删除或匿名化。
“只追加”要落实到数据库身份,而不是只在 ORM 里不提供删除方法:让迁移/表 owner 与应用 writer 使用不同角色,writer 仅获 INSERT,只读审计角色获受限 SELECT。对象所有者和超级用户仍可改变权限,因此这只是降低日常应用凭据的破坏能力,不是对数据库管理员的绝对防篡改保证。把副本送到另一个管理域、带保留锁的存储,才能降低单一凭据同时改业务与审计的风险。
保留周期应按用途、隐私最小化和适用规则设定。需要长期趋势时优先聚合/去标识化;到期数据按可审计流程删除。备份也会复制审计记录,删除策略必须覆盖备份保留期、导出副本和灾备恢复后的过期清理,不能只删在线表。
审计访问自身也要审计
只有被授权的管理员/安全人员能搜索或导出日志。限制查询时间跨度、结果条数、导出大小和频率,避免审计 API 成为批量数据泄漏口。记录谁查了谁的日志、导出范围、结果数和审批上下文,但不要把完整导出内容复制到另一条日志里。
数据库角色设计可参考 PostgreSQL GRANT,但表 owner 与超级用户仍可改变权限;“只追加”只限制日常应用身份,不是绝对防篡改。
数据库角色设计可参考 PostgreSQL GRANT,但表 owner 与超级用户仍可改变权限;“只追加”只限制日常应用身份,不是绝对防篡改。
测试矩阵
- 重要变更、拒绝、角色调整、发布/下架、文件授权和数据删除均记录正确事件。
- 事件 actor、tenant、资源 ID 从服务端上下文取值;伪造字段无法冒充他人。
- 业务事务回滚时不留下“成功”审计;成功提交后的事件可以可靠发布。
- 重复 outbox 投递不重复形成不同业务审计记录;事件 schema 升级能兼容旧记录。
- token/密码/正文/个人信息在成功、失败和异常日志中均不出现。
- 低权限用户不能读取或导出审计;审计查询本身有记录、分页和速率限制。
参考:OWASP Logging Cheat Sheet 对事件、字段和敏感数据排除提出基线。具体保留期限要结合业务用途和适用规则确定,不能只按磁盘容量决定。
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。