Почему двухбайтовые коды UTF-8 охватывают диапазон от U+0080 до U+07FF, а не до U+087F?

Почему в UTF-8 диапазон двухбайтовых кодов начинается с C2 80, а не с C0 80? Объяснение ошибки «overlong encoding» и гарантии самосинхронизации

UTF-8 — универсальный стандарт кодирования символов, который использует от 1 до 4 байтов для представления символов Юникода. При этом для кодов от U+0080 до U+07FF предусмотрено использование именно двухбайтовых последовательностей. Возникает вопрос: почему допустимый диапазон двухбайтовых кодов начинается не с байта C0 80, а с C2 80? Почему первые два возможных префиксных байта C0 и C1 считаются недопустимыми, хотя формально они бы кодировали значения из этого диапазона?

Проблема: что такое «overlong encoding» и как связана с «самосинхронизацией»?

Рассмотрим подробнее причину. UTF-8 строится так, чтобы каждый символ имел лишь одно уникальное кодирование, и чтобы поток байтов можно было читать надёжно, даже если начало чтения смещено на середину последовательности. Важные свойства UTF-8:

  • Уникальность кодировки: каждый символ Unicode представлен одним и только одним набором байтов.
  • Самосинхронизация: если процесс чтения байтов начнётся внутри многобайтового кода (например, со второй или третьей части), декодер легко найдёт начало следующего символа.

Если бы двухбайтовые коды могли начинаться с C0 80 или C1 xx, это позволило бы создавать overlong encodings — ситуаци, когда символ мог быть записан с избыточным числом байтов, например:

C0 80 — кодирование символа U+0000 (нуль согласно стандарту), 
хотя для U+0000 корректным является один байт 00.

Подобные overlong encoding опасны, потому что они нарушают однозначность кодировки. Это создаёт проблемы безопасности, особенно в системах, где проверка на валидность символов или их фильтрация зависят от правильной декодировки.

Почему в UTF-8 запрещены C0 и C1?

Два первых байта в диапазоне C0 и C1 соответствуют четырём самым младшим битам кодовой точки. Но, если использовать байты C0 или C1 с продолжением 10xxxxxx (формат UTF-8), они приводят к «overlong» кодировке символов с кодами, которые можно было бы записать короче.

Поэтому в спецификации UTF-8 байты C0 и C1 считаются запрещёнными как начальные байты двухбайтовых последовательностей. Это помогает гарантировать:

  • Уникальность каждого кодирования символа.
  • Отсутствие проблем с переполнением при проверке кодов.

Как работает «самосинхронизация» в UTF-8?

Самосинхронизация означает, что если при чтении байтов поток начинается не с начала символа, а с середины (то есть с «продолжающего» байта, у которого старшие два бита 10), декодер сможет найти начало следующего правильного символа, ориентируясь на байты с паттернами 0xxxxxxx (однобайтный символ) и 11xxxxxx (начальный байт многобайтного символа).

Но если бы разрешить C0 80 и C1 xx, то возможны неоднозначности и нарушения правил синхронизации при ошибках в потоке. Запрет этих значений избавляет от лишних погрешностей и повышает устойчивость к ошибкам передачи.

Резюме: варианты и рекомендации

Вариант 1: начинать диапазон двухбайтовых кодов с C0 80, разрешая overlong encodings.

  • Это может привести к неоднозначной и небезопасной кодировке символов.
  • Нарушает уникальность представления и усложняет проверку на валидность.
  • Может ухудшить защиту от уязвимостей типа инъекций или обхода фильтров.

Вариант 2 (стандартный): начинать с C2 80, исключая C0 и C1.

  • Гарантирует уникальность кодировки каждого символа.
  • Повышает точность и устойчивость декодера.
  • Обеспечивает защиту от атак, связанных с overlong encoding.
  • Поддерживает самосинхронизацию и надёжность обработки данных.

Выводы

Исключение первых двух возможных начальных байтов C0 и C1 как валидных для двухбайтовых кодов — это осознанное решение в стандарте UTF-8. Оно направлено на предотвращение overlong encodings, которые не гарантируют однозначности и безопасности обработки текста. Такой подход обеспечивает стабильную и предсказуемую работу систем, где применяется UTF-8, а также упрощает обработку ошибок и повышает надёжность «самосинхронизации» при чтении данных.

Если вы работаете с проверкой или синтезом UTF-8, строго придерживайтесь стандарта, запрещающего C0 и C1 в начале двухбайтовых последовательностей, чтобы обеспечить безопасность и совместимость вашего кода.

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *