你说的对
- BOM 字符在 JSON 中是非法的(不需要)
- MIME 字符集在 JSON 中是非法的(也不需要)
RFC 7159,第 8.1 节:
实现不得在 JSON 文本的开头添加字节顺序标记。
尽可能清楚地说明这一点。这是整个 RFC 中唯一的“不得”。
RFC 7159,第 11 节:
JSON 文本的 MIME 媒体类型是 application/json。
类型名称:application
子类型名称:json
必需参数:n/a
可选参数:n/a
[...]
注意: 没有为此注册定义“charset”参数。
JSON编码
JSON 唯一有效的编码是 UTF-8、UTF-16 或 UTF-32,因为第一个字符(如果有多个字符,则为前两个)的 Unicode 值总是低于 128(没有有效的 JSON可以包含前两个字符的更高值的文本)总是可以通过查看字节流来知道使用了哪些有效编码以及使用了哪种字节序。
RFC 推荐
JSON RFC 说前两个字符将始终低于 128,您应该检查前 4 个字节。
我会换一种说法:因为字符串“1”也是有效的 JSON,所以不能保证你有两个字符——更不用说 4 个字节了。
我的推荐
我对确定 JSON 编码的建议会略有不同:
快速方法:
- 如果你有 1 个字节并且它不是 NUL - 它是UTF-8
(实际上这里唯一有效的字符是 ASCII 数字)
- 如果您有 2 个字节并且它们都不是 NUL - 它是UTF-8
(那些必须是 ASCII 数字,没有前导 '0' {}
、[]
或""
)
- 如果你有 2 个字节并且只有第一个是 NUL - 它是UTF-16BE
(它必须是编码为 UTF-16,大端序的 ASCII 数字)
- 如果你有 2 个字节并且只有第二个是 NUL - 它是UTF-16LE
(它必须是编码为 UTF-16 的 ASCII 数字,小端序)
- 如果您有 3 个字节并且它们不是 NUL - 它是UTF-8
(同样,ASCII 数字没有前导 '0' "x"
,[1]
等等)
- 如果您有 4 个字节或超过 RFC 方法的工作量:
00 00 00 xx
- 它是 UTF-32BE
00 xx 00 xx
- 它是 UTF-16BE
xx 00 00 00
- 它是 UTF-32LE
xx 00 xx 00
- 它是 UTF-16LE
xx xx xx xx
- 它是 UTF-8
但它只有在它确实是任何这些编码中的有效字符串时才有效,它可能不是。此外,即使您有 5 种有效编码之一的有效字符串,它也可能不是有效的 JSON。
我的建议是进行比 RFC 中包含的更严格的验证,以验证您是否拥有:
- UTF-8、UTF-16 或 UTF-32(LE 或 BE)的有效编码
- 一个有效的 JSON
仅查找 NUL 字节是不够的。
话虽如此,您在任何时候都不需要任何 BOM 字符来确定编码,也不需要 MIME 字符集- 这两者在 JSON 中都不需要且无效。
您只需要在使用 UTF-16 和 UTF-32 时使用二进制内容传输编码,因为它们可能包含 NUL 字节。UTF-8 没有这个问题,而且 8 位内容传输编码很好,因为它在字符串中不包含 NUL(尽管它仍然包含字节 >= 128,所以 7 位传输将不起作用 - 有 UTF- 7 适用于这种传输,但它不是有效的 JSON,因为它不是唯一有效的 JSON 编码之一)。
另请参阅此答案以获取更多详细信息。
回答您的后续问题
这些是正确的推论吗?
是的。
在实施符合这种解释的 Web 服务或 Web 客户端时,我会遇到问题吗?
可能,如果您与不正确的实现进行交互。为了与不正确的实现互操作,您的实现可能会忽略 BOM - 请参阅RFC 7159,第 1.8 节:
为了互操作性,解析 JSON 文本的实现可能会忽略字节顺序标记的存在,而不是将其视为错误。
此外,忽略 MIME 字符集是兼容 JSON 实现的预期行为 - 请参阅RFC 7159,第 11 节:
注意:没有为此注册定义“charset”参数。添加一个确实对合规收件人没有影响。
安全注意事项
我个人并不认为总是需要默默地接受不正确的 JSON 流。如果您决定接受带有 BOM 和/或 MIME 字符集的输入,那么您必须回答这些问题:
- 如果 MIME 字符集和实际编码不匹配怎么办?
- 如果 BOM 和 MIME 字符集不匹配怎么办?
- 如果 BOM 与实际编码不匹配怎么办?
- 当它们都不同时该怎么办?
- 如何处理 UTF-8/16/32 以外的编码?
- 您确定所有安全检查都会按预期进行吗?
在三个独立的地方定义编码——在 JSON 字符串本身、BOM 和 MIME 字符集中,这使得问题不可避免:如果他们不同意该怎么办。除非你拒绝这样的输入,否则没有一个明显的答案。
例如,如果您有一个验证 JSON 字符串的代码,以查看在 JavaScript 中对其进行评估是否安全 - 它可能会被 MIME 字符集或 BOM 误导,并将其视为与实际不同的编码而不检测字符串它会检测它是否使用了正确的编码。(过去 HTML 的一个类似问题导致了 XSS 攻击。)
每当您决定接受具有多个且可能冲突的编码指示符的错误 JSON 字符串时,您必须为所有这些可能性做好准备。这并不是说您永远不应该这样做,因为您可能需要使用由不正确的实现生成的输入。我只是说你需要彻底考虑其中的含义。
不合格的实现
我应该针对违反上述两个属性的网络浏览器提交错误吗?
当然——如果他们称它为 JSON 并且实现不符合 JSON RFC,那么这是一个错误,应该这样报告。
您是否发现任何不符合 JSON 规范的特定实现,但他们宣传这样做?