Сигурност и архитектура на товара на клас

Предишна 1 2 Страница 2 Страница 2 от 2

Класове за зареждане и пространства за имена

За всеки клас, който зарежда, JVM следи кой зареждащ клас - независимо дали първоначален или обектен - е заредил класа. Когато зареден клас за първи път се позовава на друг клас, виртуалната машина иска реферирания клас от същия зареждащ клас, който първоначално е заредил референтния клас. Например, ако виртуалната машина зарежда клас Volcanoчрез даден товарач на клас, тя ще се опита да зареди всички класове, към които се Volcanoпрепраща през същия зареждащ клас. Ако се Volcanoотнася за клас с име Lava, може би чрез извикване на метод в клас Lava, виртуалната машина ще поиска Lavaот зареждащия клас клас Volcano. В Lavaкласа върнати от товарач клас е динамично свързан с клас Volcano.

Тъй като JVM възприема този подход при зареждане на класове, класовете могат по подразбиране да виждат само други класове, които са били заредени от същия зареждащ клас. По този начин архитектурата на Java ви позволява да създавате множество пространства от имена в едно Java приложение. Пространството на имената е набор от уникални имена на класовете, заредени от конкретен зареждащ клас. За всеки зареждащ клас JVM поддържа пространство на имена, което се попълва от имената на всички класове, които са били заредени чрез този load loader.

След като JVM е заредил клас, наречен Volcanoв определено пространство на имена, например, е невъзможно да се зареди различен клас с име Volcanoв същото пространство на имена. Можете обаче да заредите множество Volcanoкласове в JVM, защото можете да създадете множество пространства от имена в Java приложение. Можете да го направите просто като създадете зареждачи с множество класове. Ако създадете три отделни пространства от имена (по едно за всеки от трите товарачи на клас) в работещо Java приложение, след това, като заредите един Volcanoклас във всяко пространство от имена, вашата програма може да зареди три различни Volcanoкласа във вашето приложение.

Приложението на Java може да създаде екземпляр на множество обекти за зареждане на клас или от един и същи клас, или от множество класове. Следователно той може да създаде толкова (и толкова различни видове) обекти за зареждане на клас, колкото му е необходимо. Класовете, заредени от различни зареждащи класове, са в различни пространства от имена и не могат да получат достъп един до друг, освен ако приложението изрично го позволява. Когато пишете Java приложение, можете да разделите класове, заредени от различни източници, в различни пространства от имена. По този начин можете да използвате архитектурата на Java за зареждане на класове, за да контролирате всяко взаимодействие между код, зареден от различни източници. Можете да предотвратите достъпа до враждебен код и подмяната на приятелски код.

Класни товарачи за аплети

Един пример за динамично разширение с зареждачи на класове е уеб браузърът, който използва обекти за зареждане на класове, за да изтегли файловете на класа за аплет в мрежа. Уеб браузър изстрелва Java приложение, което инсталира обект за зареждане на клас - обикновено наричан loader за клас на аплети - който знае как да иска файлове от клас от HTTP сървър. Аплетите са пример за динамично разширение, тъй като когато стартира приложението Java, то не знае кои файлове от клас браузърът ще поиска да го изтегли в мрежата. Файловете на класовете за изтегляне се определят по време на изпълнение, тъй като браузърът среща страници, които съдържат Java аплети.

Приложението Java, стартирано от уеб браузъра, обикновено създава различен обект за зареждане на клас на аплети за всяко местоположение в мрежата, от което извлича файлове с класове. В резултат на това файловете от класа от различни източници се зареждат от различни обекти за зареждане на класове. Това ги поставя в различни пространства от имена в хост приложението Java. Тъй като файловете на класовете за аплети от различни източници се поставят в отделни пространства от имена, кодът на злонамерен аплет е ограничен от пряка намеса в файловете на класа, изтеглени от всеки друг източник.

Сътрудничество между класните товарачи

Често обектът за зареждане на клас разчита на други товарачи на клас - най-малкото на първоначалния товарач на клас - за да му помогне да изпълни някои от заявките за зареждане на класа, които идват по пътя му. Например, представете си, че пишете приложение на Java, което инсталира зареждащ клас, чийто особен начин на зареждане на файлове с класове се постига чрез изтеглянето им в мрежа. Да предположим, че по време на изпълнението на приложението Java се прави заявка от вашия зареждащ клас за зареждане на клас с име Volcano.

Един от начините, по който можете да напишете зареждащия клас, е първо да поиска от първоначалния зареждащ клас да намери и зареди класа от неговото надеждно хранилище. В този случай, тъй като Volcanoне е част от Java API, приемете, че първоначалният зареждащ клас не може да намери клас с име Volcano. Когато първоначалният товарач на клас реагира, че не може да зареди класа, вашият клас товарач може след това да се опита да зареди Volcanoкласа по негов персонализиран начин, като го изтегли в мрежата. Ако приемем, че зареждащият ви клас е успял да изтегли класа Volcano, този Volcanoклас може да играе роля в бъдещия ход на изпълнение на приложението.

За да продължите със същия пример, приемете, че известно време по-късно методът на клас Volcanoсе извиква за първи път и че методът се позовава на клас Stringот Java API. Тъй като за първи път референцията се използва от работещата програма, виртуалната машина иска от вашия зареждащ клас (този, който е зареден Volcano) да се зареди String. Както и преди, вашият load loader първо предава заявката на първоначалния loader на класа, но в този случай първоначалният loader на класа може да върне Stringклас обратно към вашия loader на класа.

Първоначалният loader на класа най-вероятно Stringв този момент не е трябвало да се зарежда, защото, като се има предвид, че това Stringе толкова основен клас в Java програмите, той почти сигурно е бил използван преди и следователно вече е зареден. Най-вероятно първоначалният зареждащ клас току-що върна Stringкласа, който преди това е заредил от довереното хранилище.

Тъй като първоначалният товарач на клас успя да намери класа, вашият клас товарач не се опитва да го изтегли в мрежата; той просто предава на виртуалната машина Stringкласа, върнат от първоначалния loader на класа. От този момент нататък виртуалната машина използва този Stringклас, когато клас се Volcanoпозовава на клас с име String.

Класови товарачи в пясъчника

В пясъчника на Java архитектурата на loader клас е първата линия на защита срещу злонамерен код. В края на краищата зареждащият клас внася код в JVM - код, който може да бъде враждебен.

Архитектурата на loader на класа допринася за пясъчника на Java по два начина:

  1. Той предотвратява намесата на злонамерен код в добронамерен код.
  2. Той пази границите на доверените библиотеки на класове.

Архитектурата на loader на класовете пази границите на библиотеките с доверени класове, като се уверява, че ненадеждните класове не могат да се преструват, че са им се доверили. Ако злонамерен клас може успешно да подмами JVM да повярва, че е доверен клас от Java API, този злонамерен клас потенциално може да пробие бариерата на пясъчника. Като предотвратява ненадеждните класове да се представят за надеждни класове, архитектурата на зареждащия клас блокира един потенциален подход за компрометиране на сигурността на изпълнението на Java.

Пространства с имена и щитове

Архитектурата на loader на класовете предотвратява намесата на злонамерен код с доброжелателен код, като предоставя защитени пространства от имена за класове, заредени от различни loaders на класове. Както бе споменато по-горе, пространството на имената е набор от уникални имена за заредени класове, който се поддържа от JVM.

Пространствата от имена допринасят за сигурността, защото всъщност можете да поставите щит между класовете, заредени в различни пространства от имена. Вътре в JVM класовете в едно и също пространство на имена могат да взаимодействат директно един с друг. Класовете в различни пространства от имена обаче не могат дори да открият присъствието един на друг, освен ако изрично не предоставите механизъм, който позволява на класовете да си взаимодействат. Ако злонамерен клас, веднъж зареден, е имал гарантиран достъп до всеки друг клас, в момента зареден от виртуалната машина, този клас потенциално би могъл да научи неща, които не би трябвало да знае, или може да попречи на правилното изпълнение на вашата програма.

Създаване на сигурна среда

Когато пишете приложение, което използва зареждащи класове, създавате среда, в която се изпълнява динамично зареденият код. Ако искате околната среда да бъде без дупки в сигурността, трябва да следвате определени правила, когато пишете приложението и класните товарачи. Като цяло ще искате да напишете приложението си, така че зловредният код да бъде защитен от доброжелателен код. Също така, ще искате да напишете зареждачи на класове, така че да защитават границите на надеждни библиотеки на класове, като тези на Java API.

Пространства от имена и източници на код

За да получите предимствата на сигурността, предлагани от пространствата на имената, трябва да сте сигурни, че зареждате класове от различни източници чрез различни зареждащи класове. Това е схемата, описана по-горе, използвана от уеб браузъри с активиран Java. Приложението Java, изстреляно от уеб браузър, обикновено създава различен обект за зареждане на клас на аплети за всеки източник на класове, които изтегля в мрежата. Например браузър ще използва един обект за зареждане на класове за изтегляне на класове от //www.niceapplets.com и друг обект за зареждане на класове за изтегляне на класове от //www.meanapplets.com.

Охрана на ограничени пакети

Java позволява на класове в един и същи пакет да си предоставят специални привилегии за достъп, които не се предоставят на класове извън пакета. Така че, ако вашият load loader получи заявка за зареждане на клас, който с името си нагло се декларира като част от Java API (например клас с име java.lang.Virus), вашият load loader трябва да действа предпазливо. Ако се зареди, такъв клас може да получи специален достъп до надеждните класове на java.langи евентуално да използва този специален достъп за скрити цели.

Следователно, обикновено пишете зареждащ клас, така че той просто отказва да зареди всеки клас, който твърди, че е част от Java API (или която и да е друга надеждна библиотека по време на изпълнение), но който не съществува в локалното доверено хранилище. С други думи, след като зареждащият ви клас предава заявка към първоначалния зареждащ клас и първоначалният зареждащ клас показва, че не може да зареди класа, вашият зареждащ клас трябва да провери дали класът не се декларира като член на надежден пакет. Ако това се случи, вашият клас за зареждане, вместо да се опитва да изтегли класа през мрежата, трябва да изведе изключение за защита.

Пазене на забранени пакети

Освен това може да сте инсталирали някои пакети в надеждното хранилище, които съдържат класове, които искате вашето приложение да може да зареди чрез първоначалния зареждащ клас, но че не искате да имате достъп до класове, заредени чрез вашия loader. Например, предположим, че сте създали пакет с име absolutepowerи сте го инсталирали в локалното хранилище, достъпно от първоначалния зареждащ клас. Да приемем също, че не искате класовете, заредени от вашия loader, да могат да зареждат всеки клас от absolutepowerпакета. В този случай бихте написали своя клас за зареждане, така че първото нещо, което прави, е да се увери, че исканият клас не се декларира като член наabsolutepowerпакет. Ако се изисква такъв клас, вашият клас за зареждане, вместо да предава името на класа на първоначалния товарач на клас, трябва да изведе изключение за защита.

Единственият начин, по който зареждащият клас може да разбере дали даден клас е от ограничен пакет, като например java.lang, или забранен пакет, като например absolutepower, е по името на класа. По този начин на зареждащия клас трябва да се даде списък с имената на ограничени и забранени пакети. Тъй като името на класа java.lang.Virusпоказва, че е от java.langпакета и java.langе в списъка с ограничени пакети, зареждащият ви клас трябва да изведе изключение за защита, ако първоначалният зареждащ клас не може да го зареди. По същия начин, тъй като името на класа absolutepower.FancyClassLoaderпоказва, че е част от absolutepowerпакета и absolutepowerпакетът е в списъка на забранените пакети, зареждащият ви клас трябва да изведе изключение за защита.

Клас за зареждане със сигурност

Често срещан начин за писане на лоялен клас за зареждане е да се използват следните четири стъпки:

  1. Ако съществуват пакети, от които не е разрешено да се зарежда този load loader, зареждащият клас проверява дали заявеният клас е в един от тези забранени пакети, споменати по-горе. Ако е така, то хвърля изключение за сигурността. Ако не, той продължава към стъпка две.

  2. Зареждащият клас предава заявката на първоначалния зареждащ клас. Ако първоначалният товарач на клас успешно връща класа, зареждащият клас връща същия клас. В противен случай продължава на трета стъпка.

  3. Ако съществуват надеждни пакети, към които този зареждащ клас няма право да добавя класове, зареждащият клас проверява дали заявеният клас е в един от тези ограничени пакети. Ако е така, то хвърля изключение за сигурността. Ако не, продължава на стъпка четири.

  4. И накрая, зареждащият клас се опитва да зареди класа по персонализиран начин, например чрез изтеглянето му в мрежа. Ако успее, връща класа. Ако е неуспешно, извежда грешка „не е намерена дефиниция на клас“.

Изпълнявайки стъпки една и трета, както е описано по-горе, зареждащият клас защитава границите на доверените пакети. С първата стъпка той предотвратява изобщо зареждане на клас от забранен пакет. С стъпка трета не позволява на ненадежден клас да се вмъква в надежден пакет.

Заключение

Архитектурата на зареждащия клас допринася за модела за сигурност на JVM по два начина:

  1. чрез разделяне на кода на множество пространства от имена и поставяне на "щит" между кода в различни пространства от имена
  2. чрез защита на границите на надеждни библиотеки на класове, като Java API

И двете възможности на архитектурата на Java за зареждане на класове трябва да се използват правилно от програмистите, за да се възползват от ползата за сигурността, която предлагат. За да се възползвате от щита за пространство на имената, кодът от различни източници трябва да бъде зареден чрез различни обекти за зареждане на клас. За да се възползват от надеждното опазване на границата на пакетите, трябва да бъдат написани зареждачи на класове, за да проверят имената на заявените класове спрямо списък с ограничени и забранени пакети.

За разходка през процеса на писане на зареждащ клас, включително примерен код, вижте статията на JavaWorld на Чък МакМанис , „Основите на зареждащите устройства на Java“.

Следващият месец

В статията за следващия месец ще продължа обсъждането на модела за сигурност на JVM, като опиша проверителя на класа.

Бил Венърс пише софтуер професионално от 12 години. Базиран в Силициевата долина, той предоставя софтуерни консултации и услуги за обучение под името Artima Software Company. През годините той е разработил софтуер за потребителската електроника, образованието, полупроводниците и животозастраховането. Програмирал е на много езици на много платформи: асемблерен език на различни микропроцесори, C на Unix, C ++ на Windows, Java в мрежата. Автор е на книгата „Inside the Java Virtual Machine“, издадена от McGraw-Hill.

Научете повече за тази тема

  • Книгата Спецификация на виртуалната машина Java (//www.aw.com/cp/lindholm-yellin.html), от Тим ​​Линдхолм и Франк Йелин (ISBN 0-201-63452-X), част от серията Java (// www.aw.com/cp/javaseries.html), от Addison-Wesley, е окончателната справка за виртуална машина Java.
  • Сигурни изчисления с JavaNow and the Future (бяла книга) // www.javasoft.com/marketing/collateral/security.html
  • Често задавани въпроси за сигурността на аплета

    //www.javasoft.com/sfaq/

  • Ниско ниво на сигурност в Java, от Франк Йелин //www.javasoft.com/sfaq/verifier.html
  • Началната страница на Java Security

    //www.javasoft.com/security/

  • Вижте началната страница на враждебните аплети

    //www.math.gatech.edu/~mladue/HostileApplets.html

  • Книгата Java SecurityHostile Applets, Holes and Antidotes, от д-р Гари Макгроу и Ед Фелтън, дава задълбочен анализ на проблемите със сигурността, свързани с Java. //www.rstcorp.com/java-security.html
  • Предишни статии "Под капака":
  • Lean, Mean Virtual Machine - дава представяне на Java виртуалната машина.
  • Начин на живот на Java Class File - Дава преглед на файла на класа на Java, файловият формат, в който са компилирани всички Java програми.
  • Java's Garbage-Collected Heap - Дава преглед на събирането на боклука като цяло и събраната купчина боклук на виртуалната машина Java.
  • Основи на байт кода - представя байт кодовете на виртуалната машина Java и обсъжда примитивни типове, операции на преобразуване и по-специално операции на стека.
  • Аритметика с плаваща запетая - описва поддръжката на плаваща запетая на виртуалната машина на Java и байт кодовете, които извършват операции с плаваща запетая.
  • Логика и аритметика - Описва поддръжката на виртуалната машина Java за логическа и целочислена аритметика и свързаните с тях байт кодове.
  • Обекти и масиви - Описва как виртуалната машина Java се справя с обекти и масиви и обсъжда съответните байт кодове.
  • Изключения - Описва как виртуалната машина Java се справя с изключенията и обсъжда съответните байт кодове.
  • Опитайте-накрая - Описва как виртуалната машина на Java изпълнява клаузи за опит-накрая и обсъжда съответните байт кодове.
  • Контролен поток - Описва как виртуалната машина на Java изпълнява контролен поток и обсъжда съответните байт кодове.
  • Архитектурата на Aglets - Описва вътрешната работа на aglets, автономната технология на IBM за софтуерни агенти, базирана на Java.
  • The Point of Aglets - Анализира реалната полезност на мобилните агенти като aglets, автономната технология на IBM за софтуерни агенти, базирана на Java.
  • Извикване и връщане на метод - Описва четирите начина, по които виртуалната машина Java извиква методи, включително съответните байт кодове.
  • Синхронизация на нишки - Показва как синхронизирането на нишки работи във виртуалната машина Java. Обсъжда байт кодовете за влизане и излизане от монитори.
  • Архитектурата на защитата на Java - Дава преглед на модела за сигурност, вграден в JVM, и разглежда вградените функции за безопасност на JVM.

Тази история „Сигурност и архитектура на зареждащия клас“ първоначално е публикувана от JavaWorld.