Güvenli Yazılım Geliştirme

Güvenli Yazılım Geliştirme Geliştirilen uygulamalardaki güvenlik açıklarının büyük bir kesimi hatalı kodlamada kaynaklanmaktadır. Bu hatalar platforma, işletim sistemine ya da geliştirilen programlama diline bağlı değildir. Geliştirilen bir uygulamanın davranışının kararlı olması gerekmektedir. Bir gün şöyle, bir gün böyle sonuçlar üretmemelidir. Matematikte her zaman 2+2 = 4’tür. Hiç bir zaman 3 ya da 5 olmaz. Bu matematiğin kararlı bir yapıya sahip olmasından kaynaklanmaktadır. Geliştirilen bir uygulama daaynı kararlılığı gösterebilmelidir. Her ne kadar tüm işlemleri bilgisayar yapıyor da olsa, hangi işlemlerin hangi sırayla yapılmasını belirleyen kişi bir insandır. Her insan hata yapar. O yüzden ilk yazdığınızda tam olarak çalışan bir uygulama geliştiremezsiniz. Bunun için uygulamanın son kullanıcıya verilmeden önce üzerinde yeteri kadar test yapılması gerekmektedir. Büyük şirketler geliştirdikleri uygulamaları piyasaya sürmeden önce, o uygulamanın alfa ve beta sürümleri yayımlarlar. Bu uygulamaları da dünya üzerinde binlerce kişi test eder, ve buldukları hataları bildirirler. Tüm bu süreçlerden sonra uygulama piyasaya sürülür. Test aşaması çok önemli bir aşamadır. Bu yüzden Microsoft Visual Studio .NET’in 2005 versiyonunda birim test, fonkisyonel test, stres testi gibi bir çok test imkanı sunulmaktadır.Uygulama geliştirmede çokça yapılan hatalardan biri, null (VB.NET için nothing) kontrollerinin yapılmamasıdır. Bu hata öncelikle uygulamanın güvenilirliğini sarsar. Bu hatanın oluşması durumunda sistem tutarlı olmayan bir davranış gösterebilir. Genelikle uygulamalar bir “null pointer” hatası ile sonlandırılır. Ancak yine kodlamadan kaynaklanan bir diğer hata da gereksiz yere kullanılan “try-catch” bloklarıdır. Gereksiz yere “try-catch” kullanımı hem uygulamayı yavaşlatacaktır hem de güvenlik açıkları yaratacaktır.C#using System; using System.Collections; namespace SampleApp { class Sample { static void Main() { try { SampleClass sc=new SampleClass(); int i=sc.AddRecord("sampleRecord"); Console.WriteLine( sc.GetRecord(i).Length ); Console.WriteLine(sc.GetRecord(i-1).Length ); // Listeyi veri tabanına kaydet } catch{} } } class SampleClass { private ArrayList __alList; public SampleClass() { __alList=new ArrayList(); } public string GetRecord(int index) { return index<__alList.Count && index>-1 ? __alList[index].ToString() : null; } public int AddRecord(string record) { return __alList.Add(record); } } } VB.NETImports System Imports System.Collections Module SampleApp Sub Main() Try Dim sc As New SampleClass Dim i As Integer = sc.AddRecord("sampleRecord") Console.WriteLine(sc.GetRecord(i).Length) Console.WriteLine(sc.GetRecord(i - 1).Length) " Listeyi veri tabanına kaydet Catch ex As Exception End Try End Sub Public Class SampleClass Private __alList As ArrayList Public Sub New() __alList = New ArrayList End Sub Public Function GetRecord(ByVal index As Integer) As String If (index < __alList.Count And index > -1) Then Return __alList(index) End If Return Nothing End Function Public Function AddRecord(ByVal record As String) As Integer Return __alList.Add(record) End Function End Class End Module C++.NET#include "stdafx.h" #using using namespace System; using namespace System::Collections; public __gc class SampleClass { private: ArrayList __gc* __alList; public: SampleClass::SampleClass() { __alList=__gc new ArrayList(); } String __gc* SampleClass::GetRecord(int index) { return index<__alList->Count && index>-1 ? __alList->Item[index]->ToString() : NULL; } int SampleClass::AddRecord(String __gc* record) { return __alList->Add(record); } }; int _tmain() { try { SampleClass __gc* sc=__gc new SampleClass(); int i= sc->AddRecord(S"sampleRecord"); Console::WriteLine(sc->GetRecord(i)->Length); Console::WriteLine(sc->GetRecord(i-1)->Length); // Listeyi veri tabanına kaydet } catch(Exception __gc * ) {} return 0; } J#package SampleApp; import System.*; import System.Collections.*; public class SampleMain { public SampleMain() { } /** @attribute System.STAThread() */ public static void main(String[] args) { try { SampleClass sc = new SampleClass(); int i=sc.AddRecord("sampleRecord"); Console.WriteLine(sc.GetRecord(i).get_Length()); Console.WriteLine(sc.GetRecord(i-1).get_Length()); // Listeyi veri tabanına kaydet } catch(System.Exception ex) {} } } public class SampleClass { private ArrayList __alList; public SampleClass() { __alList=new ArrayList(); } public String GetRecord(int index) { return index<__alList.get_Count() && index>-1 ? __alList.get_Item(index).ToString() : null; } public int AddRecord(String record) { return __alList.Add(record); } } Örneğin bu kodda listede olmayan bir elemanın istendiği durumlarda indeks hatası almamak için geriye “null” değeri döndürülmüştür. Ancak bu fonksiyondan dönen değerin null olup olmadığının kontrolü yapılmamıştır. Aslında burada yapılan listeye yeni bir eleman ekleme ve en son eklenan iki elemanın uzunluğunun gösterilmesidir. Eğer ki listede iki ya da daha fazla eleman varsa program hatasız bir şekilde çalışacaktır. Peki ya yoksa.. Buradaki örnekte olmadığı durum gösterilmiştir. Program listeye eklenmesi gereken elemanı ekleyecek, en son eklenen kaydın uzunluğunu gösterecektir. Fakat sondan bir önceki elemanı gösteremeyecek, ve listeyi veri tabanına kaydedemeyecektir. Bu durumda “program bazen çalışıyor ya da bazen çalışmıyor” gibi kullanıcı şikayetleri gelecektir. Yine bu örnekte gereksiz yere kullanılan bir “try-catch” bloğu bulunmaktadır. Her hangi bir hata olduğunda program normal akışına devam edecektir belki ama gereksiz kullanılan bu “try-catch” bloğu programın tutarlı bir davranış göstermesini engelleyecektir. Yine çokça yapılan bir hata da tampon bellek(buffer) kullanımında ortaya çıkmaktadır. Bellek için ayrılan sabit uzunluğun aşılması çokça kullanılan bir saldırı türüdür(buffer overrun). Mümkün olduğunca dinamik boyutta tampon bellek kullanarak bu sorunu aşabiliriz. Yine çokça yapılan bir hata da, nesnelerin bellekten kaldırılması sırasında oluşan hatalardır. Bu hata özellikle multithread(çok işlem parçacıklı) uygulamalarda ortaya çıkmaktadır. Bir işlem parçacığı ortak kullanılan bir nesneyi bellekten kaldırırken aynı anda bir diğeri de onu bellekten kaldırmak isteyebilir. void Dispose() { if( _myObj != null ) { Cleanup(_myObj); _myObj = null; } } Bu örnekteki Dispose() metotu senkronize çalışacak şekilde gerçeklenmediği için, bir işlem parçacığı Cleanup metotunun içinde yapılması gereken işlemleri gerçeklerken, bir diğeri “_myObj!=null” koşulunu test ediyor olabilir. Bu durumda iki ayrı işlem parçacığı Cleanup metodunu çağıracaktır. Bu durumda bu metot iki defa çağrılacak ve gerekli kontroller yapılmadıysa “null pointer” hatası ile karşılaşılacaktır. Özellikle dosya işlemleri gerçeklendiği bir sınıfta sistemin yanlış dosyayı kapatmaya çalışmasına ve sistem çökmelerine kadar gidecek hatalı işler oluşabilir. Kurucu metotlarda güvenlik kontrolünün yapılması da çokça yapılan bir hatadır. Bu hata yine çok işlem parçacıklı ortamlarda sorun yaratacaktır. Güvenlik kontrolünü aşmaması gereken bir işlem parçacığı tüm güvenlik altyapınızın çökmesine yol açabilir. Mümkün oldukça kurucu metot üzerinde güvenlik kontrolü yapmaktan kaçının. Kurucu metotlar kullanılacak üye değişkenlerin ilk değerlerinin atandığı metotlar olarak kullanılmalıdır. Bir çok uygulamada göz ardı edilen bir durumda değişkenlerinin değerlerinde oluşan taşmalardır. Bu taşmalar bir çok işlemin hatalı sonuç üretmesine sebep olabilmektedir. Gerçek zamanlı çalışan sistemlerde bu tür bir hata belki bir hastanın yaşamını yitirmesine, vurulacak bir hedefin ıskalanmasına yol açmaktadır. Hatta yakın geçmişte bu hatadan ötürü uzaya gönderilen bir uydu düşmüştü. C#using System; namespace SampleApp { class Sample { static void Main() { int i=Int32.MaxValue; Console.WriteLine("i : {0} ",i.ToString()); i++; Console.WriteLine("i : {0} ",i.ToString()); } } } VB.NETImports System Module SampleApp Sub Main() Dim i As Integer = Int32.MaxValue Console.WriteLine("i: {0}", i) i += 1 Console.WriteLine("i: {0}", i) End Sub End Module C++.NET#include "stdafx.h" #using using namespace System; int _tmain() { int i=Int32::MaxValue; Console::WriteLine(i); i++; Console::WriteLine(i); return 0; } J#package SampleApp; import System.*; public class SampleMain { public SampleMain() { } /** @attribute System.STAThread() */ public static void main(String[] args) { int i =Int32.MaxValue; Console.WriteLine("i: "+i); i++; Console.WriteLine("i: "+i); } } Bu örnekte i değişkenin ilk değeri 2.147.483.647’dir. Üzerine bir eklenmesi sonucu pozitif tamsayıların en büyük değeri aşılacak ve yanlış bir sonuç ile karşılaşacaktır. Nitekim i’nin son değeri -2.147.483.648 ’dir. Yani 2.147.483.648 olması gereken değernegatif olarak bu değeri almıştır. Bu da yapılan bir çok hesaplamayı değiştirecek çok büyük hatalara sebep olabilecektir. Bu örnek C#, C++.NET ve J#’ta i’ye -2.147.483.648 değerini atarken, VB.NET’te taşma hatasına sebep olacaktır. Aynı şekilde çokça yapılan bir hata da sayıların tiplerinden kaynaklanmaktadır. Örneğin, 2/3 işleminin sonucu tam sayılar kümesi üzerinde 0’dır ve kalan 2’dir. Bu işlemin reel sayılar üzerindeki sonucu 0,6666 devirli sayısıdır ve kalan 0’dır.Daha çok Microsoft .NET Remoting ile kullanılan serileştirme işleminin iki temel tipi vardır: XML Serileştirme ve İkili(binary) Serileştirme. Serileştirme işlemi sırasında nesnenin o anki durumu olduğu gibi ikili serileştirmede, ikililerden oluşan bir metine; XML serileştirmede XML’e aktarılır. Nesne bu metin ile gerektiğinde tekrar oluşturulabilir. Bu oluşturma işlemi sırasında daha önceki değişkenlere tekrar eski değerleri atanır. XML Serileştirme işlemi sırasında oluşabilecek olan değer taşmaları, sayı bir değişkene geçerli bir sayı atanmaması gibi kontroller yapılmamaktadır. Buna karşın ikili serileştirme işlemi sırasında bu kontroller yapılmaktadır. Bu tür hataların oluşması muhtemel yerlerde ikili serileştirme kullanmaya özen göstermelisiniz. Son Söz: Visual Studio .NET gibi çok gelişmiş editörler aracılığı ile artık uygulama geliştirmek oldukça basit bir hale indirgenmiştir. Ancak temel programlama kuralları halen daha geçerlidir. Bir çok kişi geliştirdiği yazılımın gerçekten de güvenilir bir şekilde geliştirip geliştirmediği konusunda özeleştiri yapmaktan bile kaçınmaktadır. Oysaki sistem güvenliği gibi yazılım güvenliği de detaylardagizlidir. Detayları es geçmeyin, ve bu detaylara takılın. Sisteminizin en zayıf olduğu durumlar bu detaylar olacaktır. Bir sonraki makalede görüşünceye dek, güvende kalın.. İlgili Makaleler: Referanslar: MSDN KütüphanesiYazar : Yunus Emre ALPÖZENe-Posta : yunus.alpozen et msakademik.net

Döküman Arama

Başlık :

Kapat