Makale Özeti

.NET ile gelen son derece gelişmiş güvenlik mekanizmasını incelemeye başlıyoruz.

Makale


.NET ve Güvenlik –1- Temel noktalar
Güvenlik hiç kuşkusuz yazılım geliştiricilerin en büyük önemi verdiği, ve günümüz yazılımlarında oldukça öne çıkan bir nokta.
Günümüzde dağınık yazılımların ve Internet destekli yazılımların geldiği noktada güvenlik daha da önemli bir faktör haline geliyor. Internetin birbirinden bağımsız milyonlarca yazılımı birbiriyle buluşturan bir platform kimliği kazanması ile birlikte yazılım dünyasında mevcut güvenlik önlemlerini bu yeni yazılım geliştirme vizyonuna göre bir adım ileri taşıyacak bir mekanizma ihtiyacı doğdu.
Tamamen yeni nesil yazılımların ihtiyaçları doğrultusunda ortaya çıkan .NET Framework güvenlik için bizlere yepyeni olanaklar sunuyor. Okuduğunuz bu satırlar ile başlayan bir dizi makale boyunca bu konuyu birlikte inceliyor olacağız.
Güvenlik mimarilerinde  alışılmış olan yaklaşım kod erişimini kullanıcı hakları kullanarak düzenlemek üzerine kurulmuştur. Belirli kullanıcılar belirli haklara sahiptir, ve bu kullanıcılar tarafından işletilen kodlar da bu haklar ve sınırlamalar doğrultusunda çalışırlar. Ancak bu noktada bir kullanıcı tarafından çalıştırılan iki ayrı kod parçacığını, yani programı birbirinden koruyan bir mekanizmanın eksikliği hissedilir.
Alternatif bir yaklaşım olarak Sandboxing denilen mekanizmayı da inceleyelim: bu mimaride ise çalıştırılan kod, adeta bir kutu içine hapsedilmiş, ve izole edilmiş bir şekilde çalışır.Doğal olarak bu modelde karşılaşılan problem, çalıştırılan kodun, gerek duyduğu önemli bazı servislere, güvenlik nedeni ile erişilmesine izin verilmemesidir.
İşte bu bilgiler ışığında günümüzün güvenlik ihtiyaçlarını tanımlamak çok da zor değil: çalıştırılan kod, planlanan görevini yerine getirmek için gerekli kaynaklara erişebilmeli, ancak kodun kaynağı, ve güvenilirliği de sağlam bir şekilde tanımlanabilmelidir.
.NET Framework işte bu temel amaç üzerinde yola çıkarak son derece detaylı ve güçlü bir güvenlik mimarisini bizlere sunuyor.
.NET Framework güvenlik mekanizması, sadece kötü niyetli kodlara karşı korumalar içermiyor, aynı zamanda kodunuzu hataya karşı koruyan bir sistem.
Peki her programcının başının belası buglara karşı .NET Framework bizi nasıl koruyor ? Yanıt : type safety, yani tür güvenliği. CLR tarafında kontrol altında tutulan managed code, tür güvenliği açısından denetleniyor. Mesela 2 bayt veriyi parametre olarak kabul eden bir fonksiyona 6 bayt uzunluğunda bir parametre geçmek isterseniz, CLR buna izin vermeyecektir.Ayrıca CLR kodun işletilmesi sırasında hafızada her istenilen noktaya dallanılmasına da engel olur. Bu sayede program sadece mantıklı adresler olan method entry pointler gibi adreslere  dallanarak ilerleyebilir. Bu sayede erişileyen adreslere dallanmaya çalışmak, veya buffer overrun gibi hatalar CLR içindeki mekanizma sayesinde hataya sebep olmadan engellenir.
Bu noktada kullanıcıların ne kadar karlı çıktığını bir örnekle gösterebiliriz. Bazen tam çok önemli bir raporunuz  ekranda save edilmemiş halde bütün masumiyeti ile beklerken, sizin çalıştırdığınız bir program tüm sistemi alaşağı ediverir !! Siz de uçan emeklerinize ağlar, bir yandan da neden save etmeden diğer programı çalıştırdım ki diye kendinize kızarsınız. CLR, managed codeu "daha çalıştırmadan önce" type safety açısından test eder. Bu sayede tatsız sürprizlerin kurbanı olma olasılığınız oldukça azalır.
Genelde göz ardı edilen bir gerçeği de burada hatırlatmak gerek: CLR istenirse unmanaged code da çalıştırabilir, ancak bu durumda kodun .NET Framework ile gelen güvenlik mekanizmalarından yararlanması mümkün değildir.
..NET Framework Güvenlik mekanizmasının temel elemanları:

  • Evidence based security: ( kanıta dayalı güvenlik )
  • Role-Based security ( rol temelli güvenlik )
  • Authentication ve Authorization ( tanımlama ve izin verme )
  • Isolated storage
  • Cryptography (şifreleme)

Gördüğünüz gibi son derece detaylı bir yapı ile karşı karşıyayız. İleride örneklerine detaylı olarak gireceğimiz bu sistemin temel taşlarını hep birlikte inceleyelim isterseniz:
Evidence based security ( kanıt a dayalı güvenlik )
İsmi her ne kadar karmaşık bir sistem hissi uyandırsa bile son derece basit ve önemli bir prensip bu layer ile bizlere sunuluyor. Bu mekanizmanın yanıt vermeye çalıştığı soru şu:  "çalıştırılmak istenen kod hakkında bilgilerimiz nelerdir ? " Bu bilgileri de şu sorulara yanıt vererek elde edebiliriz: (not: birazdan sıkça kullanacağımız assembly kavramı size yabancı ise, lütfen assemblyler hakkındaki yazılara bir göz atın, o zaman yazının size çok daha yararlı olacağına emin olabilirsiniz )
Çalıştırmak istediğimiz kod hangi siteden geliyor ? Internet üzerinden assemblyler indiren kodlar için bu soru oldukça önemli. Bu sorunun yanıtı sayesinde güvenlik sistemi, mesela sadece güvenilir bir site olan yazgelistir.com domaininden indirilen kodların çalıştırılmasına izin verebilir.
Çalıştırmak istediğimiz kod hangi URLden  geliyor ?
Diyelim ki kullandığımız domain içinde daha dar bir aralıkta assemblylerin güvenilir olduğunu düşünüyoruz. Bu durumda spesifik URLler ile güvenilir assemblyleri belirleme şansımız da var.
Çalıştırmak istediğimiz kod hangi zone dan yani bölgenden geliyor ?
Burada daha geniş bir tanım yapma şansımız var. Internet explorer ın güvenlik ayarlarına bakmış olan herkes zone kavramı ile aşinadır sanırım. Bu tanım sayesinde bir anda tüm interneti güvensiz, ve tüm lanımızı güvenli ilan edebiliriz.
Çalıştırmak istediğimiz kodun tam adı (strong namei ) nedir ?
İşte size aşılması imkansız bir mekanizma, Sadece tek bir kişinin imzalayacağı ( bu konu içinde lütfen assemblyler yazılarına göz atın...) şifrelenmiş bir kodu güvenli ilan etmeniz durumunda, size kimliğini kanıtlayamayan hiçbir kod CLR tarafından çalıştırılmaz. Şu andaki activeX kontrollerinde de bu mekanizma kullanılıyor.
Bu sorulara verilen yanıtlar sayesinde güvenlik pokitikası oluşturmak mümkündür . Bu politika ile hangi kodun çalışıp hangisinin çalışmayacağına, ve hangi kodun hangi yetkilere sahip olacağına karar verilir. Burada kod hakkında elde ettiğimiz bilgiler bahsi geçen delilleri oluşturur.
Çalıştırılmak istenen managed code, CLR içine yüklendiğinde, önce kod hakkındaki bilgiler yardımı ile gerekli deliller toplanır, daha sonra eldeki deliller ile, daha önceden belirlenmiş olan güvenlik politikası sayesinde kodun çalıştırılıp çalıştırılmayacağı, veya ne gibi işlemleri yapmaya yetkili olacağı kararlaştırılır.
Aynı zamanda assemblyler gerekli olan durumlarda permission request yapabilir. Peki neden böyle bir olanak var ? Çünkü bir assemblyi hazırlayan kişi, çalışmak için o assemblye gereken minimum izinleri bilir. Böylece eğer oluşturduğunuz assembly mesela asla hardiske yazmıyorsa, kendiniz assembly içinde, bu iznin kaldırılmasını CLRdan isteyerek, daha güvenli ve istismar edilemeyecek kod üretebilirsiniz.
CLR , bir assembly içindeki requestler ve önceden oluşturulmuş policy file yardımı ile gerekli kararları alır ve kodu belirli kısıtlamalar ile çalıştırır, veya gerekli şartlar sağlanmıyorsa hiç çalıştırmaz.
Peki ama burada etkili olan policy, yani güvenlik politikası nasıl tanımlanır, nelerden oluşur ?
Güvenlik politikası, code groups, yani kod grupları sayesinde oluşturulur. Kod grupları değişik seviyelerde tanımlanmış güvenlik önlemleri içerir. Normalde .NET Framework üç adet kod grubu, ve bu kod grupları için varsayılan ayarlar ile gelir. Ancak sistem yöneticileri, kanıt tanımları ve ayarlar ile tüm güvenlik mekanizmasını özelleştirebilirler. Konuyu biraz daha açalım isterseniz:
Kog gruplarını : Enterprise, machine, ve user level olarak özetleyebiliriz. Güvenlik mekanizması izinleri bu sıradaki kuralları inceleyerek kararlaştırır. Sistem yönetici istediği bir grubu "final " olarak işaretleyebilir.Bu durumda o seviyeden aşağıdaki izinler dikkate alınmaz. Yani machine levelda güvensiz ilan edilen bir urlden indirilen bir assembly, kullanıcı kim olursa olsun, user level o assembly hakkında ne derse desin güvensizdir.
Bu şekilde üç katmanda yapılan tanımlar ile policy oluştuktan sonra, sıra (eğer varsa) assemblylerin yapacağı permission requestlere gelir. Bu requestler üç şekilde olabilir:
1)      Assembly yi yazan programcı, bu assembly için gereken minimum izinlerin seviyesini bildirir.Bu sayede eğer policy dosyaları belirli izinleri kesinlike yasaklamışsa, ve assembly de bunlardan bazılarına çalışmak için mutlaka ihtiyaç duyduğunu bildirirse, peşin peşin assemblynin yüklenmesi işlemi CLR tarafından iptal edilir.
2)      Assembly yi yazan programcı bulunmasını tercih edeceği izinleri bildirir, ancak bunları "tercihen" istediği için bu izinler policy file ile verilmemiş ise, yine de asembly yüklenir. Yani burada belirtilen, olsa iyi olacak, ama olmasa da olur denebilecek izinlerdir J
3)      Sorumluluğunu bilen programcılar, assembly içine kullanmayacakları izinler de tanımlayarak , başkalarının kodlarını istismar etmesine karşı önlem alır ( aslında böyle bir istismar da olanaksız, nedenini ileride göreceğiz )
Peki şimdi bu ön hazırlıkları gördüğümüze göre bu mekanizma ile run time sırasında neler olur biraz da ona göz atalım.

Burada gördüğünüz şekil, durumu gerçekten net bir şekilde özetliyor. Önce assembly içinden (mesela strong name ) ve assembly hostdan ( mesela assemblynin alındığı site, url, domain…) alınan bilgiler ile kanıtlar toparlanır. Sonra bu kanıtlar ve assembly içinde bulunan izin ricaları policy evaluatora gönderilir.
Burada bir nevi hakem rolü gören policy evaluator, bu bilgiler ve ricalar ışığında kararını verir, ve bu assembly için bir grup izin yaratır. Yukarıdaki şekilde Grant3n kısaltılmışı olarak g3, bizim izinlerimizi gösteriyor.
Şimdi runtime sırasında çok sık gerçekleşen bir durumu incelemek var: diyelim ki assembly A1, programın çalışması esnasına assembly A2 içinde bir metodu çağırdı, ve o metod da, tuttu bizim meşhur A3 assembly mizi çağırdı.
Bu durumda işleyen mekanizma,  .NET Framework ün ne derecede detaylı bir mekanizma içerdiğini ıspatlıyor.
Bu zincirleme metod çağrılarında CLR bir stack walk yürütüyor, ve A3ün gerçekleştirmek istediği işlem için, bu işi başlatan A1 e kadar tüm assemblyler için bir güvenlik kontrolü yapıyor!!! Eğer, G1, G2 ve G3 izinleri bu işlemin yapılmasına uygunsa, başarı ile işlem tamamlanır. Eğer bu zincirdeki bir assemblynin bile gerekli işleme ait izni yoksa, o zaman işlem reddedilir, ve bir security exception alırsınız.
Bu mekanizma "Luring attack" olarak bilinen bir saldırı türüne karşı düşünülmüş. Oldukça yaygın bir saldırı tekniği olan bu teknikte bir işi yapmaya yetkisi olmayan kötü niyetli kod, o işe yetkili bir başka kod üzerinden o işi yapmaya çalışır.Ancak buradaki mekanizma altında bu işi yapması imkansızdır ( biraz önce istismar etmenin olanaksız olduğunu söylerken bunu kastediyordum )
.NET Framework Classlar ile çalışmanın avantajları.
Eğer belli bir işi yaparken .NET Framework altındaki sınıflara çağrılar kullanırsanız, programınız güvenlik mekanizmasından "bedava" yararlanır. Mesela sizin kodunuz hard diske bir şeyleri .NET Framework sınıfları ile yazmak isterse, güvenlik denetimleri otomatik olarak gerçekleşir. Eğer kendi sınıflarınızı yazarsanız, o zaman güvenlik taleplerinizi programcı olarak sizin belirtmeniz gerekir. Kıssadan hisse: mümkün olduğunca .NET Framework class library ile çalışarak işinizi kolaylaştırabilirsiniz.
Role based security

Evidence based securitye göre daha kısa göreceğimiz bu mekanizma yazının başında da belirttiğim gibi, güvenlik mekanizmasını belirli bir kullanıcıya göre çalıştırır. .NET Framework , bu mekanizmadan kimlikler ve prensipler kullanarak yararlanır ( identities ve principals ) Kimlikler işletim sistemindeki kullanıcılar olabileceği gibi assembly tarafından da tanımlanabilir. Bu kimliğe karşılık gelen prensipler ise kimliğin haklarını belirler Authentication ve Authorization
Tek başına birkaç uzun yazıyı doldurabileceğini tahmin ettiğim bu konuyu kısaca özetleyeceğim ( niyetim o bahsi geçen uzun yazıları ayrıca yazmak :) .NET Framework bize authentication için son derece gelişmiş olanaklar sağlıyor. Authentication için işletim sistemine ait Windows identitiy authentication ın yanısıra, Kerberos, Microsoft passport ve forms based authentication gibi seçeneklerimiz de var. Bu metodların hepsi de ayrıca asp.NET altında kullanılabiliyorlar. Bu konuya ileride detaylı olarak gireceğiz.
Isolated Storage

Tamamen güvenilmez ilan edilmemiş bir kod için eğer bir şekilde harddiske bir şeyler yazmak mutkaka zorunlu ise ( mesela bir event log gibi ) o zaman .NET Framework bize Isolated Storage denilen bir mekanizma sunuyor. Bu mekanizma, koda yazabileceği, tamamen izole bir disk alanı sağlıyor,  ve bu alan sadece kod tarafından erişilebilen dışarıya kapalı özel bir oda görevi görüyor.
Cryptography

Modern bir platformun vazgeçilmez unsurlarından biri olan şifreleme, .NET Framework tarafından son derece güçlü bir şekilde destekleniyor. RSA, DSA, Rijndael/AES, Triple DES, DES,  RC2, MD5, SHA1, SHA-256, SHA-384 ve SHA-512 gibi son derece kalabalık bir algoritma listesi emrinize amade. Bu sayede dijital imzalardan, random sayı üretmeye kadar her şeyi kolayca yapabilirsiniz.
Güvenlik istekleri nasıl gerçekleşir ?
Güvenlik isteklerini bir sonraki yazıda kod örnekleri ile göreceğimiz declerative ve imperative istekler olarak ikiye ayırmak mümkün. İlkinin metadata içine yazılan statik izin istekleri , ikincisinin ise dinamik olarak oluşan izin istekleri olduğunu söyleyebiliriz.
Bir sonraki yazımız bu yazıda verilen temeller ve kod örnekleri üzerine olacak.
Buradaki temelleri kafanızda oturttunuz takdirde, bir sonraki yazıyı çok eğlenceli bulacağınıza eminim :) O zamana kadar hoşçakalın.
Şeref Arıkan