|
|
Postat pe 31.07.2023 18:36:01
|
|
|
|

Într-o dimineață însorită, stăteam în fața laptopului meu și restructuram niște cod C#. Totul merge bine și va fi o zi productivă. Apoi am adăugat prea multe semne egale la șirul constant literal și lucrurile au explodat. Productivitatea a dispărut. Reconstrucția calmă de duminică a rămas în urmă. Chiar și soarele a decis să se ascundă în spatele norilor.
După ce am petrecut 30-40 de minute încercând să-mi dau seama ce greșesc, mi-am dat seama că nu era vina mea. E Microsoft. Se pare că am dat peste un bug vechi în funcția de decodare Base64.Convert.FromBase64StringAcest bug există cu siguranță încă de la introducerea .NET 1.1 în 2003. Wow! Asta e vechi. Și nu este foarte greu de reprodus. Bună treabă:
Din punct de vedere tehnic, asta e ilegal, Base64. Versiunea legală este "abc=". Observați că doar un caracter de umplutură =. Codificarea Base64 reprezintă fiecare 6 biți de intrare binară într-un caracter ASCII. Aceasta înseamnă că fiecare 4 caractere dintr-un șir codificat în Base64 reprezintă 3 octeți. Când datele codificate nu sunt un multiplu de 3 octeți, encoderul Base64 adaugă un caracter de umplutură pentru a face ca Base64 să fie un multiplu de 4 caractere. Aceasta va genera un șir Base64 care este completat corect cu "abc=". Adăugarea altei = îl va invalida.
Base64 "abc=" este decodat ca doi octeți [105, 183]. Așa este. Adăugarea unui alt caracter de umplutură la final nu ar trebui să schimbe cu adevărat valoarea codificată. E ca și cum ai adăuga un spațiu la finalul unei propoziții. Da, este acolo, dar nu schimbă sensul propoziției. Dar .NET nu crede asta. "abc==" este decodat ca un octet [109]. Nu doar că a devenit mai scurt, ceea ce a fost ciudat pentru că am făcut inputul mai lung. De asemenea, a devenit diferit. Primul octet merge de la 105 la 109. Și nici nu a făcut o excepție. Adaugă încă un = și obții o excepție. Uimitor!
Cod:
Ieşire:
'abc=' -> [105, 183] 'abc==' -> [109] Este cu adevărat uimitor că nimeni nu a observat asta de atâția ani. Sau a fost găsit, dar nu a fost reparat. Base64 este foarte important în schimbul de informații pe rețea. Se poate vedea peste tot. Totuși, .NET nu a eliminat decodorul Base64 defect de ani de zile.
La început nu-mi venea să cred și am început să investighez. Am căutat pe Google o vreme și nu am găsit prea multe. Apoi am postat pe StackOverflow și nici eu nu am avut prea mult noroc. Odată ce am înțeles ce se întâmplă, a trebuit chiar să-mi răspund singur la întrebări. După ce am căutat o vreme pe GitHub, am dat peste o soluție făcută în .NET Core în iulie 2018. Deci cea mai recentă versiune .NET Core gestionează corect această problemă și face o excepție:
Excepție negestionată: System.FormatException: Intrarea nu este un șir valid Base-64 deoarece conține un caracter non-base 64, mai mult de două caractere de umplutură sau un caracter ilegal printre Personaje umplute. la System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength) la System.Convert.FromBase64String(String s) la Program.DecodeAndPrint(String base64) în ./base64/Program.cs:line 13 la Program.Main() în ./base64/Program.cs:linia 8 Le-a luat cam 15 ani să găsească și să rezolve problema. Interesant este că nimeni nu a încercat cu adevărat să o repare în mod specific. Acest lucru s-a întâmplat când au rescris codul pentru a-l face mai rapid:
Convert.FromBase64() are un bug subtil în care al doilea caracter umplut ilegal la finalul șirului face ca decodarea să "reușească" prin eliminarea penultimului caracter.
Suntem la . Această eroare a fost corectată accidental când API-ul a fost optimizat în NetCore 2.1. Adaugă teste la erorile de înregistrare și asigură-te că nu dăm înapoi. Deci această problemă este rezolvată în .NET Core 2.2. Dar în cea mai recentă versiune a .NET Framework 4.7.2 încă are probleme. Se pare că este stricat și în Mono.
Soluția din .NET 4.7.2 este să reumpli șirul populat incorect cu ceva de genul acesta:
Original:Autentificarea cu hyperlink este vizibilă.
|
Precedent:Azure DevOps (viii) Compilează ASP.NET proiecte MVC folosind Pipelines BuildUrmător:Este folosit un nou cronometru în .NET 6, PeriodicTimer,
|