摘要

  • RFC 5293 规定,多数测试和存储、发送、修改动作读取当时的当前头部;MDN、DSN 等处置通知以及错误触发的隐式保留必须读取未修改的原始头部。
  • 编辑前后在去重意义上仍是同一封邮件,因此“脚本成功”不能证明哪一版被保存、签名验证、通知引用或最终送达。

绿灯掩盖了视图切换

某企业网关先删除外部来信自带的 X-Approved,再加入内部审核标签,随后归档并转发。控制台显示脚本成功。争议发生后,邮箱副本、DSN 和 DKIM 结果却互不相同。

这不是协议自相矛盾。addheader 与 deleteheader 操作的是一个按顺序变化的当前状态。默认添加在头部开端,:last 可放到末端;删除既可覆盖全部同名字段,也可用 :index 选择某次出现。计数先发生,值匹配后发生。前一次删除会改变后一次命令所面对的序号。

因此,最终文件相同,也不代表中间没有出现过足以影响测试的字段。只有动作序列能说明当时发生了什么。

原始与当前都由规范授权

exists、header 等测试,以及查看头部的 vacation,会看到此前编辑后的状态。保存、发送或继续修改邮件的普通动作也使用当前头部。RFC 6609 进一步要求,这些变化在包含脚本与调用脚本之间双向传递。

但 MDN、DSN 或类似通知必须使用原始未修改头部。若错误终止脚本,Sieve 所要求的隐式保留同样回到原始头部。正常隐式保留则被理解为在编辑之后执行,前提是它没有被取消。

所以,仅记录“执行了 keep”并不够;还必须记录它为何发生。

消息身份不等于字节一致

RFC 5293 要求去重时把修改后的邮件视为原邮件。两个冗余的保留动作不能在同一邮箱产生两份副本,但实现可以选择执行其中哪一个。

这恰好把三个层次分开:处理的是哪个消息身份、某个动作读取哪套头部、外部最终观察到什么结果。去重相等不是字节相等,字节变化也不必然产生新消息身份。

被静默拒绝的动作也要留下证据

本地策略可以限制可编辑字段,违规尝试会被静默忽略。实现绝不能允许删除 Received 或 Auto-Submitted,却必须允许修改 Subject。保护 Received,是为了保留 SMTP 防环路所依赖的轨迹。

因此“无异常结束”并不等于每条编辑都生效。审计应同时记录请求、策略判断以及前后哈希。

加密证明也有层次

头部变化可能破坏 DKIM,包括原本证明某字段不存在的签名。S/MIME 或 OpenPGP/MIME 往往在签名内容中保存一套内部头部;外层被编辑时,内层仍可能完好。此时不是简单的真与假,而是验证器看了哪一层。

格式合法也不代表来源可信。攻击者可以预先写入看似内部的批准标签。可靠流程应先冻结原始来信并验签,再隔离不可信标签,最后产生带明确来源的本地声明。

应保存的决策收据

收据至少包括:原始头部哈希、全部字段及出现顺序、脚本版本与能力集、逐步动作编号、选择器和比较器、策略是否放行、每步前后的当前头部哈希,以及下一动作实际读取的视图。

还要保存修改前后签名结果、去重键、实现选择的冗余动作、存储或提交确认,以及目的端的独立观察。选择 redirect 只证明分支被选中,不证明收件方已接收或保存。

来源