摘要

  • RFC8599 要求代理定期换发 PURR,即使底层推送参数没有变化;同时,旧值只要仍关联进行中的对话,就必须保留。
  • PURR 只在生成它的代理语境内唯一,不是设备的全球身份证。每段对话的参与选择、路由、本地映射、注册有效性与推送服务授权,不能合并成一个笼统的“唤醒权限”。
  • 隐私保护不是立即抹除全部内部状态。合理边界是减少对外可关联的信息,同时让持有旧依赖的本地代理继续履责,直到该依赖真正结束。

两个“必须”,不能只实现一个

一个接口如果要求换发引用,又要求旧引用继续可用,很容易被误读为自相矛盾。前者似乎在说尽快抛弃过去,后者似乎在说尽量记住过去。但 RFC8599 的长时间对话机制并没有提出这种二选一:它要改变的是未来对外暴露的引用,要保留的是尚未结束的对话依赖。这两个对象不相同,两个动作也不相互替代。

设想一段对话建立时使用引用 P1。后来,终端再次注册,收到新的 P2;另一段新对话可以使用 P2。第一段对话的后续请求却可能仍携带 P1。这里的 P1、P2 是解释用的虚构标签,不是抓包数据、真实标识或已观测事故。这个情景问的不是终端有没有拿到新值,而是代理还能不能处理此前已经形成的依赖。

如果系统删除了 P1 对应的映射,它可以通过许多看似令人放心的检查:最近一次注册成功、新引用能查到、新对话能建立、推送服务能受理新请求。可是,这些结果都没有证明第一段对话已经结束。对新状态的验证不能充当旧责任已关闭的证据。旧请求到了以后才发现无法解析,不是隐私保护天然必须支付的代价,而是把引用更新误当成了依赖终止。

2019年5月发布的 RFC8599 在第6.2.1节明确区分了这两项义务。代理必须定期生成新的 Proxy Unique Registration Reference,简称 PURR,即使推送参数未改变;也必须保留旧 PURR,只要与它们关联的对话仍在进行。一个新值的出现,并不自动撤销所有旧值已经承担的用途。治理问题恰好发生在这里:更新的权力与持续服务的责任,需要一起被描述。

应用睡着了,不代表注册消失了

移动操作系统可以暂停应用。一个 SIP 用户代理即使仍有有效的注册绑定,也未必随时保持能够接收信令的应用状态。推送机制用来补上这种可达性条件:代理向推送服务请求通知,应用醒来后刷新注册,后续信令才有机会通过适当的注册与连接条件抵达。通知不是把 SIP 请求换一种包装直接塞进手机。

因此,推送服务受理通知、用户代理醒来、注册刷新、原来的 SIP 请求被转发,是不同事件。不能用第一件事证明最后一件事已经成功。反过来,等待中的 SIP 请求也不会因为通知已经受理就获得无限寿命。需要考察的是一条有多个边界的过程,而不是一个永远有效的“在线”字段。

用户代理先向所选的 Push Notification Service,即 PNS,取得 Push Resource ID,简称 PRID。该值的格式由具体服务决定。代理申请推送时,需要提供商信息、PRID,以及该提供商要求时的额外参数。PNS 的发现、注册和维护并不由 RFC8599 统一规定。标准没有把不同提供商的订阅、身份和生命周期收编为一套全球账户制度。

SIP 注册绑定、传输连接、PNS 资源、进行中的对话和一条具体请求,各有自己的存续条件。连接中断不等于所有对话已经结束;对话仍在进行,也不等于提供商资源永远有效。把这些状态压缩成一个“活跃设备”标签,虽然方便展示,却可能丢掉决定是否能继续申请推送的关键事实。

注册过程还针对单独的绑定处理。一个用户代理可能拥有多个绑定,推送触发的刷新涉及它们各自的注册安排,而不是给一个想象中的全球设备登记续期。保留这种粒度,才有可能说明某个已移除绑定为何不能再触发通知,同时不随意宣告另一项仍存续的依赖也已消失。

让对方知道引用,不让对方知道全部坐标

远端对话参与者需要能够发出后续请求,但没有理由因此获得应用在推送服务中的私密坐标。RFC8599 对用户代理作出限制:除 pn-purr 这个明确例外外,不得在非 REGISTER 请求中包含相关的 pn-* 参数。也就是说,pn-provider、pn-prid 和 pn-param 不应随着普通对话请求送给另一端。

PURR 在这里承担间接引用的作用。代理自己生成值,并用它查回已经存储的、申请推送所需的信息。引用只在该代理的语境内要求唯一。它不是跨所有网络通用的设备号码,也不需要一个机构替所有代理分配永久身份。一个对端拿到的,是通向有限本地功能的引用,不是整个设备与提供商关系的公开说明书。

标准要求 PURR 值不可伪造、匿名,并且对于代理之外的实体不可关联。持有映射的代理本身当然必须能够关联,否则无法找到所需信息。这不是偶然留下的漏洞,而是功能与隐私边界的一部分。足够大的安全随机值空间可以是实现办法之一,但规范没有强制全球使用同一种算法、数据库或编码形式。

这组属性不能外推为全部 SIP 信息均已匿名。其他头字段、对话行为和运营日志可能还有信息;一个不透明值也不能证明运营者没有记录关联。公开承诺应该针对实际保证的对象,而不是把“引用值不可关联”升级为“任何观察者都不可能识别任何通信活动”。后者没有得到这套机制的支持。

PURR 也不是 GRUU。RFC5627 的 Globally Routable UA URI 用来把请求路由到特定用户代理实例,其全球可路由角色与 PURR 的代理本地注册查找不同。两者都与通信可达性有关,并不意味着可以混用。把一个局部查询引用称为全球身份,会扩大它被想象中的权力,也可能诱导不必要的集中记录。

换发的意义于是更加清楚:不要让远端长期依赖同一个稳定外部值来进行关联。保留的意义也同样清楚:本地代理仍要处理进行中对话使用的早期值。对外减少稳定性,与对内保持有限关联,可以同时发生。它们对应的是不同读者、不同用途和不同责任。

参与不是一次性覆盖所有对话的许可

代理支持推送,不等于应用的每一段对话自动加入这一机制。RFC8599 把选择交给用户代理的本地策略。应用可以根据对话或媒体特性,决定这一段是否支持推送辅助的入站对话中请求。另一段对话有自己的选择,不能仅因为终端曾经接受过推送,就视为以后所有会话都获得同样许可。

选择参与时,用户代理把最近收到的 sip.pnspurr 指示值放入相关初始 Contact 的 pn-purr 参数。如果没有收到这个能力指示,它不得自己发明一个 pn-purr。对端应当收到有实际代理能力支撑的引用,而不是应用对某个未知中间节点作出的单方面承诺。

RFC6809 的 Feature-Caps 框架提供了背景:不由 Contact URI 表示的网络实体,也需要表达自身能力。IANA 对 sip.pnspurr 的登记描述,涉及代理把对话中请求与注册信息关联起来。这是关联能力的声明,不是推送服务的通用授权证明,更不是控制应用媒体与行为的无限许可。

实际条件必须拆开:用户代理为该对话作出选择,请求沿适当路由到达代理,代理能解析引用,底层注册与提供商条件仍可使用。推送服务如何鉴权与授权,则属于具体 PNS 的规则。任何一个条件成立,都不能替其他条件完成验证。“查到了引用”与“有权继续唤醒”,不是同一句话。

这种分工的治理价值不是减少所有责任,而是让责任落在正确的位置。应用无需请全球机构审批每段对话的本地策略;代理也不能拿提供商的受理结果,掩盖它在路由、注册或对话选择上的缺失。有限共同接口可以协调这些参与者,但不必把它们的后续判断统一交给一个新的许可中心。

旧引用的责任,要以依赖而不是日历判断

一次后续注册响应可以把新值交给用户代理,却不能仅凭这一事实证明所有已建立对话都已经替换了此前获知的联系信息。早期对话仍然使用旧值,是规范要求保留旧 PURR 的理由。应当问的是哪个进行中的对话还关联该值,而不是该值是否看起来已经“不够新”。

这也限定了清理行为。RFC8599 没有规定全球统一的换发周期,也没有给所有旧引用规定一个固定保存小时数。代理可以选择本地实现,但本地维护计划不能凭空关闭尚存的对话依赖。到了某个清理时刻,并不天然意味着对端已不再使用旧引用。

另一方面,保留义务也不是永久收藏所有值。它与仍在进行的关联对话相连。把问题说成“立即删除”或“永远不删”的两难,会回避真正需要设计的退出条件:什么时候旧值不再承担这项依赖,怎样知道相应责任已经结束。这里没有必要发明一个统一的全球对话结束裁判。

容量估算因此不能只看当前注册条数。引用发行频率、对话持续时间和依赖终止,可能影响需要保留的关联状态。这是从机制推导的运营建议,不是本研究测得的生产规模,也不是 RFC 要求采用的表结构。合规性要看它能否履行义务,不是数据库是否长得像某个示例。

节点接替会把这个问题放大。新节点复制了最新注册,不一定就能处理早期对话的 P1。代理替换、分片变化和映射存储迁移都需要解释剩余依赖。RFC8599 并未规定一套通用复制、迁移或密钥轮换协议。采用标准功能,并不等于已经证明任意维护切换下的状态连续性。

这里的成本具有归属:承担解析的本地代理及其运营者,需要保存或妥善交接仍被依赖的关联。成本的存在不能自动论证一个全局 PURR 目录的必要性。集中目录可能方便查询,却也可能把原本局部的关联知识扩散到更多实体,改变机制的隐私与控制边界。

还必须经过能解析它的代理

正确映射存放在某个节点上,只有请求能到达它时才有用。在适用的初始对话处理里,支持此机制的代理必须通过 Record-Route 留在后续对话请求的路径上。RFC3261 对话的路由集与远端目标提供了这一架构背景。PURR 不能代替路由,也不能把任意经过的节点变成负责该关联的代理。

RFC3327 的 Path 是相关但不同的机制。它记录到达已注册用户代理所需的中间路径;Record-Route 保留的是已建立对话的后续路径。注册侧知道怎样到达终端,不等于该对话之后一定访问保存 PURR 的节点。把二者混为一谈,容易用一张正确的注册示意图,证明一个尚未被验证的对话路径。

RFC5626 的用户代理发起连接、保活与多连接安排,又针对另一组可达性问题,包括 NAT 和防火墙限制。它不能保证移动应用持续处于可接收信令的状态。连接可用、注册有效、路由正确与应用已醒来,是应分别观察的条件,不是四种可互换的“成功”标签。

在第6.2.3节规定的对话中请求处理中,pn-purr 可能出现在 Request-URI 或 Route URI 中,具体取决于路由集构造。代理能查回所需信息时,把 SIP 请求放入待处理存储,再申请推送。收到相关 REGISTER 事务的2xx响应后,代理取出关联的对话中请求并转发给用户代理。

这里不进行第5.3节用于其他场景的 URI 比较。对话中请求并没有携带 pn-prid、pn-provider 和 pn-param 这些私密参数。代理用 PURR 与注册响应之间的关联来找请求。间接引用不只是把信息“遮住”了;它确实改变了关联请求所用的依据。

上述次序明确针对对话中请求。初始请求有其自己的条件,包括传输方式影响下的次序。不能据此宣称所有 SIP 请求在所有情况下都必须等到 REGISTER 的2xx才可能抵达用户代理。避免这种过度概括,才能在解释隐私机制时不破坏周围协议的真实边界。

保留依赖,不能把已终止的权限延长

旧对话仍依赖引用,不等于其底层注册可以永不失效。代理获知某个绑定已过期或被移除后,RFC8599 要求它不得再用相关 PRID 申请推送。旧 PURR 留着,并不能恢复已移除的绑定、重新使无效订阅有效,或替提供商补出不存在的授权。

请求本身也有期限。代理等待通知、注册及其响应时,发起方的事务会继续走向超时。规范明确提醒这一点,并建议选择主要影响该请求事务、而非无端损害整个对话的错误响应。持续服务义务不是无限等待的许可证,更不是保证每次尝试都能成功。

PNS 的具体安全规则也不能被 PURR 替代。RFC8599 把具体提供商的鉴权和授权留给对应规范,同时要求 SIP 信令得到适当加密保护。注册中的推送参数不得泄露给其他用户或不可信网络实体。注册事件通知是另一个可能的泄露路径,不能只检查初始 Contact 就宣布保护完成。

RFC8030 描述 HTTP Web Push 的安全条件,但它不替 SIP 应用决定每段对话是否参与。服务受理一个通知、代理解析一个值、远端拥有一段已建立对话,仍然是不同事实。混成单一权限,会让不必要的唤醒、电量消耗和额外信令缺少清楚的责任归属。

于是需要同时保留两条界线:尚存的对话依赖不能被新引用抹除;已结束的注册或提供商权限也不能被旧依赖重新激活。它们不是相互抵销的义务。一个本地系统只有能够分别解释这两条界线,才可能合理处理旧状态,而不是一律删除或一律续命。

文档可以证明有功能,不能证明某个集群已履责

本研究采用固定版本的 OpenSIPS3.6 registrar 文档作为实现证据。文档描述 pn_enable_purr、pn_process_purr,以及可配置的 pn_refresh_timeout。它让能力宣布、引用查找与异步等待这些操作具有具体实现语境,但不是任何运营者现网的合规报告。

OpenSIPS 在2020年的两篇支持说明,也解释了注册刷新与对话中处理。它们具有历史实现参考价值,却不能证明当前某个部署如何保留所有旧关联,或节点之间是否复制成功。本研究没有向任何 SIP 网络发送请求,没有测量实际集群的保留、延迟或恢复行为。

协议勘误同样需要按状态引用。8136 是已验证的编辑性勘误,修正第4.1.4节中的 sip.pnsreq 为 sip.pnsreg。7136 是已报告的技术性勘误,涉及 REGISTER Request-URI 示例;它并不是已被接受的规范修订。分析不需要拼出一个未经测试的可执行示例,更不能把待处理报告写成已经生效的新规则。

这些证据共同支持一个有限但重要的判断:协议确实要求在换发与保留之间维持双重责任;公开实现文档说明该控制面可以落实为具体操作;它们不提供任意部署已经正确完成这件事的证明。把范围说清楚,比用一串功能名制造“已经验证”的印象更有价值。

从私密引用看到有限共同规则

卢恒提出的最小初始规格、本地化未来决策与自愿采用,为这一机制提供了一种明确声明的治理视角。这里不是说 RFC8599 的作者采用了这套框架,而是用它分析:共同协议应把引用的范围、对话参与、关联连续性和终止边界讲清楚;后续存储、容量、策略与维护选择则留给承担责任的本地系统。

公共规则不必审批每次对话,才能使承诺可理解。恰恰因为它不接管全部后续操作,才更需要把最初建立的义务写准确。一个私密引用能够换新,同时旧对话能够继续依赖本地关联,就是小接口携带持续责任的实例。

结论不是禁止轮换,不是无限保留,也不是建设全球权限中心。它是拒绝一个偷换:新值证明了发行,却没有证明旧关系结束。合理的治理让未来交换减少不必要暴露,也让已经形成的交换不被更新动作无声遗忘。

来源