摘要

  • RFC 5360 把 permission 与 relay 的 translation logic 放在一起。列表管理员提出加入一个 URI 并收到 HTTP 202,只表示操作进入处理;接收者仍处于 pending,尚未授权 relay 向其转发。
  • permission document 绑定发送者范围、原始接收者或 target URI、最终 recipient URI,以及 grant/deny 能力。认证点击者、安装状态和后续请求命中这份许可,是三张不同收据。
  • 单次只加一个接收者、470 Consent Needed、刷新、删除与 Trigger-Consent 撤销共同约束放大和过期权限,但都不能单独证明 fan-out 正确、终端收到、用户注意到或撤销后没有残留发送。

一个“同意”必须回答三方关系

relay 收到发往 target URI 的请求,然后生成一个或多个发往 recipient URI 的新请求。它可能是 proxy、B2BUA 或混合设备。列表、注册和本地规则都可能成为 translation logic 的输入。

因此,同意的对象不是抽象的“接收 SIP”。permission document 要描述 sender、target 和最终 recipient。接收者答应的,是某个 relay 在特定范围内执行这项翻译。

规范允许 sender 常常使用通配,也允许 target 在文档模型下通配,但最终 recipient URI 不得通配。这个不对称保证授权最终落到明确地址,而不是让一次操作覆盖任意未来接收者。

语法正确仍不等于含义被理解。人类可读部分是否展示了通配?它和机器 XML 是否一致?target 的别名后来是否指向了另一项服务?sender 范围是否因身份系统变化而扩大?

应保存原始字节、哈希、格式版本、relay 身份、sender、target、recipient、grant/deny 能力哈希和向用户呈现的文本。后来的每一次转发还要产生运行时匹配收据。

如果只留一个 consented=true,审计无法知道这个“是”究竟答应了什么。

管理员有权修改,不代表有权替别人答应

relay 通常会先认证并授权可以改列表的客户端。这防止陌生人任意重写配置,却没有解决最终接收者的意愿。

一个完全合法的管理员仍可把不愿接收的人加入组。RFC 5360 因而把配置权与同意权分开:管理员可以提出 recipient,最终地址的控制者决定 relay 是否能够向那里翻译。

审计记录要保留两条决策。第一条是哪个 principal 根据什么政策提出了变更;第二条是谁作为 recipient 所有者,在看见哪份 permission document 后授权或拒绝。

“由已授权管理员添加”不能替代第二条。列表名称、组织职位或群组所有权都不会自动吸收成员对自身地址的权限。

这也是为什么 permission 要落在执行翻译的 relay 上。管理员界面的截图只能证明意图,不能保证运行中的 relay 使用了相同版本和同一份许可。

HTTP 202 是进入队列,不是接收者同意

典型流程里,A 通过 XCAP 请求加入 B。relay 回复 HTTP 202,把 B 标成 pending,然后才发送包含 permission document 的 MESSAGE。

202 证明 relay 接受了这项管理操作并将继续处理。它不证明 B 在线、请求送达、B 阅读、授权安装、未来请求命中或内容最终送达。

Pending Additions 事件包正是为了暴露异步状态:pending、waiting、error、denied、granted。如果 B 离线且没有 store-and-forward,permission MESSAGE 可能失败,A 需要得到错误,而不是从早先的 202 推导成功。

保存操作 ID、target、候选 recipient、初始响应、状态版本和每次 NOTIFY。进入 granted 时,将状态与文档哈希和身份验证事件相连。

被接受处理的列表修改,只授权 relay 继续请求同意;它没有借到 B 的未来答案。

翻译执行点才是权限控制面

translation logic 把一个进入地址映射到多个输出地址,一对多时尤其可能放大。即使一对一,也可以把原本发往某个名称的流量导向一个没有请求它的人。

RFC 5360 要 relay 存储与翻译逻辑相关的 permissions。没有授权的 recipient 在执行时应被忽略。这样,决定 fan-out 的组件拥有它需要执行的权限状态。

但数据库里存在 permission 还不够。一次真实请求必须把 sender、target、recipient、permission 版本和输出 request ID 连起来。缺少运行时 join,无法证明 relay 没有读到旧缓存、错误别名或别的租户状态。

架构也改变证据保管。proxy 可能保留端到端对话关系,B2BUA 会终止并重建。报告必须指出哪个组件改变 URI、应用 permission、生成请求和记录结果。

“列表服务已经同意”太含糊;列表没有意愿,接收者有意愿,relay 有执行责任。

空 PUBLISH 仍然携带身份决定

接收者向文档里的 grant 或 deny URI 发送空 body 的 SIP PUBLISH 或 HTTP GET。内容为空,是因为 capability URI 已经指向特定 permission,而不是因为请求没有语义。

relay 必须确认动作来自最终 recipient 的所有者。规范列出 SIP Identity、受信行政域内的 P-Asserted-Identity、共享秘密场景下的 Digest,以及 return routability。

它们的证明范围不同。P-Asserted-Identity 依赖信任边界;Digest 依赖秘密和 challenge;签名身份需要与 recipient owner 对照;return routability 主要证明某个主体拿到了秘密能力。

不要只存 authenticated=true。记录方法、principal、被验证的 URI、信任域、credential context、capability 哈希、策略版本和决定。

属于另一 principal 的有效凭据,仍然不是这个 recipient 的同意。

Return routability 证明秘密能力到达,而非永久身份

RFC 5360 承认 return routability 弱于 SIP Identity,但在后者当时不普及时可用。relay 生成难以猜测的 URI,把它放进 permission request,能使用该 URI 的一方被视为通过验证。

这个结论依赖明确条件:MESSAGE 必须发往 SIPS URI;grant/deny URI 必须是安全的 SIPS 或 HTTPS;随机部分至少具有 32 位密码学随机性。

即便全部满足,证明也很窄:有人取得并使用了沿受保护路径交付的 capability。它不独立证明真实姓名、持续控制、充分理解,也不排除 store-and-forward 副本泄露。

日志应记录生成器和熵声明、capability 哈希、交付路径、兑换时间、重放处理和操作主体,避免保存可重用明文。

从 return routability 迁移到 SIP Identity 会改变验收政策。旧收据不能被事后包装成更强的身份结论。

单次一个 recipient 是带宽信用,而非总限速

同意请求自身也能被放大。攻击者若用一个很小的配置请求加入大量 URI,relay 就会向每个地址发送 MESSAGE。

规范通过 credit-based 约束减少这项不对称:XCAP 每个 HTTP transaction 最多加入一个 recipient;在这一框架下 REGISTER 每次最多一个 contact。客户端为每个潜在输出付出接近的输入成本。

这不是全局速率限制。攻击者仍可发很多小请求、使用许多身份或延长时间。它阻止一次变更直接购买巨大的 fan-out,并不证明整体流量安全。

监控 client identity、target、recipient、输入/输出字节、频率、409/403 拒绝和重试。必要时在账户、target 和 destination 维度增加总预算。

还要检查离线存储和重试不会把一次 permission request 复制成延迟放大。

一项缺失许可让 470 阻止整次展开

request-contained URI list 在通信发生时才选择接收者。relay 预先维护一个已取得 permission 的 URI 超集,再检查本次请求内的具体列表。

如果其中任何一个 URI 没有许可,relay 必须不执行翻译,并应返回 470 Consent Needed,通过 Permission-Missing 指出缺失项。

这是原子边界。若先向有权限者发送、再报告其他地址失败,效果已经发生,而且调用者可能把部分成功误读成普通错误。不同结果还可能泄露成员构成。

保存输入列表哈希、permission set 版本、逐项匹配与 470 响应。missing list 也属敏感信息,应限制可见范围。

捕获的 RFC Editor errata 页面显示一个 Reported 技术报告,讨论 URI 中逗号、问号或分号可能使 Permission-Missing 的 addr-spec 解析含糊时使用尖括号。它只是待处理快照,不是已验证修订。

撤销意愿要穿过恢复流程才能改变状态

recipient 可以使用最初得到的 deny URI。若能力丢失,后续翻译请求可携带 Trigger-Consent 和 target URI,recipient 借此请求新 permission document,再执行 deny。

这说明“想撤销”和“已删除”之间存在窗口。用户甚至可能先收到另一条不想要的请求,才取得恢复入口。

relay 要把 trigger capability 与正确 recipient 和 target 关联。第一个 PUBLISH 的 200 只表示恢复请求被接受,不表示旧 permission 已消失。

撤销账本要记录意图、trigger、新文档、认证后的 deny、翻译逻辑版本、切换时刻以及旧授权下最后一次输出。并发请求必须有明确 cutover 规则。

上下文消失也应删除 permission。移除 recipient、注册 contact 过期或长期不刷新,都不能留下永续权力。规范建议周期刷新,但将间隔交给应用确定。

“从未主动撤销”不等于“永远有效”。

第三方 REGISTER 把绑定和授权拆开

普通注册往往由同一实体设置 contact 并在同一连接上接收,绑定与授权历史上同时完成。第三方 REGISTER 打破这个前提:攻击者可以把自己的 AoR 绑定到受害者 contact,让不请自来的流量转去受害者。

在该框架下,202 只把 contact 置于 pending,registrar 随后向 contact 请求 permission,并通过 Pending Additions 报告状态。

记录 AoR、contact、registrant principal、连接关系、是否第三方、permission tuple 和最终 forwarding decision。

这不是说所有注册都需要额外仪式。规范区分同一连接上注册并接收的常见信任情形,也区分不接受第三方注册的 registrar。必须先说清是哪种架构。

“REGISTER 成功”否则会掩盖它可能只代表进入处理,并未获得转发权限。

加密保护文档,不替用户解释文档

permission document 和 pending state 揭示 sender、target、recipient 的敏感关系。攻击者若修改文档,就可能让用户批准与界面所示不同的翻译。

RFC 5360 建议强完整性与机密性,并讨论端到端 S/MIME 或在其不可用时使用 TLS/SIPS。store-and-forward 即便客户端接口不是 SIP,也应保护交付。

这些措施保护传输边界,却不证明 human-readable 与 machine part 一致,不证明通配已展示,也不证明 relay 后来安装和执行的是同一 tuple。

呈现前比较两份表示,用哈希绑定用户看到的文本,分别记录 transport、content protection、identity、interpretation 与 execution。

测试修改、重放、capability 泄漏和旧状态。不要让安全通道替同意语义背书。

后来的语法澄清不会自动进入产品

RFC 8217 更新 RFC 5360 等 SIP 文档,澄清何时使用 name-addr production。这改变有关字段的规范解释环境。

文档关系不证明某个 relay 已升级 parser,也不能在不知道版本时重新解释旧 trace。每次测试要保存适用 RFC 集合和实现版本。

Proposed Standard 状态同样是文档事实,不是采用率、互操作或当前服务行为的收据。