AgentDepthAgent 工程学习指南
输入概念或问题,检索正文和待编写目录。
第一次学习?从 L0 总览开始 →

审计、数据保留与变更治理

我的学习 →

正在读取学习记录…

安全团队需要回答:某次工具请求是谁发起的、按哪一版策略允许或拒绝、记录还保留多久。正常记录可以写为“第9天,u2,允许,策略1”。排查时先找请求与决策,避免把整段用户消息、邮箱和密钥复制进日志。

审计是保留能够追踪行为与决策的记录。数据保留是为这些记录设定生命周期:什么时候继续存、什么时候到期清理。变更治理是让策略修改经过复核,并留下版本与责任人,让历史决定可以对照当时的规则解释。

示例用整数“天”替代真实时间,第10天只保留最近七天的事件。真实服务应选定时间基准、时区和保留起点,考虑调查冻结及合法留存要求。

记录内容、链和保留窗口怎样相连

每条事件从白名单参数构建:时间、主体、决定、策略版本。摘要是由内容算出的固定长度指纹;prev 保存前一条指纹,把相邻记录连接起来。verify 同时重新计算内容摘要和核对前后连接,能发现当前列表里的内容改动和乱序。

摘要链要配合受控存储与外部可信检查点。拥有整份日志写权限的人可以重算所有摘要;删除尾部也不会被当前列表自检发现。生产场景可采用独立写入身份、访问控制、外部签名检查点或不可变存储,以保存完整性的参照。

retain 用年龄筛选窗口,年龄等于保留天数时到期,未来日期也不会进入当前窗口。它重建一个新链,便于独立检查保留片段;旧链的摘要连续性在窗口切换时结束。实际系统要记录清理批次、窗口边界与旧检查点,才能解释为什么旧事件消失。

要回答的问题使用字段或规则例子
谁做了什么决定actor、decisionu2 / allow
当时用哪版策略policy_version1
当前内容有没有变化digest、prev改决定后校验 False
是否仍在保留窗口0 ≤ 年龄 < days第9天在第10天仍保留
谁能改保留规则另一复核人、版本递增版本1变版本2
审计、数据保留与变更治理中输入、检查和结果分支的关系
图1:审计事件按前一条摘要连接;内容校验后筛选保留窗口,策略变更再核对复核人与版本。

在空目录保存并运行两个文件

在 PowerShell 输入以下命令创建练习目录,并进入目录。目录创建好后,在编辑器中新建下面两份文件并保存到这个目录。

PowerShell
New-Item -ItemType Directory -Force l8-audit
Set-Location l8-audit

macOS 终端:

Bash
mkdir -p l8-audit
cd l8-audit

先保存 audit_demo.py。它包含前面解释的内容校验以及正常和边界输入。

Python
"""审计事件使用字段白名单、策略版本与保留规则。"""
import hashlib
import json
from dataclasses import dataclass

RETENTION = 7

@dataclass(frozen=True)
class Policy:
    version: int
    days: int

def policy_change(old, days, actor, reviewer):
    if days <= 0 or actor == reviewer:
        raise ValueError("需正数保留期和另一复核人")
    return Policy(old.version + 1, days)

def add_event(events, day, actor, decision, policy):
    # 从显式参数构造记录,避免把原始请求或秘密整包写入日志。
    event = {"day": day, "actor": actor, "decision": decision,
             "policy_version": policy.version,
             "prev": events[-1]["digest"] if events else "START"}
    payload = json.dumps(event, sort_keys=True, ensure_ascii=False).encode()
    event["digest"] = hashlib.sha256(payload).hexdigest()
    events.append(event)

def verify(events):
    previous = "START"
    for event in events:
        payload = {key: value for key, value in event.items() if key != "digest"}
        digest = hashlib.sha256(json.dumps(
            payload, sort_keys=True, ensure_ascii=False).encode()).hexdigest()
        if event["prev"] != previous or event["digest"] != digest:
            return False
        previous = event["digest"]
    return True

def retain(events, today, policy):
    # 年龄等于保留期时到期;新窗口重建链,原摘要无法继续串联。
    kept = [event for event in events if 0 <= today - event["day"] < policy.days]
    rebuilt = []
    for event in kept:
        add_event(rebuilt, event["day"], event["actor"], event["decision"],
                  Policy(event["policy_version"], policy.days))
    return rebuilt

def main():
    policy = Policy(1, RETENTION)
    events = []
    add_event(events, 1, "u1", "deny", policy)
    add_event(events, 9, "u2", "allow", policy)
    print("完整链 ->", verify(events))
    events[1]["decision"] = "deny"
    print("改动记录 ->", verify(events))
    events[1]["decision"] = "allow"
    kept = retain(events, 10, policy)
    print("保留窗口 ->", [event["day"] for event in kept])
    updated = policy_change(policy, 3, "editor", "reviewer")
    print("策略变更 ->", updated.version, updated.days)

if __name__ == "__main__":
    main()

再保存 test_audit.py。测试通过程序的公开函数检查正常路径与拒绝路径;测试文件和主程序放在同一目录,才能按文件名导入。

Python
import unittest
# 固定良性输入检查正常、拒绝和边界路径,不连接外部服务。
from audit_demo import Policy, add_event, policy_change, retain, verify

class AuditTests(unittest.TestCase):
    def setUp(self):
        self.policy = Policy(1, 7)
        self.events = []
        add_event(self.events, 1, "u1", "deny", self.policy)
        add_event(self.events, 9, "u2", "allow", self.policy)

    def test_chain(self):
        self.assertTrue(verify(self.events))

    def test_mutation(self):
        self.events[1]["decision"] = "deny"
        self.assertFalse(verify(self.events))

    def test_reorder(self):
        self.assertFalse(verify(list(reversed(self.events))))

    def test_retention_boundary(self):
        add_event(self.events, 7, "u3", "allow", self.policy)
        kept = retain(self.events, 8, self.policy)
        self.assertEqual([event["day"] for event in kept], [7])
        self.assertTrue(verify(kept))

    def test_minimal_fields(self):
        self.assertEqual(set(self.events[0]),
                         {"day", "actor", "decision", "policy_version", "prev", "digest"})

    def test_policy_review(self):
        self.assertEqual(policy_change(self.policy, 3, "a", "b"), Policy(2, 3))
        for days, reviewer in ((0, "b"), (3, "a")):
            with self.assertRaises(ValueError):
                policy_change(self.policy, days, "a", reviewer)

if __name__ == "__main__":
    unittest.main()

返回同一个终端,先列出当前目录内容,确认两个文件名都以 .py 结尾,然后运行主程序。

Bash
ls
python -X utf8 audit_demo.py

程序输出:

TXT
完整链 -> True
改动记录 -> False
保留窗口 -> [9]
策略变更 -> 2 3

完整链校验为 True;修改第二条决定后变为 False。恢复字段后,七天保留窗口留下第9天事件。把保留天数变更为3,策略版本递增为2。示例在内存中筛选与返回结果,不会删除磁盘上的日志文件;生产清理还需要覆盖副本、备份和检索索引,并遵循相应保留制度。

运行测试:

Bash
python -X utf8 -m unittest -v test_audit.py

末尾应显示 Ran 6 tests 和 OK。若提示找不到模块,先用 dir 核对目录与文件名。

改一处,观察决定怎样变化

把 RETENTION = 7 改成 RETENTION = 10,重新运行主程序。第三行由 保留窗口 -> [9] 变成 保留窗口 -> [1, 9]:第1天到第10天的年龄为9,小于新的10天窗口。恢复7。

Bash
python -X utf8 audit_demo.py

恢复原值后,重新运行测试。

日志越来越多时先收敛字段与生命周期

现象检查位置修正动作
日志含秘密或全文请求记录函数入参与字段白名单构建,脱敏后再写入
摘要一致却少了事件外部检查点或序列范围对照预期最后序号/检查点,不能仅自检
保留窗口删掉调查资料冻结或例外留存状态在清理前检查冻结规则与授权
历史决定无法解释事件策略版本与历史配置保存可检索的配置版本和变更记录
新日志失败但工具继续成功写入可靠性与故障流程根据动作风险决定阻断、重试或受控降级

代码的 policy_change 只验证正数和不同复核人,生产变更服务要从已认证身份取得两人的角色,并记录变更原因、生效范围和回滚版本。别把名字不同当成真实职责分离。保留期也要先验证再部署,避免一次错误配置清理掉必要记录。

OWASP 日志指南讨论记录内容、敏感数据排除、访问控制与日志生命周期。日志完整性和保留治理需要存储系统、权限及运营流程共同实现。

课后练习展开习题收起习题

4 道练习 · 客观题规则评分 · 其余题目待批改

正在读取本地记录…

不调用模型,不执行代码。简答、代码和场景题仅保存答案并展示自查标准,不生成评分、徽章或认证。输入内容不会自动保存,请点击提交;不要填写个人信息或密钥。

练习 1 / 4 · v1.0.1

选择题 · 先判断边界

关于“审计、数据保留与变更治理”,哪种做法更稳妥?

对应知识点:L8-audit

选择一个答案
练习 2 / 4 · v1.0.0

简答题 · 解释失败

写出一个失败输入并说明记录字段。

对应知识点:L8-audit

练习 3 / 4 · v1.0.0

代码题 · 边界测试

补充正常、边界、失败三组输入和预期结果。

对应知识点:L8-audit

练习 4 / 4 · v1.0.0

场景题 · 结果未知

证据不足时如何设计状态和人工接管?

对应知识点:L8-audit

图片查看器
100%

文章配图 · 按 Esc 关闭