Programmering

Sikkerhet og klasseverifisereren

Denne månedens artikkel fortsetter diskusjonen om Javas sikkerhetsmodell som ble startet i august "Under hetten." I den artikkelen ga jeg en generell oversikt over sikkerhetsmekanismene innebygd i Java virtual machine (JVM). Jeg så også nøye på ett aspekt av disse sikkerhetsmekanismene: JVMs innebygde sikkerhetsfunksjoner. I september 'Under the Hood' undersøkte jeg klasselasterarkitekturen, et annet aspekt av JVMs innebygde sikkerhetsmekanismer. Denne måneden vil jeg fokusere på den tredje spissen i JVMs sikkerhetsstrategi: klasseverifisereren.

Klassefilverifisereren

Hver virtuell Java-maskin har en klassefilverifiser, som sørger for at lastede klassefiler har riktig intern struktur. Hvis klassefilverifisereren oppdager et problem med en klassefil, gir det et unntak. Fordi en klassefil bare er en sekvens av binære data, kan en virtuell maskin ikke vite om en bestemt klassefil ble generert av en velmenende Java-kompilator eller av skyggefulle kjeks som er bøyd på å kompromittere integriteten til den virtuelle maskinen. Som en konsekvens har alle JVM-implementeringer en klassefilverifiser som kan påberopes på ikke-klarerte klasser for å sikre at klassene er trygge å bruke.

Et av sikkerhetsmålene som klassefilverifisereren hjelper til med å oppnå er programmets robusthet. Hvis en buggy-kompilator eller kunnskapsrik cracker genererte en klassefil som inneholdt en metode hvis bykoder inkluderte en instruksjon om å hoppe utover slutten av metoden, kan den metoden, hvis den ble påkalt, få den virtuelle maskinen til å krasje. Derfor er det av hensyn til robusthet viktig at den virtuelle maskinen verifiserer integriteten til bykodene den importerer.

Selv om designere av virtuelle Java-maskiner har lov til å bestemme når de virtuelle maskinene deres skal utføre disse kontrollene, vil mange implementeringer sjekke mest like etter at en klasse er lastet. En slik virtuell maskin analyserer bykoder (og verifiserer integriteten) en gang, før de noen gang blir utført. Som en del av bekreftelsen av bytekoder sørger den virtuelle Java-maskinen for at alle hoppinstruksjoner - for eksempel gå til (hopp alltid), ifeq (hopp hvis toppen av stakken er null), etc. - forårsake et hopp til en annen gyldig instruksjon i bytekodestrømmen til metoden. Som en konsekvens trenger ikke den virtuelle maskinen å sjekke om det er et gyldig mål hver gang den møter en hoppinstruksjon når den kjører bytekoder. I de fleste tilfeller er det en mer effektiv måte å sikre robusthet enn å sjekke hver bytekodeinstruksjon hver gang den kjøres, å sjekke alle bytekoder en gang før de kjøres.

En klassefilverifiser som utfører kontrollen så tidlig som mulig, opererer sannsynligvis i to forskjellige faser. I løpet av fase en, som finner sted like etter at en klasse er lastet, sjekker klassefilverifisereren den interne strukturen til klassefilen, inkludert verifisering av integriteten til bytekodene den inneholder. I løpet av fase to, som foregår når bytekoder utføres, bekrefter klassefilverifisereren eksistensen av symbolsk refererte klasser, felt og metoder.

Fase en: Interne kontroller

I løpet av fase en sjekker klassefilverifisereren alt som er mulig å sjekke inn en klassefil ved å se på bare klassefilen selv (uten å undersøke noen andre klasser eller grensesnitt). Fase en av klassefilverifisereren sørger for at den importerte klassefilen er riktig utformet, internt konsistent, overholder begrensningene til Java-programmeringsspråket, og inneholder bytekoder som er trygge for den virtuelle Java-maskinen å utføre. Hvis klassefilverifisereren finner ut at noen av disse ikke stemmer, kaster det en feil, og klassefilen brukes aldri av programmet.

Kontrollerer format og intern konsistens

Foruten å verifisere integriteten til bytekodene, utfører verifikatoren mange kontroller for riktig klassefilformat og intern konsistens i løpet av fase en. For eksempel må hver klassefil starte med de samme fire byte, det magiske tallet: 0xCAFEBABE. Hensikten med magiske tall er å gjøre det enkelt for filparsere å gjenkjenne en bestemt type fil. Dermed er det første en klassefilverifiserer sannsynligvis sjekker at den importerte filen faktisk begynner med 0xCAFEBABE.

Klassefilverifisereren kontrollerer også for å sikre at klassefilen verken er avkortet eller forbedret med ekstra etterfølgende byte. Selv om forskjellige klassefiler kan være forskjellige lengder, indikerer hver enkelt komponent i en klassefil lengden så vel som typen. Verifisereren kan bruke komponenttypene og lengdene til å bestemme riktig total lengde for hver enkelt klassefil. På denne måten kan den bekrefte at den importerte filen har en lengde som samsvarer med det interne innholdet.

Kontrolløren ser også på individuelle komponenter for å sikre at de er velformede forekomster av deres type komponent. For eksempel lagres en metodebeskrivelse (metodens returtype og antall og typer parametere) i klassefilen som en streng som må følge en viss kontekstfri grammatikk. En av kontrollene verifisereren utfører på individuelle komponenter, er å sørge for at hver metodebeskrivelse er en velformet streng av riktig grammatikk.

I tillegg kontrollerer klassefilverifisereren at klassen selv overholder visse begrensninger som er plassert av den ved spesifikasjonen av Java-programmeringsspråket. For eksempel håndhever bekrefteren regelen om at alle klasser, unntatt klasse Gjenstand, må ha en superklasse. Dermed sjekker klassefilverifisereren ved kjøretid noen av Java-språkreglene som burde vært håndhevet på kompileringstidspunktet. Fordi verifisereren ikke har noen måte å vite om klassefilen ble generert av en velvillig, feilfri kompilator, sjekker den hver klassefil for å sikre at reglene følges.

$config[zx-auto] not found$config[zx-overlay] not found