folder Tahribat.com Forumları
linefolder Web sitesi Güvenliği, DOS - DDOS Saldırıları...
linefolder [Dokuman] - PHP Ile Basit SQL Injection Önleme Yöntemleri



[Dokuman] - PHP Ile Basit SQL Injection Önleme Yöntemleri

  1. KısayolKısayol reportŞikayet pmÖzel Mesaj
    Austen
    Austen's avatar
    Kayıt Tarihi: 13/Ağustos/2012
    Erkek

    SQL Injection Nedir?

    SQL Injection kısaca veritabanı işlemlerinizde sorgularınızın başka kişiler tarafından zararlı hale getirildiği işlemdir. Bu sorun genellikle kullanıcıdan gelen bilgilerin kontrol edilmeden, filtrelenmeden işleme konulması ile ortaya çıksada basit bir unutkanlık bile buna yol açabilir.

    SQL Injection sistemleriniz için oldukça tehlikelidir. Kullanılan yönteme göre kimi zaman veritabanınızın boşaltılması, kimi zamanda veritabanınızın bir yedeğini bir başkasının eline geçmesini sağlayabilir. Hatta içerik yönetim sistemine sahip bir sisteme sahipseniz yönetici hesabınızın başkasının eline geçmesi sonucu sisteminize geriye dönüşü olmayan hasarlar verilmesini sağlayabilir.

    Bu makalede SQL Injection hakkında dikkat edilmesi gereken temel başlıklardan söz edeceğim.

    Tarayıcı Tarafında Dikkat Edilmesi Gerekenler

     Dikkat edilmesi gereken 2 ana başlık var. Web sayfası tarafında dikkat edilmesi gerekenler ve sayfa işlenirken dikkat edilmesi gerekenler. Bu başlıkta web sayfasında, yani veri daha işleme konulmadan dikkat edilmesi gerekenlerden bahsedeceğim.

    Sayfalarımızda temel girdi çıktı işlemlerini formlar veya form elemanları aracılığı ile gerçekleştiriyoruz. Mesela basit bir form düşünelim. Formda basit alanlarımız olsun. Kullanıcı adı ve  şifre girilmesini istediniz. Siz bu alanlara yalnızca belirli sayıda karakterlerin girilmesini istiyorsunuz. Html tagleri ile limitlerinizi tanımladınız. Artık kullanıcı adı ve şifre bölümlerine örneğin 10 karakter girilmesini sağlıyorsunuz. Ama baktınız bu yetmiyor. Kullanıcı adına yalnızca alfanumerik karakterler girilsin dediniz. Js -javascript- ile bunu da gerçekleştirdiniz. Artık formunuz gayet düzgün bir şekilde, istediğiniz gibi işliyor... Böyle düşünüp işinize devam ederseniz yanılırsınız. Bu tanımlamalara hiçbir zaman güvenmemeniz gerekiyor. Kullanıcı sayfasının üzerinde firebug ve benzeri programlar aracılığı ile her türlü manipülasyon (güncelleme, değiştirme vs) işlemleri yapılabiliyor. Siz 10 karakter sınırı koyarsınız, kullanıcı bunu alır 20 yapar. Siz buna güvenip sayfanız işlenirken herhangi başka bir sorgu eklemezseniz kullanıcı rahatlıkla 20 karakterli bilgisini veritabanına kaydetmeyi başarır. Js ile sadece harf girilmesini sağladığınızı düşündünüz. Kullanıcı tarayıcısındaki js desteğini kapattığı an tüm sorgunuz de-aktif hale gelir. Bunlar bu örnekte basit gibi görünsede bir çok kişi bu hatayı yapıp daha sonrasında formu işlerken herhangi bir kontrol yapmadan geçiştiriyor. Bu da takdir edersiniz ki çok büyük bir güvenlik açığına sebep oluyor.

    Ayrıca sayfalarınıza başka bir dil veya sayfa aracılığıyla da bilgi gönderilebilir. Alan adları uygun şekilde ayarlandıktan sonra bir başkası kendi hazırladığı sayfayı sizin sayfanızdan geliyormuş gibi gönderebilir ve çalışmasını sağlayabilir. Sunucu tarafında gerekli önlemleri almayıp sayfanızdaki önlemlere güvendiğinizde yine çok büyük bir kapıyı hırsızlar için açık bırakmış oluyorsunuz.

    Sayfa İşlenirken Dikkat Edilmesi Gerekenler

    Yukarıda sayfamızı hazırlarken dikkat etmemiz gerekenleri görmüştük. Şimdi bu sayfayı sunucu tarafında işlerken dikkat etmemiz gerekenlerden bahsedelim.

    Üstteki örnekten devam edelim. Formumuz yine kullanıcı adı ve şifre alanlarını içersin. Kullanıcı adı ve parola alanlarına max. 10 karakter girilmesini ve kullanıcı adında yalnızca alfanumerik karakterlerin girilmesini isteyelim.

    Bunu sunucu tarafında nasıl sorgulayabiliriz diyebilirsiniz. PHP'nin yerleşik kütüphanesinde bu işlemler için bir dizi komut tanımlıdır. Bunlardan isteğinize uygun olanları kullanabilirsiniz. Ayrıca bu komutlar sizin için yetersizse regular expression (düzenli ifadeler) kullanarak ve tanımlayarak istediğiniz kontrolün yapılmasını sağlayabilirsiniz. PHP'nin içindeki bazı komutları ve ne işe yaradıklarından bahsedelim.

    Altta gösterdiğimiz komutları kullanabilmeniz için php versiyonunuzun en az 4.0.4 olması gerekmektedir. Ayrıca fonksiyonlar geriye dönüş değeri olarak true/false döndürür.

    ctype_alnum(): Girdinin tüm karakterlerinin alfanumerik karakterlerden oluşup oluşmadığını sorgular. Alfanumerik; a-z arası alfabe karakterleri (Türkçe karakterler hariç) ve 0-9 arası rakamları kapsar.

    http://php.net/manual/tr/function.ctype-alnum.php

    ctype_alpha(): Girdinin tüm karakterlerinin alfabetik karakterlerden oluşup oluşmadığını sorgular. Alfabetik karakterlerin içerisinde Türkçe karakterler yer almaz. Türkçe karakterlerle ilgili sorgunun nasıl yapılacağını altta ayrıca göstereceğim.

    www.php.net/manual/tr/function.ctype-alpha.php

    ctype_lower(): Girdinin tüm karakterlerinin küçük harflerden oluşup oluşmadığını sorgular. ctype_upper() tam tersi, büyük harfleri sorgular.

    http://www.php.net/manual/tr/function.ctype-lower.php

    http://www.php.net/manual/tr/function.ctype-upper.php

    Ctype komutları dışında işinize yarayabilecek diğer fonksiyonlar şöyledir.

    is_float(): Girdinin float/double tipinde (10.2 gibi) olup olmadığını sorgular.

    http://se2.php.net/manual/tr/function.is-float.php

    is_bool(): Girdinin mantıksal true/false tipinde olup olmadığını sorgular. 1/0 gibi değerler false döner.

    http://se2.php.net/manual/tr/function.is-bool.php

    empty(): Girdinin boş olup olmadığını sorgular. Aşağıdaki karakterler boşluk olarak ele alınır;

    1 0 (bir tamsayı olarak 0)
    2 "" (boş bir dizge)
    3 "0" (bir string olarak 0)
    4 NULL
    5 FALSE
    6 array() (boş bir dizi)
    7 var $var; (bir sınıf içinde bir değeri olmaksızın bildirilmiş bir değişken)

    http://se2.php.net/manual/tr/function.empty.php

    is_int(): Girdinin tamsayı olup olmadığını sorgular.

    http://se2.php.net/manual/tr/function.is-int.php

    is_numeric(): Girdinin sayısal bir tür olup olmadığını sorgular. Bilimsel gösterimler (1e5), tamsayılar ve noktalı sayıları kapsar.

    http://se2.php.net/manual/tr/function.is-numeric.php

    isset(): Girdide belirtilen değişkenin tanımlı olup olmadığını sorgular. Değişken null değer içeriyorsa da false döndürür.

    http://se2.php.net/manual/tr/function.isset.php

    strlen(): Girdinin karakter sayısı uzunluğunu döndürür.

    http://php.net/manual/en/function.strlen.php

    Yerleşik, işinize yarayabilecek komutlar ve ne işe yaradıklarından bahsettik. Üstte alpha ve alnum komutlarının Türkçe karakterlerini sorgulamadığını söylemiştim. Şimdi onları nasıl kontrol edebileceğinizden ve birkaç farklı sorgudan bahsedelim.

    Eğer girdinin içerisinde Türkçe karakter geçip geçmediğini sorgulamak isterseniz;

    1 if(preg_match("/[ığüşçöİ]/i","kontrol edilecek girdi"))
    2 echo "Türkçe karakter içeriyor";

    Eğer girdinin yalnızca Türkçe karakterleri içerecek şekilde alfabetik karakterlerden oluşup oluşmadığını sorgulamak isterseniz;

    1 if(preg_match("/^[a-zığüşçöİ]+$/iu","kontrol edilecek girdi"))
    2 echo "Alfabetik karakterlerden oluşuyor";

    Üstteki işlevin alfanumerik için olanı;

    1 if(preg_match("/^[a-z0-9ığüşçöİ]+$/iu","kontrol edilecek girdi"))
    2 echo "Alfanumerik karakterlerden oluşuyor";

    Bu fonksiyonlar neredeyse tüm form işlemlerinize yetecek, güvenilir fonksiyonlardır. Gelen verinin hangi tipte ve nasıl olmasını istiyorsanız bunlardan uygun olanlar ile sınamamız gerekiyor.

    Verilerin Filtrelenmesi

    Script tarafında yapmamız gereken ikinci işlem verilerimizi filtreleme işlemi olmalıdır. Bir üst maddede verilerimizin tiplerini ve biçimlerini sorgulamayı öğrenmiştik. Bu aşamada ise verilerimizi kullanırken yani veritabanı ile iletişime girerken yapmamız gerekenlerden bahsedeceğim.

    Bu iki madde aslında birbirlerini tamamlayan maddelerdir. İkisinden birisini atlamanız halinde bir açık kapı bırakmış olursunuz. Benim size önerim bu iki maddedeki kurallara uyup o şekilde yolunuza devam etmeniz olur. Başkaları sizin sisteminize sızmak için sizin yaptığınız hataları, bıraktığınız arka kapıları ararlar. Eğer tembellik edip, bunu da bırakırsam bir şey olmaz bulamazlar şeklinde düşünürseniz ileride olası bir durumun tohumlarını ekmişsiniz demektir.

    Filtreleme aşamalarını maddeleyecek olursak;

    1.      Kullanıcıya hiçbir zaman güvenmeyin..En ufak ihtimali dahi gözden kaçırmadan dikkatli bir şekilde yolumuza devam etmemiz gerekiyor. En büyük düşmanımız kullanıcılarımızdır.

    2.      Veritabanındaki metinsel alanlara girdi yaparken girdileri tek tırnakların arasına alın.

    1 insert into test (isim,soyisim) values ('test','deneme')

    3.      Sorgulardaki db ismi, tablo adı, kolon adları, takma isimler gibi tanımlayıcılarda yatık tırnak (`) kullanın.

    1 select `isim`,count(*) as `toplam` from `tablomuz` ...

    4.      Tüm girdilere kaçış (escape) işlemi uygulayın. Bu işlem girdinizdeki veritabanına tehdit oluşturabilecek karakterlerin önlenip zararsız hale getirilmesini sağlar.

    Mysql veritabanı için: mysql_real_escape_string(girdi)

    5.      Sorgularda özel anlamı bulunan % ve _ karakterlerine ayrıca escape işlemi uygulayın. addcslashes() komutu 2. parametrede belirtilen karakterlere escape işlemi uygulayan yararlı bir komuttur.

    1 $girdi=addcslashes($girdi,”%_”)
    2 select xx from `tablom` where isim like '$girdi%'

    6.      Veritabanındaki sayısal alanlara girdi yaparken verinin belirttiğiniz türde olduğundan emin olmak için intval() tarzı komutları kullanın. intval belirtilen girdiyi tam sayıya çevirir. Float türü için floatval() komutunu kulanabilirsiniz.

    7.      Sorgumuzda “order by girdi” şeklinde bir ifade kullanırken girdide belirtilen alan adının tablodaki alanlardan bir tanesi olup olmadığını sorgulayın.

    1 $res=mysql_query(“show columns from tablomuz”);
    2 while($row=mysql_fetch_row($res))
    3 $tabloAlanlari[]=$row[0];
    4 if(!in_array($girdi,$tabloAlanlari))
    5 $girdi=”belirleyeceğinizDefaultKolonAdı”;

    8.      Veritabanınızdan sadece sayfanızda kullanacağınız kadar bilgiyi alın.

    9.      SQL'e ait bazı özel kavramları ASCII karşılıklarıyla, emsalleriyle güncelleyin.

    01 $girdi=str_ireplace("where","&#119here",$girdi);
    02 $girdi=str_ireplace("drop","&#100rop",$girdi);
    03 $girdi=str_ireplace("select","&#115elect",$girdi);
    04 $girdi=str_ireplace("union","&#117nion",$girdi);
    05 $girdi=str_ireplace("like","&#108ike",$girdi);
    06 $girdi=str_ireplace("update","&#117pdate",$girdi);
    07 $girdi=str_ireplace("delete","&#100elete",$girdi);
    08 $girdi=str_ireplace("insert","&#105nsert",$girdi);
    09 $girdi=str_ireplace("show","&#115how",$girdi);
    10 $girdi=str_ireplace("alter","&#97lter",$girdi);

    Bunlar düz kullanımlarda geçerli olan kurallardır. Birde PHP'nin içerisinde yerleşik olarak bulunan PDO dediğimiz hazır veritabanı yapıları mevcuttur. Bunları kullanarak zahmetsiz, seri ve güvenilir bir şekilde sorgularınızı hazırlayabilirsiniz. PDO'yu kullanabilmek için PHP versiyonunuzun en az 5 olması gerekmektedir.

    PHP PDO: http://tr.php.net/manual/tr/book.pdo.php

    Özetle benim söyleyeceklerim bu kadar. Bu kurallara dikkat ettiğiniz müddetçe, veritabanınızın güvenliğini de gerekli şekilde sağladınız müddetçe pek problem yaşamayacağınızı düşünüyorum.

    Eksiklerim, atladığım noktalar elbette olabilir. Bunlara dikkat ettiğiniz halde yine sorunlar yaşayabilirsiniz. Bunlar sizin kodlamalarınıza, davranışlarınıza, güvenlik bilginize göre ortaya çıkabilir. Sisteminizin sorunsuz olmasını istiyorsanız mutlaka bilgili bir veya birkaç kişiye sisteminizi test ettirin. Hatta imkanınız varsa sisteminizi kullanıcı testlerine sokun. En iyi sorun bulma yöntemi bu şekildedir. Ne kadar çok kişi sisteminizi test ederse o kadar sorunsuz bir sisteme sahip olursunuz.

     

  2. KısayolKısayol reportŞikayet pmÖzel Mesaj
    aLman
    aLman's avatar
    Banlanmış Üye
    Kayıt Tarihi: 02/Aralık/2007
    Erkek

    makale için teşekkürler faydalı bi bilgi programcı arkadaslarımız için

  3. KısayolKısayol reportŞikayet pmÖzel Mesaj
    eyuperol
    eyuperol's avatar
    Kayıt Tarihi: 13/Şubat/2011
    Erkek

    emeğine sağlık


    İnsanları ( küçük-büyük, yaşlı-genç, tanıdık-tanımadık, eşdost-sevgili vs vs ) sevin, saygınızı gösterin ama kesinlikle insanlara acımayın. Çünkü acıdığınız anda acınacak duruma düşüyorsunuz
  4. KısayolKısayol reportŞikayet pmÖzel Mesaj
    ZINDIK
    ZINDIK's avatar
    Kayıt Tarihi: 31/Mart/2007
    Erkek

    Güzel döküman ellerine sağlık.

     

  5. KısayolKısayol reportŞikayet pmÖzel Mesaj
    angelofhope
    angelofhope's avatar
    Banlanmış Üye
    Kayıt Tarihi: 16/Temmuz/2008
    Erkek

    eline sağlık hocam.


    Programming is our Religion, Algorithms are Our Bible & We ./make Our Own GOD
  6. KısayolKısayol reportŞikayet pmÖzel Mesaj
    gencbeyin
    gencbeyin's avatar
    Banlanmış Üye
    Kayıt Tarihi: 24/Ağustos/2005
    Erkek

    aşağıdaki kodu kullanıyorum, dw içindeki default fonksiyon.

     <?

     

    f (!function_exists("GetSQLValueString")) {

    function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") 

    {

      if (PHP_VERSION < 6) {

        $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue;

      }

     

      $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue);

     

      switch ($theType) {

        case "text":

          $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";

          break;    

        case "long":

        case "int":

          $theValue = ($theValue != "") ? intval($theValue) : "NULL";

          break;

        case "double":

          $theValue = ($theValue != "") ? doubleval($theValue) : "NULL";

          break;

        case "date":

          $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";

          break;

        case "defined":

          $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue;

          break;

      }

      return $theValue;

    }

    }

     

    ?>

    gencbeyin tarafından 19/Ağu/12 13:41 tarihinde düzenlenmiştir
  7. KısayolKısayol reportŞikayet pmÖzel Mesaj
    Austen
    Austen's avatar
    Kayıt Tarihi: 13/Ağustos/2012
    Erkek
    gencbeyin bunu yazdı

    aşağıdaki kodu kullanıyorum, dw içindeki default fonksiyon.

     <?

     

    f (!function_exists("GetSQLValueString")) {

    function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") 

    {

      if (PHP_VERSION < 6) {

        $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue;

      }

     

      $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue);

     

      switch ($theType) {

        case "text":

          $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";

          break;    

        case "long":

        case "int":

          $theValue = ($theValue != "") ? intval($theValue) : "NULL";

          break;

        case "double":

          $theValue = ($theValue != "") ? doubleval($theValue) : "NULL";

          break;

        case "date":

          $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";

          break;

        case "defined":

          $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue;

          break;

      }

      return $theValue;

    }

    }

     

    ?>

    Keşke bunun gibi tek bir fonksiyon hazırlayabilsekte sorunlarımız kalmasa ama yetmiyor :) Üstte nedenlerini açıkladım. Sadece bunu kullanırsanız yine açık kapınız kalıyor.

  8. KısayolKısayol reportŞikayet pmÖzel Mesaj
    Arlong
    Arlong's avatar
    Kayıt Tarihi: 14/Şubat/2005
    Erkek

    tavsiyem bunlarla uğraşmak yerine prepared statement kullanın.

Toplam Hit: 2689 Toplam Mesaj: 8