Programmering

Sikkerhed og klassifikator

Denne måneds artikel fortsætter diskussionen af ​​Java's sikkerhedsmodel, der blev indledt i august "Under the Hood". I den artikel gav jeg et generelt overblik over de sikkerhedsmekanismer, der er indbygget i Java virtual machine (JVM). Jeg kiggede også nøje på et aspekt af disse sikkerhedsmekanismer: JVM's indbyggede sikkerhedsfunktioner. I september's "Under the Hood" undersøgte jeg klasselæsserarkitekturen, et andet aspekt af JVM's indbyggede sikkerhedsmekanismer. Denne måned vil jeg fokusere på den tredje tand i JVM's sikkerhedsstrategi: klassifikatoren.

Klassefilverifikatoren

Hver virtuel Java-maskine har en klassefilverifikator, som sikrer, at indlæste klassefiler har en korrekt intern struktur. Hvis klassefilverifikatoren opdager et problem med en klassefil, kaster den en undtagelse. Fordi en klassefil kun er en sekvens af binære data, kan en virtuel maskine ikke vide, om en bestemt klassefil blev genereret af en velmenende Java-kompilator eller af skyggefulde crackere, der er bundet til at kompromittere integriteten af ​​den virtuelle maskine. Som en konsekvens har alle JVM-implementeringer en klassefilverifikator, der kan påberåbes på ikke-tillid til klasser for at sikre, at klasserne er sikre at bruge.

Et af de sikkerhedsmål, som klassefilverifikatoren hjælper med at nå, er programmets robusthed. Hvis en buggy-kompilator eller kyndig krakker genererede en klassefil, der indeholdt en metode, hvis bytekoder indeholdt en instruktion om at springe ud over slutningen af ​​metoden, kunne denne metode, hvis den blev påberåbt, få den virtuelle maskine til at gå ned. Af hensyn til robusthed er det således vigtigt, at den virtuelle maskine verificerer integriteten af ​​de bytekoder, den importerer.

Selvom designere af virtuelle Java-maskiner har lov til at beslutte, hvornår deres virtuelle maskiner skal udføre disse kontroller, foretager mange implementeringer mest kontrol lige efter, at en klasse er indlæst. En sådan virtuel maskine analyserer bytekoder (og verificerer deres integritet) en gang, før de nogensinde udføres. Som en del af sin verifikation af bytecodes sørger den virtuelle Java-maskine for, at alle springinstruktioner - for eksempel gå til (spring altid), ifeq (spring, hvis toppen af ​​stakken er nul) osv. - forårsage et spring til en anden gyldig instruktion i metatens bytecode-strøm. Som en konsekvens behøver den virtuelle maskine ikke kontrollere et gyldigt mål hver gang den støder på en springinstruktion, når den udfører bytecodes. I de fleste tilfælde er det en mere effektiv måde at sikre robusthed end at kontrollere hver bytecode-instruktion hver gang den udføres, at kontrollere alle bytekoder en gang, før de udføres.

En klassefilverifikator, der udfører sin kontrol så tidligt som muligt, fungerer sandsynligvis i to forskellige faser. Under fase et, der finder sted lige efter, at en klasse er indlæst, kontrollerer klassefilverifikatoren den interne struktur for klassefilen, herunder verificering af integriteten af ​​de bytekoder, den indeholder. Under fase to, der finder sted, når bytekoder udføres, bekræfter klassefilverifikatoren eksistensen af ​​symbolsk refererede klasser, felter og metoder.

Fase et: Intern kontrol

I fase 1 kontrollerer klassefilverifikatoren alt, hvad der er muligt at kontrollere i en klassefil ved kun at se på selve klassefilen (uden at undersøge andre klasser eller grænseflader). Fase en af ​​klassefilverifikatoren sørger for, at den importerede klassefil er korrekt dannet, internt konsistent, overholder begrænsningerne i Java-programmeringssproget og indeholder bytekoder, der er sikre for den virtuelle Java-maskine at udføre. Hvis klassefilverifikatoren finder ud af, at nogen af ​​disse ikke er rigtige, kaster den en fejl, og klassefilen bruges aldrig af programmet.

Kontrol af format og intern konsistens

Udover at kontrollere bytekodernes integritet udfører verifikatoren mange kontroller for korrekt klassefilformat og intern konsistens i fase 1. For eksempel skal hver klassefil starte med de samme fire byte, det magiske tal: 0xCAFEBABE. Formålet med magiske tal er at gøre det let for filparsere at genkende en bestemt type fil. Således er det første, som en klassefilverifikator sandsynligvis kontrollerer, at den importerede fil faktisk begynder med 0xCAFEBABE.

Klassefilverifikatoren kontrollerer også for at sikre, at klassefilen hverken er afkortet eller forbedret med ekstra efterfølgende byte. Selvom forskellige klassefiler kan have forskellige længder, angiver hver enkelt komponent indeholdt i en klassefil dens længde såvel som dens type. Verifikatoren kan bruge komponenttyperne og længderne til at bestemme den korrekte samlede længde for hver individuelle klassefil. På denne måde kan den kontrollere, at den importerede fil har en længde, der er i overensstemmelse med dens interne indhold.

Verifikatoren ser også på individuelle komponenter for at sikre, at de er velformede forekomster af deres type komponent. For eksempel er en metodebeskrivelse (metodens returtype og antallet og typerne af dens parametre) gemt i klassefilen som en streng, der skal overholde en bestemt kontekstfri grammatik. En af de kontroller, som verifikatoren udfører på individuelle komponenter, er at sikre, at hver metodebeskrivelse er en velformet streng af den relevante grammatik.

Derudover kontrollerer klassefilverifikatoren, at klassen selv overholder visse begrænsninger, der er pålagt den ved specifikationen af ​​Java-programmeringssproget. For eksempel håndhæver verifikatoren reglen om, at alle klasser undtagen klasse Objekt, skal have en superklasse. Klassefilverifikatoren kontrollerer således ved kørsel nogle af de Java-sprogregler, der skulle have været håndhævet på kompileringstidspunktet. Da verifikatoren ikke har nogen måde at vide, om klassefilen blev genereret af en velvillig, fejlfri kompilator, kontrollerer den hver klassefil for at sikre, at reglerne følges.