Sigurnost i provjera klase

Ovomjesečni članak nastavlja raspravu o Java-ovom sigurnosnom modelu započetu u kolovozu "Under the Hood". U tom sam članku dao općeniti pregled sigurnosnih mehanizama ugrađenih u Java virtualni stroj (JVM). Također sam pomno pogledao jedan aspekt tih sigurnosnih mehanizama: ugrađene sigurnosne značajke JVM-a. U rujanskom "Under the Hood" ispitivao sam arhitekturu učitavača klasa, još jedan aspekt ugrađenih sigurnosnih mehanizama JVM-a. Ovaj mjesec usredotočit ću se na treći dio sigurnosne strategije JVM-a: provjeravač klase.

Provjerivač klase datoteka

Svaki Java virtualni stroj ima provjeru datoteka klase, što osigurava da učitane datoteke klase imaju odgovarajuću unutarnju strukturu. Ako verifikator datoteke klase otkrije problem s datotekom klase, izbacuje iznimku. Budući da je datoteka klase samo slijed binarnih podataka, virtualni stroj ne može znati je li određenu datoteku klase generirao dobronamjerni Java-ov kompajler ili sjenoviti krekeri koji teže da ugroze integritet virtualnog stroja. Kao posljedica toga, sve JVM implementacije imaju provjeru datoteke klase koja se može pozvati na nepouzdanim klasama kako bi se osiguralo da su klase sigurne za upotrebu.

Jedan od sigurnosnih ciljeva koje verifikator datoteka pomaže postići je robusnost programa. Ako je sastavljač programskih pogrešaka ili pametni kreker generirao datoteku klase koja je sadržavala metodu čiji su bajtkodovi sadržavali naredbu za preskakanje dalje od kraja metode, ta bi metoda mogla, ako je pozvana, prouzročiti pad sustava. Stoga je, zbog robusnosti, važno da virtualni stroj provjeri integritet bytecode-a koje uvozi.

Iako dizajneri Java virtualnih strojeva smiju odlučiti kada će njihovi virtualni strojevi izvršiti ove provjere, mnoge će implementacije većinu provjera obaviti odmah nakon učitavanja klase. Takav virtualni stroj jednom analizira bytecode (i provjerava njihov integritet) prije nego što se ikad izvrše. Kao dio provjere bajt kodova, Java virtualni stroj osigurava sve upute za preskakanje - na primjer, goto(uvijek preskoči),ifeq(skok ako je vrh steka nula), itd. - izazovite skok na drugu važeću naredbu u toku bajtkoda metode. Kao posljedica toga, virtualni stroj ne mora provjeravati valjanu metu svaki put kad naiđe na naredbu za skok dok izvršava bajt kodove. U većini slučajeva provjera svih bajt kodova prije nego što se izvrše učinkovitiji je način zajamčiti robusnost od provjere svake naredbe bajt koda svaki put kad se izvrši.

Provjerivač klase datoteka koji provjeru vrši što je prije moguće, najvjerojatnije djeluje u dvije različite faze. Tijekom prve faze, koja se odvija neposredno nakon učitavanja klase, verifikator datoteke klase provjerava unutarnju strukturu datoteke klase, uključujući provjeru integriteta bytecode-a koje sadrži. Tijekom druge faze, koja se odvija dok se izvršavaju bytecode-ovi, provjerivač klase datoteke potvrđuje postojanje simbolički referenciranih klasa, polja i metoda.

Prva faza: Interne provjere

Tijekom prve faze provjerivač klase provjerava sve što je moguće provjeriti u datoteci klase gledajući samo samu datoteku klase (bez ispitivanja bilo koje druge klase ili sučelja). Prva faza provjere datoteke klase osigurava da je uvezena datoteka klase pravilno oblikovana, interno konzistentna, pridržava se ograničenja Java programskog jezika i sadrži bajtkodove koji će biti sigurni za izvršavanje Java virtualnog stroja. Ako verifikator datoteke klase utvrdi da bilo što od toga nije istina, izbacuje pogrešku i program nikada ne koristi datoteku klase.

Provjera formata i unutarnje dosljednosti

Osim provjere integriteta bajt kodova, verifikator tijekom prve faze provodi mnoge provjere ispravnog formata datoteke klase i unutarnje dosljednosti. Na primjer, svaki razred datoteka mora početi s istim četiri bajta, magični broj: 0xCAFEBABE. Svrha magičnih brojeva je olakšati analizatorima datoteka prepoznavanje određene vrste datoteka. Dakle, prva stvar koju vjerodostojnik provjerava datoteke je da uvezena datoteka doista započinje 0xCAFEBABE.

Provjerivač klase datoteke također provjerava je li datoteka klase niti skraćena niti poboljšana dodatnim pratećim bajtovima. Iako različite datoteke klase mogu biti različite duljine, svaka pojedinačna komponenta sadržana unutar datoteke klase označava njezinu duljinu kao i vrstu. Provjerivač može koristiti vrste i duljine komponenata kako bi odredio točnu ukupnu duljinu za svaku pojedinu datoteku razreda. Na taj način može provjeriti ima li uvozna datoteka duljinu koja je u skladu s njezinim internim sadržajem.

Verifikator također proučava pojedine komponente kako bi se uvjerio da su to dobro oblikovani primjerci njihove vrste komponenata. Na primjer, opisnik metode (vrsta povrata metode i broj i tipovi njezinih parametara) pohranjen je u datoteci klase kao niz koji se mora pridržavati određene gramatike bez konteksta. Jedna od provjera koju verifikator provodi na pojedinim komponentama jest osigurati da je svaki deskriptor metode dobro oblikovan niz odgovarajuće gramatike.

Uz to, verifikator datoteke klase provjerava pridržava li se klasa određenih ograničenja koja su joj postavljena specifikacijom programskog jezika Java. Na primjer, verifikator provodi pravilo da sve klase, osim klase Object, moraju imati superklasu. Dakle, provjerivač klase datoteka tijekom izvođenja provjerava neka od pravila Java jezika koja su trebala biti izvršena u vrijeme kompajliranja. Budući da verifikator nikako ne može znati je li datoteku klase generirao dobroćudni prevodilac bez grešaka, provjerava svaku datoteku klase kako bi osigurao poštivanje pravila.