Nekega sončnega jutra sem sedel pred prenosnikom in preurejal nekaj C# kode. Vse poteka dobro in dan bo produktiven. Potem sem dodal preveč enačbnih znakov k konstantnemu nizu literalu in stvari so eksplodirale. Produktivnost je izginila. Nedeljska mirna obnova je za nami. Tudi sonce se je odločilo, da se skrije za oblake.
Po 30 do 40 minutah, ko sem poskušal ugotoviti, kaj delam narobe, sem spoznal, da to nisem bil jaz. To je Microsoft. Očitno sem naletel na staro napako v funkciji dekodiranja Base64.Pretvori.FromBase64StringTa napaka je zagotovo prisotna že od uvedbe .NET 1.1 leta 2003. Wow! To je staro. In ni zelo težko reproducirati. Dobro opravljeno:
Tehnično gledano je to nezakonit Base64. Pravna različica je "abc=". Opazite, da je le en polnilni znak =. Base64 kodiranje predstavlja vsakih 6 bitov binarnega vhoda v enem ASCII znaku. To pomeni, da vsak 4 znak v Base64 kodiranem nizu predstavlja 3 bajte. Ko kodirani podatki niso večkratnik 3 bajtov, Base64 kodirnik doda polnilni znak, da Base64 postane večkratnik štirih znakov. To bo ustvarilo Base64 niz, ki je pravilno napolnjen z "abc=". Dodajanje še enega = ga razveljavi.
Base64 "abc=" je dekodiran kot dva bajta [105, 183]. Tako je. Dodajanje še enega polnilnega znaka na koncu ne bi smelo bistveno spremeniti kodirane vrednosti. To je kot dodajanje presledka na konec stavka. Da, je tam, a ne spremeni pomena stavka. A .NET tako ne misli. "abc==" je dekodiran kot bajt [109]. Ne samo, da je postal krajši, kar je bilo čudno, ker smo vhod podaljšali. Postala je tudi drugačna. Prvi bajt gre od 105 do 109. In ni bilo nobene izjeme. Dodaj še en = in dobiš izjemo. Neverjetno!
Koda:
Izhod:
'abc=' -> [105, 183] 'abc==' -> [109] Res je neverjetno, da tega nihče ni opazil že toliko let. Ali pa so ga našli, a niso popravili. Base64 je zelo pomemben pri izmenjavi informacij v omrežju. Videti jo je povsod. Vendar pa .NET že leta ni odstranil pomanjkljivega Base64 dekoderja.
Sprva nisem mogel verjeti in začel raziskovati. Nekaj časa sem iskal po Googlu, a nisem našel veliko. Potem sem objavil na StackOverflow in tudi nisem imel veliko sreče. Ko sem ugotovil, kaj se dogaja, sem moral celo sam odgovarjati na svoja vprašanja. Po nekaj iskanja na GitHubu sem naletel na popravek, narejen v .NET Core julija 2018. Torej najnovejša različica .NET Core to težavo pravilno obravnava in vrže izjemo:
Neobdelana izjema: System.FormatIzjema: Vhod ni veljaven niz Base-64, saj vsebuje znak, ki ni iz osnove 64, več kot dva polnilna znaka ali nedovoljen znak med Liki za oblazinjenje. at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength) at System.Convert.FromBase64String(String s) na Program.DecodeAndPrint (niz base64) v ./base64/Program.cs:vrstica 13 at Program.Main() v ./base64/Program.cs:line 8 Potrebovali so približno 15 let, da so našli in odpravili težavo. Zanimivo je, da se nihče ni zares trudil popraviti tega posebej. To se je zgodilo, ko so kodo prepisali, da bi bila hitrejša:
Convert.FromBase64() ima subtilno napako, kjer drugi nezakonito polnjen znak na koncu niza povzroči, da dekodiranje "uspe" z odstranitvijo predzadnjega znaka.
Nahajamo se na . Ta napaka je bila nehote odpravljena, ko je bil API optimiziran v NetCore 2.1. Dodajte teste za beleženje napak in poskrbite, da ne gremo nazaj. Torej je ta težava odpravljena v .NET Core 2.2. Vendar pa v najnovejši različici .NET Frameworka 4.7.2 še vedno obstajajo težave. Zdi se, da je pokvarjen tudi v Mono.
Rešitev v .NET 4.7.2 je, da napačno napolnjen niz napolnite z nečim takim:
Izvirno:Prijava do hiperpovezave je vidna.
|