Спецификация JSON и использование кодировки BOM/charset

Я читал в спецификации RFC-4627, и я пришел к интерпретации:

При рекламе полезной нагрузки как application/json mime-type,

  • там MUST не должно быть BOM в начале правильно закодированных потоков JSON (на основе раздела "3. Кодирование" ) и
  • поддерживаются никакие параметры мультимедиа, поэтому заголовок mime-типа application/json; charset=utf-8 не соответствует не RFC-4627 (на основе раздела "6. Соображения IANA" ).

Являются ли эти правильные выводы? Будет ли я сталкиваться с проблемой при внедрении веб-сервисов или веб-клиентов, которые придерживаются этих интерпретаций? Должен ли я регистрировать ошибки в отношении веб-браузеров, которые нарушают два свойства выше?

Ответ 1

Вы правы

  • Символ спецификации запрещен в JSON (и не нужен)
  • Кодировка MIME является незаконной в JSON (и не нужна также)

RFC 7159, раздел 8.1:

Реализации НЕ ДОЛЖНЫ добавлять знак байтового порядка в начало текста JSON.

Это делается так ясно, как может быть. Это единственный "НЕ ДОЛЖЕН" во всем RFC.

RFC 7159, раздел 11:

Тип мультимедиа MIME для текста JSON - application/json.
Имя типа: приложение
Имя подтипа: 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
    (это должна быть цифра ASCII, кодированная как UTF-16, большой endian).
  • если у вас есть 2 байта, а второй - NUL - UTF-16LE
    (это должна быть цифра ASCII, закодированная как UTF-16, немного endian)
  • если у вас 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, недостаточно.

Чтобы сказать, что ни в коем случае вам не нужны какие-либо спецификации, чтобы определить кодировку, вам не нужен MIME-набор символов - оба из них не нужны, а недействительны в JSON.

Вам нужно использовать двоичную кодировку передачи контента при использовании UTF-16 и UTF-32, поскольку они могут содержать NUL-байты. UTF-8 не имеет этой проблемы, и 8-битное кодирование передачи контента прекрасное, так как оно не содержит NUL в строке (хотя оно все еще содержит байты >= 128, поэтому 7-битная передача не будет работать - есть UTF- 7, который будет работать для такой передачи, но это было бы недействительным JSON, поскольку оно не является единственным допустимым кодированием JSON).

Подробнее см. этот ответ.

Отвечая на ваши последующие вопросы

Являются ли эти правильные выводы?

Да.

Я столкнулся с проблемой при внедрении веб-сервисов или веб-клиентов, которые придерживаются этих интерпретаций?

Возможно, если вы взаимодействуете с неверными реализациями. Ваша реализация МОЖЕТ игнорировать спецификацию для взаимодействия с неправильными реализациями - см. RFC 7159, раздел 1.8:

В интересах интероперабельности, реализаций что разобрать JSON-тексты МОГУТ игнорировать наличие знака порядка байтов а не рассматривать его как ошибку.

Кроме того, игнорирование кодировки MIME - это ожидаемое поведение совместимых реализаций JSON - см. RFC 7159, раздел 11:

Примечание. Для этой регистрации не задан параметр "charset". Добавление одного действительно не влияет на совместимых получателей.

Вопросы безопасности

Я лично не уверен, что всегда требуется мягкое принятие неправильных потоков JSON. Если вы решите принять ввод с кодировкой BOM и/или MIME, вам придется ответить на эти вопросы:

  • Что делать в случае несоответствия между кодировкой MIME и фактической кодировкой?
  • Что делать в случае несоответствия между кодировкой BOM и MIME?
  • Что делать в случае несоответствия между спецификацией и фактической кодировкой?
  • Что делать, если все они отличаются?
  • Что делать с кодировками, отличными от UTF-8/16/32?
  • Вы уверены, что все проверки безопасности будут работать как ожидалось?

Наличие кодировки, определенной в трех независимых местах - в самой строке JSON, в спецификации и в кодировке MIME неизбежно возникает вопрос: что делать, если они не согласны. И если вы не отклоняете такой ввод, то нет однозначного ответа.

Например, если у вас есть код, который проверяет строку JSON, чтобы убедиться в том, что она безопасна для ее оценки в JavaScript - это может быть введено в заблуждение кодировкой MIME или спецификацией, и рассматривать это как другую кодировку, чем она есть на самом деле. не обнаруживать строки, которые он обнаружил бы, если бы использовал правильную кодировку. (Аналогичная проблема с HTML привела к атакам XSS в прошлом.)

Вы должны быть готовы ко всем этим возможностям, когда вы решите принять неправильные строки JSON с несколькими и, возможно, противоречивыми индикаторами кодирования. Это не означает, что вы никогда не должны этого делать, потому что вам может потребоваться потребление, созданное с помощью неправильных реализаций. Я просто говорю, что вам нужно тщательно рассмотреть последствия.

Несоответствующие реализации

Должен ли я записывать ошибки в отношении веб-браузеров, которые нарушают два свойства выше?

Конечно, если они называют это JSON, и реализация не соответствует JSON RFC, то это ошибка и должна быть представлена ​​как таковая.

Вы нашли какие-либо конкретные реализации, которые не соответствуют спецификации JSON, и все же они рекламируют это?

Ответ 2

Я думаю, что вы правы в вопросе 1, из-за раздела 3 о первых двух символах, являющихся ASCII и unicode часто задаваемых вопросов о спецификациях, см. "В: Как мне работать с спецификациями?", Ответьте на часть 3. Ваш акцент на MUST может быть немного сильным: часто задаваемые вопросы, как представляется, СЛЕДУЕТ.

Не знаю ответа на вопрос 2.