Една слънчева сутрин седях пред лаптопа си и преструктурирах някакъв 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 от години.
Първоначално не можех да повярвам и започнах да разследвам. Търсих в Google известно време и не намерих много. После публикувах в 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 е да се запълни неправилно попълненият низ с нещо такова:
Оригинален:Входът към хиперлинк е видим.
|