摘要

  • RFC 5137 面向确实需要用 ASCII 表示 Unicode 的协议;能够直接承载 UTF-8 时,通常应优先使用原生形式。
  • 规范建议转义 Unicode 码点,而不是再次转写 UTF-8 字节或 UTF-16 代码单元,除非具体场景有充分理由。
  • 采用转义的协议必须写明完整语法,也必须规定如何按字面表示转义引导符。
  • 显式结束定界符能说明四位、五位或六位十六进制数到哪里结束。
  • 单看 \u 无法判断语法,因为 C、Java、JSON 等环境赋予相似写法不同含义。
  • 新的互联网协议不应把代理项对作为通用转义模型,否则补充平面字符又会被拆成两段。
  • 推荐形式包括有显式定界的反斜杠-u 码点引用,以及保留分号的 XML 十六进制字符引用。
  • 正确恢复码点不代表整串文本合法、已规范化、适合某个标识符字段或与另一串相等。
  • 安全检查若放在反转义或规范化的错误一侧,仍可能被另一种拼写绕过。
  • 码点相同、字形相似、标识符相等和账户绑定是四类不同命题。
  • 可审计链条应分别保存入口字节、原始转义、标量值、规范化结果、比较键和授权结果。
  • 领导者应让每个转换边界有明确责任人,不能让最清晰的一张语法收据替代所有后续判断。

“解析成功”为什么不是终点

设想一个账户导入任务。上游把非 ASCII 字符写成转义,下游解析器得到一个码点,目录服务把整个名字规范化,权限系统再用自己的比较键寻找主体。四个组件都可能在日志里写下“用户名”,但它们处理的对象并不相同。

RFC 5137 管的是其中最早的一道门:当互联网协议无法直接承载 Unicode 字符而必须使用 ASCII 时,应怎样引用那个字符。它建议直接写码点,不要让读者先还原一组 UTF-8 字节,或先拼接两个 UTF-16 代码单元。这样更便于人工查表,也减少了一层隐藏解码。

规范同时明确收住了权力。它假设准备转义的字符串已经“有效且合理”,并不制定一般性的 Unicode 字符串处理规则。什么能成为用户名、是否必须 NFC、哪些字符需要上下文规则、如何比较、如何显示,都属于别的合同。

因此,“解析器恢复了正确码点”是一张有价值但范围有限的收据。它不能自然升级成“用户输入了我们以为的名字”,更不能升级成“系统把权限给了正确的人”。每跨过一层,都必须说明输入、转换规则、输出和决策主体。

码点与字节不能互相冒充

Unicode 码点是编码空间中的整数。UTF-8 用一到四个字节把它序列化;UTF-16 用一个或两个十六位代码单元表示。转义字节是在描述运输包装,转义码点是在直接命名抽象值。两者都可能需要,但证据含义不同。

如果把 UTF-8 字节逐个转义,非 ASCII 字符会裂成数个十六进制片段。审计者必须知道采用 UTF-8,再运行一次解码,才能找到真正的码点。这个过程容易掩盖截断、重复或错误顺序。RFC 5137 因而把直接码点形式作为缺省偏好。

这并不意味着可以丢弃字节证据。入口处仍要验证 UTF-8 是否完整、最短且不含禁止序列。只记录最终码点,就无法证明原始传输没有经过替换或宽松修复。可靠系统同时保存两张收据:网络边界的字节与声明编码,解释边界的标量值与错误。

UTF-16 的补充平面字符尤其能说明问题。它们需要高、低两个代理项。每一半在某些语言语法里都像一个合法的四位转义,却不是独立 Unicode 标量值。若过滤器逐段检查、后续服务才拼合,字符就可能在控制之后才出现。RFC 5137 因此说互联网协议不应使用代理项对。

定界符把猜测变成合同

码点范围到 U+10FFFF,直接十六进制引用会有四、五或六位。如果没有结束边界,紧接着的十六进制字符究竟属于值还是正文,就要靠固定宽度或实现习惯来猜。不同程序可能在同一位置切出不同结果。

RFC 5137 给出的反斜杠-u 推荐形式用成对撇号包住四到六位数字;另一种推荐形式沿用 XML 数字字符引用,并保留结尾分号。HTML 有时允许省略分号,但规范指出这样会失去定界带来的好处。协议还要规定怎样按字面写反斜杠或与号。

关键不在于全世界采用同一种标点。相关协议沿用已有写法可能更一致。关键是语法必须闭合:引导符、大小写、位数、结束符、合法范围、字面转义和错误行为都要写清楚。

测试也必须围绕边界设计。四、五、六位值后面紧跟十六进制字符时怎样解析;缺少结束符是否拒绝;代理项、超范围值、引导符本身、截断输入怎样处理。用同一个库编码再解码,只能证明它与自己一致,不能证明独立实现互通。

同一个 \u,不同的语法所有者

\u 看起来很熟悉,却不是一张通用 Unicode 许可证。C 用大小写不同的形式表示不同位宽;Java 的四位值指向 UTF-16 代码单元;JSON 也用四位转义,并用一对转义表示某些补充字符;还有语言使用花括号或可变位数。

这些规则在各自环境里未必错误。危险发生在团队只复制外观,没有带上语法所有者。生产者认为自己发送 UTF-16 单元,网关却按码点验证;基础平面测试全部通过,一到补充平面才分叉。若测试素材长期只覆盖英语和少量欧洲文字,问题会一直潜伏。

JSON 就展示了分层合同的必要性。RFC 8259 定义 JSON 字符串,包括代理项对写法;I-JSON 再要求有效 Unicode 标量值,并提醒未配对代理项会产生不可预测结果。加强约束来自命名清楚的上层配置,而不是来自 \u 三个可见字符。

迁移时也不能把多种含义放进一个自动探测器。旧字段和新字段应在解码之前就能按版本或语法区分。遥测要统计每个生产者使用哪份合同,才能安全撤掉兼容路径。看不到错误,不等于旧解释已经没人使用。

反转义之后,规范化才开始

Unicode 中不同码点序列可能具有规范等价关系。UAX #15 定义 NFC、NFD、NFKC 和 NFKD;RFC 5198 为 Net-Unicode 选择 UTF-8 与 NFC;W3C 字符模型要求规范说明接受、产生和比较哪种形式。RFC 5137 的清晰定界并不会替系统做出这些选择。

操作顺序因此成为安全属性。若过滤器只检查反转义前的 ASCII 拼写,数字形式可以在之后变成被禁字符。若唯一性检查在规范化前执行,而查找在 NFC 后执行,两条当时不同的输入可能落到同一个键。若展示层才规范化,用户看到相同名字,存储和权限却仍认为它们不同。

RFC 5137 的安全章节直接提示了这一点:最小形式、规范化和其他安全检查可能在错误阶段执行。稳健流程应明确为:先验证转义语法,再恢复标量值,拒绝不合法范围,然后应用字段规定的规范化和字符串配置,计算比较键,最后才做授权决定。

“已规范化”也不是完整证据。必须说明形式、Unicode 数据版本、所处阶段、输入与输出。兼容规范化可能抹去业务需要保留的差异;大小写映射依赖具体配置;长度限制在规范化前后会得到不同结果。模糊标签只会制造下一张越权收据。

标识符是政策,不只是文本

PRECIS 为国际化标识符和自由文本提供类别、配置、上下文规则、规范化与比较框架。IDNA 为域名标签定义有效类别、NFC 约束以及 A-label 和 U-label 的关系。这些机制都从“已经拿到码点”之后才开始工作。

某个值可以是合法 Unicode 标量,却不适合当前字段。是否允许可能取决于相邻字符、书写系统、分配状态和 Unicode 版本。域名标签还要遵循自己的转换与往返验证。数值转义没有证明它可注册、已存在或指向预期资源。

视觉相似又是另一层。不同码点可能产生近似字形,同一序列也会随字体、塑形引擎、方向与上下文而变化。明确列出码点有助于调查者发现差异,却不能保证普通读者不会误认。

高影响标识符需要受控字符集、书写系统政策、易混淆审查、碰撞检测,以及不依赖争议名称的恢复路径。授权最好绑定稳定的不透明主体 ID,把国际化名称作为可展示、可搜索、可复核的属性。这样展示层的歧义不会直接选择权力。

为每次转换留一张收据

入口收据记录字节、声明编码和通道。解析收据记录原始 ASCII 转义、选用语法和错误。Unicode 收据记录恢复出的标量值。配置收据记录版本、规范化形式、上下文检查和比较键。授权收据记录内部主体、政策、资源和结果。

若人工判断依赖外观,还要记录字体、塑形引擎、语言环境、方向和周边文本。截图能保存效果,却不能说明底层序列;原始日志能保存数据,却不一定反映操作者当时看见的内容。二者应关联,而不是互相替代。

端到端测试应穿过真实运行时:Java 生产者、JSON 网关、UTF-8 服务、数据库和浏览器客户端。素材要包括补充平面字符、组合序列、双向文本、字面引导符、禁止值和截断。既验证成功路径,也验证每种拒绝。静默插入替换字符是一次身份变化,不能记为成功。

这条收据链的价值,是让争议停在真正发生变化的位置。团队无需把所有问题笼统归罪于 Unicode,也不能拿最漂亮的控制台截图替代现实。每层只对自己观察到的对象负责。

薄标准不应变成厚授权

RFC 5137 的优点正是克制。它在必须跨越 ASCII 边界时提供共享默认:直接引用码点,优先使用明确结束边界。它提高可读性和可调试性,却不宣称自己是身份系统、注册政策或反欺骗机制。

组织设计也应保持同样边界。协议负责人拥有语法;运行时负责人证明标量解码;国际化负责人拥有规范化与配置;产品负责人定义相等与碰撞;安全负责人验证检查顺序和易混淆;授权系统把决定绑定到稳定主体。

二阶风险在于,拥有最清晰证据界面的团队逐渐获得超出证据范围的解释权。解析器展示了准确数字,事件就被结论为“身份正确”;页面显示了预期字形,存储就被假定相同;目录接受了字符串,操作者就被假定有权。每一步都把局部真相变成全局幻觉。

RFC 5137 给出了第一张好收据。成熟系统的标志,是没有人把它当作最后一张。

来源

  1. RFC 5137 HTML 版
  2. RFC 5137 纯文本版
  3. RFC Editor 的 RFC 5137 记录
  4. IETF Datatracker 的 RFC 5137 记录
  5. RFC 5137 文档历史
  6. RFC 5137 勘误检索
  7. RFC 3629:UTF-8
  8. RFC 2781:UTF-16
  9. RFC 5198:Net-Unicode
  10. RFC 6365:国际化术语
  11. RFC 2277:IETF 语言与字符集政策
  12. RFC 8259:JSON
  13. RFC 7493:I-JSON
  14. RFC 8264:PRECIS 框架
  15. RFC 5890:IDNA 定义
  16. RFC 5891:IDNA 协议
  17. Unicode 标准附录第 15 号
  18. W3C 字符模型
  19. 最小初始规范、局部未来决策与自愿采用
  20. 现实层、符号权力,以及清晰为何显得敌对
  21. 运行代码优先