Однажды солнечным утром я сидел перед своим ноутбуком и перестраивал какой-то код на C#. Всё идёт хорошо, и день будет продуктивным. Потом я добавил слишком много знаков равенства в постоянный литерал строки, и всё взорвалось. Продуктивность исчезла. Воскресная спокойная реконструкция позади. Даже солнце решило спрятаться за облаками.
Потратив 30–40 минут, пытаясь понять, что я делаю не так, я понял, что дело не во мне. Это Microsoft. Похоже, я наткнулся на старый баг в функции декодирования Base64.Convert.fromBase64StringЭтот баг определённо существует с момента появления .NET 1.1 в 2003 году. Ой! Это уже старо. И воспроизвести их несложно. Молодец:
Технически, это незаконная Base64. Юридическая версия — «abc=». Обратите внимание, что только один заполнительный символ =. Кодирование Base64 представляет каждые 6 бит бинарного входа в одном ASCII-символе. Это означает, что каждые 4 символа в строке, закодированной Base64, представляют собой 3 байта. Если закодированные данные не кратны 3 байтам, кодировщик Base64 добавляет заполняющий символ, чтобы Base64 становился кратным 4 символам. Это сгенерирует строку Base64, которая правильно заполняется "abc=". Добавление ещё одного = аннулирует его.
Base64 «abc=» декодируется в виде двух байт [105, 183]. Это правильно. Добавление ещё одного символа заполнения в конце не должно сильно менять закодированное значение. Это как добавить пробел в конце предложения. Да, оно есть, но это не меняет смысл предложения. Но .NET так не считает. «abc==» декодируется в виде байта [109]. Не только он стал короче, что было странно, потому что мы сделали ввод длиннее. Он также изменился. Первый байт идёт от 105 до 109. И это не было исключением. Добавьте ещё = — и получите исключение. Удивительный!
Код:
Выпуск:
'abc=' -> [105, 183] 'abc==' -> [109] Удивительно, что никто не замечал этого столько лет. Или её нашли, но не исправили. Base64 очень важен для обмена информацией в сети. Его можно увидеть повсюду. Однако .NET уже много лет не избавляется от неисправного декодера Base64.
Сначала я не мог поверить и начал расследование. Я некоторое время гуглил, но ничего не нашёл. Потом я написал на StackOverflow и тоже особо не повезло. Когда я понял, что происходит, мне даже пришлось отвечать на свои собственные вопросы. После некоторого поиска на GitHub я наткнулся на исправление, сделанное в .NET Core в июле 2018 года. Поэтому последняя версия .NET Core корректно решает эту проблему и выдает исключение:
Необработанное исключение: System.FormatException: Вход не является действительной строкой Base-64, так как содержит не-базовый символ 64, более двух символов заполнения или нелегальный символ среди Наполняющие персонажей. at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength) на System.Convert.FromBase64String(String s) на Program.DecodeAndPrint (String base64) в ./base64/Program.cs:строке 13 на Program.Main() в ./base64/Program.cs:строка 8 Им понадобилось около 15 лет, чтобы найти и исправить проблему. Интересно, что никто особо не пытался это исправить. Это произошло, когда они переписали код, чтобы ускорить процесс:
В Convert.FromBase64() есть тонкая ошибка, когда второй нелегально наполненный символ в конце строки вызывает «успех» декодирования за счёт удаления предпоследнего символа.
Мы находимся на . Эта ошибка была случайно исправлена при оптимизации API в NetCore 2.1. Добавьте тесты к ошибкам журнала и убедитесь, что мы не возвращаемся назад. Итак, эта проблема решена в .NET Core 2.2. Но в текущей последней версии .NET Framework 4.7.2 проблемы всё ещё есть. Похоже, что он сломался и в моно.
Обходной путь в .NET 4.7.2 — заполнить неправильно заполненную строку примерно таким:
Исходный текст:Вход по гиперссылке виден.
|