Kód-eredet alapú biztonság a .NET platformon













szerzõk:



Albert István (ialbert@avalon.aut.bme.hu)

BME Automatizálási és Alkalmazott Informatikai Tanszék


Balássy György (balassy@avalon.aut.bme.hu)

BME Automatizálási és Alkalmazott Informatikai Tanszék


Rajacsics Tamás (raja@avalon.aut.bme.hu)

BME Automatizálási és Alkalmazott Informatikai Tanszék


Péteri Szilárd (zila@avalon.aut.bme.hu)

BME Automatizálási és Alkalmazott Informatikai Tanszék


Dr. Charaf Hassan (hassan@aut.bme.hu)

BME Automatizálási és Alkalmazott Informatikai Tanszék



Bevezetõ

A mai számítógépes rendszerekre egyre nagyobb veszélyt jelentenek a rosszindulatú szoftverkomponensek. A felhasználók egyre több olyan kódot futtatnak, amelyek eredetérõl nem tudnak meggyõzõdni, illetve nem tudják a szoftver mûködését biztonságos keretek közé szorítani. Vagy nagyon megkötik a szoftver kezét, lényegében mûködésképtelenné és használhatatlanná téve azt, vagy kénytelenek lemondani a biztonságról és a program használata közben reménykedni, hogy nem rosszindulatú kódot futtatnak.

Ismeretlen eredetû, veszélyes mobil kódokat letölthetünk az Internetrõl, megkaphatunk levélben vagy dokumentumokhoz csatolva.

A legutóbbi idõk “I Love You” vírusával kezdõdõ járvány felhívta a figyelmet az operációs rendszerekben található nagyon veszélyes biztonsági résekre és azokra az alkalmazásokra, amelyek lehetõvé teszik rosszindulatú mobilkódok futtatását (például a Microsoft Outlook levelezõprogramjának nem biztonságos futtatókörnyezetére).

A számítástechnika hõskorában, amikor az Internet még csak gyerekcipõben tipegett néhány jószándékú fizikus jóvoltából, és a dokumentumok nem tartalmaztak apró programokat amelyek maguktól elindultak – akkor még sokkal egyszerûbb volt az operációs rendszert tervezõ mérnökök feladata: a rendszert és az ott tárolt adatokat ’csak’ az illetéktelen felhasználóktól kellett félteni. Így az egyes erõforrásokhoz felhasználótól függõ korlátozásokat vezettek be. Nemcsak az adatok tárolására szolgáló fájlok és mappák védelmét oldották meg, de többnyire gondoskodtak a rendszer védelmérõl is, a kritikus erõforrásokhoz (processzor, memória, szabad háttértárterület, stb.) való hozzáférés korlátozásával. A ma is használt biztonsági koncepció lényege, hogy minden felhasználó csak bizonyos mûveleteket végezhet el a rendszerben található erõforrásokon, és a felhasználókat többnyire felhasználónevük és jelszavuk azonosítja.

Napjainkban azonban jelentõsen ki kell bõvíteni ezt a védelmi mechanizmust. Ma egy felhasználó nem minden esetben tud meggyõzõdni arról, hogy az általa használni kívánt szoftver megbízható forrásból származik. Sõt, az Internet terjedésével egyre kevésbé lehetünk biztosak abban, hogy a letöltött és elindított alkalmazások nem tesznek kárt a számítógépünkben – vagy azon keresztül akár egy egész vállalati rendszerben. Internetes honlapot manapság névtelenül is lehet készíteni, és a keresõmotorok adatbázisát is manipulálni lehet megfelelõ módszerekkel. Ezután csak meg kell írni a rosszindulatú kódot és feltenni a honlapra. Elhelyezhetünk mellé még néhány jól hangzó reklámot, nyertes díjat, idézhetünk meg nem jelent cikkekbõl és összehasonlításokból, sõt ha mindez még nem lenne elég, kódunkat digitális aláírással is elláthatjuk – akár továbbra is névtelenül. Ezután a gyanútlan felhasználó szinte biztos, hogy letölti és elindítja programunkat.

Bár születtek eddig is megoldások a probléma orvoslása érdekében (web szkriptek és letöltött komponensek korlátozása, digitális aláírás), ezek nem kínáltak sem teljes megoldást, sem a szükséges rugalmasságot nem biztosították.

A napjainkban megjelenõ .NET platform olyan biztonsági mechanizmussal rendelkezik, amely megoldást nyújthat mindezekre a problémákra. A .NET platformot az ECMA-335 Common Language Specification (CLI) szabvány definiálja [http://www.ecma.ch/ecma1/STAND/ecma-335.htm].

Mielõtt azonban elmélyülnénk a technológiai részletekben, bemutatjuk az új platform néhány olyan elemét, amelyek ismerete hasznos lehet a rendszer megismeréséhez.

.NET Framework (keretrendszer)

A teljes .NET platform áttekintõ ismertetése messze meghaladná a cikk terjedelmét. Éppen ezért ennek csak egy részét, a .NET Frameworköt mutatjuk be, ami a platform lelke.

A .NET Framework az úgynevezett futtatókörnyezetbõl (Common Language Runtime - CLR) és alaposztály-könyvtárból áll. Ez a keretrendszer köti össze a programot az operációs rendszerrel. A futtatókörnyezet által futtatott programok a legtöbb esetben minden rendszerszolgáltatást az alaposztályokon keresztül érnek el. A CLI által definiált környezet nagyon hasonló szolgáltatásokat nyújt, mint a Java, bár az újabb rendszer tervezésénél és kifejlesztésénél felhasználták azokat a tapasztatokat, amelyek a Java-nál felmerültek, illetve néhány komolyabb lehetõséggel, funkcióval (feature) bõvítették is (például több programnyelv együttmûködésének támogatása, stb).


A .NET Framework biztonsági szolgáltatásai

A kóderedet alapú biztonság természetesen nem váltja fel az eddig meglévõ és bevált szerep alapú biztonságot, hanem kiterjeszti az elõzõ modellt. Az erõforrások elérésének sikeressége tehát nemcsak a futtató személyazonosságán vagy a felhasznált számítógépen múlik, hanem függ a futtatott kód bizonyos tulajdonságaitól (eredetétõl) is. A cikk célja a kóderedet alapú biztonsági mechanizmus bemutatása.


Kóderedet alapú biztonság

Áttekintés

Az autorizáció – azaz a biztonsági politika érvényesítése – biztosítja, hogy egy adott erõforráson minden program csak a számára engedélyezett mûveleteket hajtja végre. Az erõforrások manapság nemcsak a fájlokat, memóriát jelentik, hanem a vágólapot (clipboard), környezeti beállításokat, képernyõt (dialógusablakok megjelenítése), regisztrációs adatbázist (registry) stb. A CLR (Common Language Runtime – a futtatókörnyezet) által futtatott programok ezeket az erõforrásokat az alaposztályok (Base Classes) használatával érik el, amelyekbe ez az ellenõrzési mechanizmus be van építve. Tehát ha egy olyan mûveletet hajt végre az adott alkalmazás, amelyhez nincs joga, a megfelelõ funkció egy biztonsági kivétel (SecurityException) dobásával visszautasítja annak végrehajtását. Hogy egy alkalmazásnak mihez van joga és mihez nincs, azt a biztonsági politika határozza meg, amelyet vállalatonként (enterprise), gépenként és felhasználónként lehet meghatározni.

Ez erõforrás elérésénél a futtatókörnyezet megvizsgálja többek között, hogy ki az alkalmazás futtatója, milyen számítógépen fut az adott komponens és honnan lett letöltve, ki fejlesztette – és végül, hogy a biztonsági politika beállításai szerint a felhasználónak és a kódnak van-e joga az adott mûvelet végrehajtásához.

Biztonsági politika

A biztonsági politika határozza meg, hogy az egyes kódok milyen erõforrásokhoz férhetnek hozzá. A jogosultságok és erõforrások struktúrája szinte teljesen megegyezik a már megszokott szerep alapú sémával: a legtöbb operációs rendszerben megadhatjuk, hogy egy mappában levõ fájlokat, vagy akár egy adott fájlt mely felhasználók, vagy felhasználócsoportok írhatnak, olvashatnak, stb. Sõt, többnyire lehetõség van örökléssel megvalósított hierarchizált jogosultságstruktúra kialakítására is.

Felhasználók esetén ezt általában a rendszergazda teszi meg: például az új kolléga belépéskor megkapja azonosítóját, jelszavát, és ezzel együtt jogokat kap bizonyos erõforrások elérésére is. Amikor a kolléga más cégnél helyezkedik el, akkor az itteni felhasználófiókját (account) a rendszergazda megszünteti.

Rögtön láthatjuk, hogy az Internetrõl letöltött alkalmazások esetén ez a módszer nem mûködik. Nem várhatjuk el (és nem is lehetséges), hogy minden egyes program letöltésénél egy adminisztrátor azonnal mondja meg és állítsa be, hogy az a program mit tehet, és mit nem. A rendszerbe kerülõ kód jogait nem tudjuk jól meghatározott idõpontban (mint például a kolléga felvétele) egyéni elbírálás alapján megadni, tehát elõre ki kell alakítani azokat a szempontokat, amelyek alapján a rendszerbe kerülõ komponenseket megkülönböztetjük és csoportokba soroljuk, illetve ezekhez a csoportokhoz jogokat kell rendelnünk.

Bizonyítékok

Az új kollégákat megkülönbözteti például a beosztásuk, az osztály, ahol dolgoznak, végsõ soron pedig személyi számuk. A Internetrõl érkezõ kódnál is vannak ilyen információk, amelyek a kód eredetére utalnak. Ezeket bizonyítéknak (evidence) hívjuk, és minden .NET-es programhoz tartoznak ilyen elemek, amelyeket a futtatókörnyezet (runtime host) rendel hozzá. Például:

Néhány ilyen információelem elõre be van építve a CLR-be, de ez a modell tetszõlegesen kiterjeszthetõ (ahogy látni fogjuk, a .NET Frameworkben szinte minden mechanizmus kiterjeszthetõ a megfelelõ interfészt megvalósító osztályok létrehozásával).

A teljes kép megismerése érdekében be kell vezetünk a szerelvény (assembly) fogalmát. A szerelvény a .NET alapú programok logikailag szervezõegysége. A legtöbb esetben egy szerelvény egy fájlt (dll vagy exe kiterjesztésû állományt) jelent, de ez nem szükségképpen vagy így, egy szerelvény állhat akár több fájlból is (ekkor a fájloknak együtt kell mozogniuk). Az eredet alapú biztonsági mechanizmus legkisebb konfigurálható egysége a szerelvény (assembly), hiszen ehhez rendelhetõk azok az információelemek, bizonyítékok, amelyek az ellenõrzés alapját képezik. (A továbbiakban a szerelvény, kód, állomány és program fogalmakat szinonimaként fogjuk használni a szóismétlések elkerülése és a jobb érthetõség érdekében.)

A biztonsági politika kiértékelése

Miután a futtatórendszer megszerezte a szerelvény eredetére vonatkozó összes bizonyítékot, a biztonsági rendszer kiértékeli a biztonsági politikát.

A biztonsági politika három fõ szintbõl áll: vállalati (enterprise), számítógép és felhasználó. Minden szint több kódcsoportból és a kódcsoportokhoz tartozó engedélyhalmazokból áll. A kódcsoport feltételeket jelent a kód (szerelvény) eredetére vonatkozóan: például milyen URL-rõl lett letöltve. Az engedélyhalmaz pedig jogoknak (erõforrások és rajtuk végezhetõ mûveletek) újrafelhasználható csoportosítása. Ezek segítségével a rendszergazda például meghatározhatja, hogy egy adott cég által aláírt komponens mely fájlokhoz férhet hozzá, vagy egy bizonyos site-ról letöltött program a felhasználókkal dialógusablakok segítségével kommunikálhat.

A hatékony struktúra kialakítása érdekében a kódcsoportok hierarchiába rendezhetõk, és a fa ágain a gyökérbõl lefelé haladva egyre több engedélyt kaphatnak a szûkebb feltételeknek megfelelõ alkalmazások.

A CLR mind a három fát végigjárva a következõ módon határozza meg a kódhoz tartozó engedélyek halmazát: az egy szinten (fán) belül megkapott engedélyeket összegzi (unió), majd az így kapott három jogosultsághalmaznak a metszetét veszi.

A következõ lépés az így megszerzett engedélyek finomhangolása a kódhoz tartozó attribútumok alapján. A fejlesztõ a .NET platformra írt kódhoz attribútumokat rendelhet forráskód szinten, amelyek a lefordított állományba kerülnek és késõbb lekérdezhetõk. Ezek az attribútumok olyan métainformációk, amelyek befolyásolják a futtatókörnyezetnek vagy magának a kódnak a mûködését. A letöltött állományhoz (szerelvényhez) három fajta engedélyhalmaz rendelhetõ: minimális, opcionális és visszautasított engedélyek. Ezek jelentése a következõ:

A kódhoz rendelt effektív jogosultsághalmazt (Granted) a következõ módon határozza meg a CLR:

A minimálisan (Minimal) és az opcionálisan (Optional) kért engedélyhalmazok uniójának és a biztonsági politika által meghatározott halmaznak (Allowed) a metszetét képezzük, majd az eredményhalmazból kivonjuk a visszautasított jogosultságokat (Refused):

Granted = ( Minimal È Optional ) Ç Allowed - Refused

Így megkaptuk azokat az engedélyeket (erõforrások és hozzájuk rendelt mûveletek), amelyekkel a futtatott kód rendelkezik.

Futásidejû mûködés

Minden .NET-re írt program kérheti bizonyos engedélyek meglétét. Ezt vagy metódushoz illetve osztályhoz rendelt attribútummal teheti meg deklaratív módon, vagy futás idejû hívással (a System.IPermission interfész Demand metódushívásával). Valamilyen erõforráshoz való hozzáférés esetén a felelõs .NET osztály (ami a Base Class Library-ban található) meghívott függvénye (például a FileStream konstruktora, amely megnyit egy fájlt) egy Demand metódushívással biztosítja azt, hogy a futó kód (a futtató felhasználóval együtt) jogosult az adott fájl megnyitására. Ilyenkor a futtatókörnyezet nemcsak a közvetlen hívóhoz rendelt jogokat vizsgálja meg, hanem az teljes hívási vermet megvizsgálja, azaz az adott mûvelet végrehajtásához az összes hívó kódnak rendelkeznie kell a megfelelõ jogosultságokkal. (Az alábbi ábrán a látható hívási verem nem rendelkezik a írási joggal, így a konstruktor SecurityException kivétellel tér vissza.)

A fenti mûködés természetesen komoly sebességproblémákat okozhat, ezekre az esetekre külön megoldások készíthetõk, erre szolgálhat például az Assert metódushívás. Ez a hívás lehetõvé teszi, hogy a kód egy erõforráshoz anélkül hozzáférjen, hogy az összes hívó rendelkezne a megfelelõ engedéllyel. Természetesen az Assert metódus hívásához is egy megfelelõ engedély szükséges, hiszen ez egy nagyon veszély függvényhívás.

A másik jogosultság, amelyik ugyancsak sok veszélyt rejt magában, az a nem menedzselt kódhoz való hozzáférés. Nem menedzselt kód minden olyan kód, amit nem a CLR futtat, így például az operációs rendszer programozói interfésze is (Microsoft Windows esetén a Win32API). Tehát ha tehát egy kódnak joga van nem menedzselt kódot hívni, akkor lehetõsége van a CLR biztonsági mechanizmusát megkerülve közvetlen operációs rendszer szolgáltatásokat elérni. Például a .NET platform szolgáltatásait megvalósító alaposztály állományai mindkét jogosultságot megkapják (nem menedzselt kódhoz való hozzáférést és az Assert hívás jogát), mert például egy fájl megnyitásához ezek az osztályok természetesen az operációs rendszert szolgáltatásait használják (Win32API CreateFile függvényét). A FileStream konstruktora a Demand metódushívással megvizsgálja, hogy a hívó programok (a teljes hívási verem figyelembevételével) megnyithatják-e a paraméterben megadott állományt, majd az Assert függvényhívás után meghívják az operációs rendszer megfelelõ függvényét (nem menedzselt kód hívása). Az Assert hívás nélkül az operációs rendszer hívás csak akkor futna le, ha az összes hívó rendelkezne a nem menedzselt kód elérésének engedélyével.

Ahogy a fenti példából is látszik, minden olyan kérés, amely az operációs rendszer által karbantartott és felügyelt erõforrásra vonatkozik, végül is eljut az operációs rendszerhez. Ekkor természetesen az operációs rendszer is ellenõrzi, hogy az adott felhasználónak joga van-e a mûvelet végrehajtására vagy sem (Microsoft Windows NT, 2000, XP operációs rendszerek esetén megvizsgálja, hogy a futtató személy rendelkezik-e a megfelelõ NTFS jogosultságokkal vagy sem). Ez azonban a kóderedet alapú mechanizmust többnyire nem érinti, mert a mai operációs rendszerek biztonsági ellenõrzései alapvetõen szerep alapúak.

Rövid összehasonlítás a Java 2 platformmal

A két platform kóderedet alapú biztonsági szolgáltatásait tekintve nagyon hasonló, koncepcionális eltérések nem találhatók, mindkét rendszer lehetõséget biztosít a bevezetõben vázolt problémák megoldására.

Ki kell emelnünk azonban a .NET alapú megoldás erõsségét: az attribútumokat. A szerelvényekhez, osztályokhoz, metódusokhoz rendelt metainformációk lehetõvé teszik a biztonsági struktúra finomhangolását, és mivel ezeket az információkat a fejlesztõ állítja be a forrásfájlban és belekerülnek a lefordított állományba is, nincs szükség külön konfigurációs fájlokra, és az alkalmazásról még futtatás elõtt eldönthetõ, hogy rendelkezik-e a szükséges jogokkal (például nem a mentés funkciónál derül ki, hogy nem tudjuk elmenteni a munkánkat, mert a kód nem rendelkezik a megfelelõ jogosultsággal). Az attribútumok és a hierarchizált biztonsági struktúra használatával valamivel hatékonyabb, konzisztensebb és rugalmasabb megoldást alakíthatunk ki.

Összefoglalás

A napjainkban megjelenõ új platform, a .NET nagyon jól átgondolt biztonsági mechanizmussal rendelkezik, amely figyelembe veszi a futtatott kód eredetét is, így megoldást nyújt azokra a problémákra, amelyeket a modern világ információs médiája, az Internet egyre szélesebbkörû használata vet fel.


Irodalomjegyzék





Specifications

[http://www.ecma.ch/ecma1/STAND/ecma-335.htm]



Microsoft Developer Network

[http://msdn.microsoft.com]



.NET Framework Developer’s Guide: Code Access Security

[http://msdn.microsoft.com/library/en-us/cpguidnf/html/cpconcodeaccesssecurity.asp]



Security in .NET: Enforce Code Access Rights with the Common Language Runtime

[http://msdn.microsoft.com/msdnmag/issues/01/02/CAS/print.asp]



Java security evolution and concepts, Part 2: Discover the ins and outs of Java security

[http://www.javaworld.com/javaworld/jw-07-2000/jw-0728-security_p.html]



Java security evolution and concepts, Part 3: Applet security

[http://www.javaworld.com/javaworld/jw-12-2000/jw-1215-security_p.html]



Permissions in the Java 2 SDK

[http://java.sun.com/j2se/1.3/docs/guide/security/permissions.html]