|
|
Opublikowano 31.07.2023 18:36:01
|
|
|
|

Pewnego słonecznego poranka siedziałem przed laptopem, restrukturyzując kod w C#. Wszystko idzie dobrze i zapowiada się produktywny dzień. Potem dodałem zbyt wiele równych do dosłownego ciągu stałego i wszystko eksplodowało. Produktywność zniknęła. Niedzielna spokojna odbudowa jest już za nami. Nawet słońce postanowiło schować się za chmurami.
Po spędzeniu 30 do 40 minut próbując zrozumieć, co robię źle, zrozumiałem, że to nie moja wina. To Microsoft. Najwyraźniej natknąłem się na stary błąd w funkcji dekodowania Base64.Convert.FromBase64StringTen błąd zdecydowanie istnieje od czasu wprowadzenia .NET 1.1 w 2003 roku. Wow! To już stare. I nie jest trudno go rozmnażać. Dobra robota:
Technicznie rzecz biorąc, to jest nielegalny Base64. Wersja prawna to "abc=". Zauważ, że tylko jeden znak wypełniacz =. Kodowanie Base64 reprezentuje co 6 bitów binarnego wejścia w jednym znaku ASCII. Oznacza to, że każde 4 znaki w ciągu zakodowanym w Base64 reprezentują 3 bajty. Gdy zakodowane dane nie są wielokrotnością 3 bajtów, enkoder Base64 dodaje znak wypełniający, aby Base64 był wielokrotnością 4 znaków. To wygeneruje ciąg Base64, który jest prawidłowo wypełniony liczbą "abc=". Dodanie kolejnego = unieważni ją.
Base64 "abc=" jest dekodowane jako dwa bajty [105, 183]. Zgadza się. Dodanie kolejnego znaku dosypującego na końcu nie powinno zmieniać zakodowanej wartości. To jak dodanie spacji na końcu zdania. Tak, jest, ale nie zmienia znaczenia zdania. Ale .NET tak nie uważa. "abc==" jest dekodowane jako bajt [109]. Nie tylko się skrócił, co było dziwne, bo wydłużaliśmy wejścia. Stało się też inne. Pierwszy bajt przechodzi z 105 do 109. I nie wyrzucało też wyjątku. Dodaj kolejny = i otrzymasz wyjątek. Zdumiewający!
Kod:
Wyjście:
'abc=' -> [105, 183] 'abc==' -> [109] To naprawdę niesamowite, że nikt tego nie zauważył przez tyle lat. Albo został znaleziony, ale nie naprawiony. Base64 odgrywa ogromne znaczenie w wymianie informacji w sieci. Można go zobaczyć wszędzie. Jednak .NET od lat nie pozbył się wadliwego dekodera Base64.
Na początku nie mogłem w to uwierzyć i zacząłem to sprawdzać. Szukałem w Google przez jakiś czas i niewiele znalazłem. Potem napisałem na StackOverflow i też nie miałem zbyt wiele szczęścia. Gdy już zrozumiałem, co się dzieje, musiałem nawet sam odpowiadać na swoje pytania. Po dłuższym szukaniu na GitHubie natknąłem się na poprawkę wprowadzoną w .NET Core w lipcu 2018 roku. Najnowsza wersja .NET Core poprawnie radzi sobie z tym problemem i zawiera wyjątek:
Nieobsługiwany wyjątek: System.FormatWyjątek: Wejście nie jest ważnym łańcuchem Base-64, ponieważ zawiera znak niebędący bazą 64, więcej niż dwa znaki dosypujące lub nielegalny znak wśród postacie wypełniające posiłek. at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength) at System.Convert.FromBase64String(String s) at Program.DecodeAndPrint (String base64) w ./base64/Program.cs:linia 13 at Program.Main() w ./base64/Program.cs:line 8 Zajęło im około 15 lat, by znaleźć i naprawić problem. Co ciekawe, nikt tak naprawdę nie próbował tego naprawić konkretnie. Stało się tak, gdy przepisali kod, żeby był szybszy:
Convert.FromBase64() ma subtelny błąd, gdzie drugi nielegalnie dopełniony znak na końcu ciągu powoduje "sukces" dekodowania poprzez usunięcie przedostatniego znaku.
Jesteśmy na . Ten błąd został przypadkowo naprawiony, gdy API zostało zoptymalizowane w NetCore 2.1. Dodaj testy, aby rejestrować błędy i upewnij się, że nie cofamy się do końca. Ten problem został naprawiony w .NET Core 2.2. Jednak w najnowszej wersji .NET Framework 4.7.2 nadal występują problemy. Wygląda na to, że jest zepsute też w mono.
Obejście w .NET 4.7.2 polega na uzupełnieniu błędnie wypełnionego ciągu czymś takim:
Oryginał:Logowanie do linku jest widoczne.
|
Poprzedni:Azure DevOps (viii) Kompiluje ASP.NET projekty MVC z użyciem Pipelines BuildNastępny:Nowy timer w .NET 6, PeriodicTimer, jest używany
|