摘要

  • 《JMAP Enhanced Result References》草案允许后续方法用 JSON Pointer 或可选的 JSON Path,从先前方法响应中选择值,并把它们放进对象属性、PatchObject 或查询过滤条件。
  • 解析层可以故意不理解业务语义;它证明路径怎样得到一个值,却不证明该值来自权威来源、仍然新鲜,或有权支配目标操作。

网关收到一组按顺序执行的 JMAP 方法调用。第一个方法返回对象集合,第二个方法的某个字段写着带 # 前缀的结果引用。网关根据 resultOf 找到前一调用,用 name 核对方法,再执行 path。表达式命中一个节点,值的 JSON 类型也与目标字段相符。

从中间层看,这是一笔完美交易:输入、选择、输出都有确定记录。后来才发现,被选中的标识符属于另一个账户。它在结构上正确,在目标业务域里却不成立。

网关没有失职。它从来没有被赋予理解账户归属、租户边界、资料敏感度或机构授权的能力。真正的失误,是上层把“解析器无法判断”悄悄改写成了“解析器已经批准”。

《JMAP Enhanced Result References》第 02 版发布于 2026 年 6 月 21 日,将于 12 月 23 日到期。研究截止时,它是 JMAP 工作组推进中的 Standards Track Internet-Draft,不是最终 RFC,也不是任何产品实现、互操作或部署普及度的证明。

草案扩展了 RFC 8620 已有的结果引用。一个 JMAP 请求可以包含多个顺序执行的方法调用;后续调用通过 resultOf、name 和 path 引用先前响应。新机制把引用延伸到 /set 的对象属性、PatchObject 的值,以及 /query 的 FilterCondition,并在 JSON Pointer 之外提供可选的 JSON Path。

它解决的是请求内数据流,不是数据的政治来源。

分层是优势,也是证据边界

草案明确允许一个语法中间层在“不理解类型、属性和语义”的情况下处理引用。它只需把先前响应视为不透明 JSON,应用 JSON Pointer 或 JSON Path,得到节点列表。之后,方法执行层再依据目标属性的定义解释结果并验证类型。

这是值得保留的设计。代理、网关或公共服务器组件可以统一实现选择机制,不必复制每一种 JMAP 数据模型。故障也更容易定位:是找不到先前响应,是表达式非法,是匹配数量不符,还是目标类型拒绝?

但同一设计也限定了成功回执的含义。语法层最多能证明:某个表达式在某份响应上产生了这些节点。它不能证明节点对应正确的客户、正确的法律主体、正确的工作流阶段或正确的处置权限。

方法层的类型检查也只增加有限证据。一个字符串可以是合法的邮箱 ID 形状,却属于错误账户;一个对象可以符合预期结构,却包含不得向目标受众披露的资料;一个布尔值可以完全有效,却来自已经过期的策略判断。

因此,好的系统不是取消分层,而是把分层写进证据链。语法层留下来源调用、选择器和匹配数量;方法层留下目标字段、预期类型和验证结果;授权层留下主体、账户、策略版本和允许理由。任何一层都不替其他层说话。

零个节点不是一个统一答案

JSON Path 可以返回零个、一个或多个节点。草案必须把这个节点列表映射为目标字段需要的形状。对原始值或单对象目标,恰好一个节点成为值,零个节点成为 null,多个节点产生 invalidResultReference。对数组目标,零个节点成为 [],一个或多个按 RFC 9535 的节点顺序组成数组。对 map 目标,零个节点成为 {},只有一个符合类型的对象才能被接受。

这些规则统一了传输层,却没有统一业务含义。

null 可能表示缺失,也可能命令服务器清空现有属性。[] 可能表示“没有找到成员”,也可能表示“把成员集合替换为空”。{} 可能表示没有额外设置,也可能抹掉全部命名配置。解析器只知道形状,不知道后果。

JSON Pointer 还揭示了一个细微差异:不含 wildcard 的精确路径如果不存在,应当失败;带 wildcard 的选择没有命中时,则可能按照目标类型产生空值。开发者若只为减少错误而把精确指针改成宽泛选择器,就可能把“没有证据”变成“执行清空”。

每个高后果目标都需要自己的零匹配政策:拒绝、保留旧值、明确清空,还是转交人工判断。这个政策应由理解目标语义的层制定,而不是由公共解析器猜测。

多个节点暴露的不是格式问题

当目标只能接受一个值而表达式找到多个节点时,拒绝不是不便,而是歧义回执。选择第一个节点会把文档顺序或遍历顺序伪装成优先级。选择最后一个同样没有授权依据。

如果业务确实需要“最新的一项”“受信任级别最高的一项”或“由某主体批准的一项”,就应在选择表达式、源数据或上层政策中明确这个规则,并保留它的来源。没有规则时,多个候选就是证据不足。

数组目标可以接受多个节点,但这也不意味着数组里的每一项都适合后续用途。顺序由 RFC 9535 节点列表确定,不一定是业务排序;去重、优先级和冲突处置仍然需要目标域规则。

草案拒绝隐式类型转换同样重要。字符串不能为了通过过滤器而自动变成数字或布尔值,复杂对象也不应被序列化为字符串来绕过类型要求。失败保留了源和目标并不兼容这一事实;转换则可能制造虚假的兼容性。

例如字符串 "01" 可能是代码,数字 1 可能是数量。把二者视为同一数据,是解释行为,不是格式修复。解释必须有一个承担责任的规则制定者。

带 # 的属性是一条数据通道

在 /set 中,带 # 前缀的属性保存结果引用,对应的不带前缀属性是目标。处理会递归进入嵌套对象。若同一请求同时给出 property 和 #property,含义发生冲突,应被拒绝。引用解析失败会让相关对象得到 invalidResultReference。

PatchObject 的目标键仍必须使用带 # 的 JSON Pointer 路径,而作为值的结果引用可以在能力允许时使用 JSON Pointer 或 JSON Path。这两套路径容易被人混为一谈:一个描述“改哪里”,另一个描述“值从哪里来”。审计记录必须同时保留。

在 /query 中,FilterCondition 也能使用类似形式。过滤条件可递归嵌套,服务器必须逐层处理。失败的引用会拒绝整个查询;普通属性和引用属性并存则是参数歧义。

动态过滤尤其容易扩大影响。前一响应提供一个类型正确的邮箱 ID,后一查询就可能以它为范围。若这个 ID 属于错误账户,或上一选择器太宽,查询仍可能在语法上完全正确。成本很低的一次选择,可以打开范围很大的读取。

所以监控不能只看 JSON Path 评估耗时,还要看它驱动了什么:后续查询扫描多少对象,写入面向什么受众,是否跨账户或租户,授权决策使用哪个策略版本。

缓存必须携带安全上下文

结果引用看起来适合缓存。同一份响应、同一个表达式,往往得到同一个值。若缓存键只包含响应字节和路径,结构结果可能确实正确;授权结果却未必仍然正确。

一个用户在私人空间成员身份有效时完成选择,之后管理员撤销权限。若后续请求继续复用旧结果,节点仍然存在,字符串仍然相同,但使用它的资格已经失效。缓存命中只能证明“找到了键”,不能证明“键包含了所有应当重新判断的事实”。

草案要求缓存不得跨用户或安全上下文共享,并应在访问控制变化时失效。实际设计应把主体、账户、相关权限和策略时期绑定到条目。必要时还应包含目标上下文,因为同一个源值可用于内部计算,却不能用于公开输出。

时序侧信道也不能忽略。如果命中与重新计算的延迟明显不同,攻击者可能推断另一个上下文中是否存在某条路径或数据。隔离不只是避免返回错误字节,也包括避免泄露存在性。

运行记录应区分现场解析和缓存复用,并能说明复用为何仍被允许。没有安全时期的 cache hit 只是性能指标。

JSON Path 的表达力需要预算

JSON Pointer 沿确定结构前进。JSON Path 可以过滤、使用 wildcard,并递归下降。面对大响应时,复杂表达式可能产生巨大的节点列表,消耗大量 CPU 和内存。嵌套引用还可能反复复制复杂对象。

顺序方法调用阻止了最简单的直接循环引用,却阻止不了病态链条。服务器需要限制表达式复杂度、评估时间、节点列表大小、单请求引用总数、嵌套深度和累计成本。

超过预算时应明确拒绝,不能静默截断。截断会改变选择结果,却保留成功外观;对单值目标而言,它甚至可能把“多个候选”伪装成“恰好一个”。资源保护不能通过伪造证据来实现。

JSON Path 解析器本身也是安全依赖,需要维护、补丁、隔离和恶意表达式测试。把表达式当作普通元数据,会遗漏真正执行遍历的代码。

资源限制的回执也应进入运营分析。突然上升的 invalidResultReference 可能表示攻击、源响应规模变化,或客户端表达式从精确选择退化成无界递归。盲目提高限额不一定是修复,也可能把控制失效变成更大的事故。

审计必须保存数据怎样获得影响力

最终对象只能回答“现在是什么”。它不能回答“这个值为什么会在这里”。增强结果引用提供了天然的血缘坐标:来源调用、方法名、路径、匹配数量、解析类型和目标位置。丢掉这些信息,相当于主动删除决策链。

Heng Lu 的薄层原则要求协调机制解决有限问题,并保留边界。解析器参与移动 JSON,不因此获得业务授权;方法实现参与类型验证,不因此代表人的意图;能力广告说明服务器支持语法,不因此授予客户端跨任何上下文复制数据的权力。

运行代码应验证负面命题:零、一个、多个节点分别进入标量、数组和 map;精确指针缺失与 wildcard 空匹配不同;类型相符但账户错误的标识符被拒绝;权限变更使缓存失效;递归表达式超过预算后拒绝而不是截断;审计能够从最终写入反向重建来源。

代理读对 JSON 是值得信赖的技术能力。正因为它做得准确,系统更应准确描述它没有做什么。它没有判断材料能否公开,没有判断标识符属于哪个治理域,也没有决定空结果是否等于删除。让中间层保持无知并不可怕;可怕的是,上层利用这种无知生成了一张看似完整的批准书。

来源