Makale Özeti

Web sitenizde üyelik sistemi yoksa ve bir şekilde ziyaretçilerinizin veri girişi yapmasına olanak tanıyorsanız otomatik veri girişi saldırılarından korunmanız şart. İster blog sitenizdeki yorum araçları olsun ister kişisel web sitenizdeki ziyaretçi defteri, bir gün, bir anda binlerce mesaj ile karşılaşabilirsiniz. Bu makalemizde bizi bu tarz zor durumlardan kurtarmaya yönelik bir sunucu kontrolünden, AJAX Control Toolkit içerisindeki NoBot kontrolünden bahsediyor olacağız.

Makale

Eğer bir blog siteniz varsa ilk yaşadığınız sorun büyük ihtimal ile makalelerinize gelen otomarik yorumların silinmesi ile uğraşmak olacaktır. Her tür web sitesinde üyelik sistemi olmadan ziyaretçilerden veri girişi alan yerlere yönelik saldırılar yapmak mümkün. Bu durum çoğu zaman karşılaştığımız sorunlardan biri. Çözümlerden ilki CAPTCHA kontrolleri; bir resim olarak sayfaya yerleştirilen yamuk yumuk yazıları site ziyaretçilerinize okutarak başka bir kutucuğa yazmalarını istemek aslında pek de ziyaretçilerin hayatını kolaylaştıran bir yapı değil. Güvenlik arttıkça kolay kullanırlılığın azaldığı bir gerçek. Peki hiç kullanıcıyı rahatsız etmeden neler yapabiliriz?

İlk olarak web sitemizde veri girişi yapılan sayfaların ne kadar süre sonra PostBack olduğunu kontrol edebiliriz. Eğer bir sayfa iki saniyeden kısa bir sürede PostBack oluyorsa büyük ihtimal ile sayfa içerisinde yazılması gereken verileri bir insan yazmamıştır ve bizim düşmanımız olan otomarik yazılımlardan biri kullanılmıştır. İkinci seçenek ise her IP adresi üzerinden yapılabilecek PostBack sayısını sınırlandırmak olabilir. Tabi bu sınırlandırmayı da belirli süre aralıklarında sıfırlamamız gerekir, aksi halde PostBack kotasını dolduran kişi bir daha işlem yapamayacaktır. Son olarak daha da ileri giderek söz konusu form verisinin gerçekten bir internet tarayıcısından gönderiliyor olup olmadığını kontrol etmek için istemci taraflı JavaScript işlemleri yaptırarak sonucunu aynı işlemleri sunucu tarafında da yaptırarak karşılaştırabiliriz. Böylece eğer sonuçlar tutmuyorsa bir şekilde istemci tarafında JavaScript kodları düzgün çalıştırılamamış demektir. "Ya internet tarayıcısı JavaScript desteklemiyorsa" diye itiraz ettiğinizi duyar gibiyim. O zaman zaten sitemizi pek de kullanamayacaklar, çünkü artık herşey için AJAX kullanıyoruz :)

Tüm bu kontrolleri tek tek gelin yapalım diyeceğimi sanıyorsanız aldanıyorsunuz. Bu işlemlerin hepsini bizim yerimize yapan NoBot sunucu kontrolünü inceleyerek hızlı bir şekilde ilerliyor olacağız. AJAX Control Toolkit içerisinde sunucu kontrollerinden NoBot kontrolünü sayfamıza eklediğimizde aşağıdaki mark-up (XHTML) kodu ile karşılaşıyoruz.

<ajaxToolkit:NoBot ID="NoBot1" runat="server" />

NoBot kontrolü içerisinde düzenleyebileceğimiz toplam üç farklı ayar bulunuyor. Bunlardan ilki olan CutoffMaximumInstances özelliğine aynı IP adresi üzerinden toplam kaç PostBack yapılabileceğine dair limitimizi aktarıyoruz. Söz konusu limitin belirli aralıklarla sıfırlanması gerektiğinden bahsetmiştik, böylece aynı IP adresi üzerinden kullanıcılar önceden sınıra dayanmış olsalar bile belirli bir süre sonra tekrar PostBack yapabilecekler. Bahsettiğimiz değeri, yani sıfırlanma süresini de CutoffWindowSeconds özelliğine aktarıyoruz. Aşağıdaki örneğimizde bir IP adresinden 50 saniye içerisinde en fazla 10 PostBack yapılabilecek. Son olarak ResponseMinimumDelaySeconds özelliğine aktardığımız sayısal değer ise aslında sayfanın ilk açılışından ne kadar süre sonra PostBack yapılabileceğine dair saniye cinsinde veriyi saklıyor. Örneğimizde sayfamız beş saniyeden önce PostBack yapılamayacak.

<ajaxToolkit:NoBot ID="NoBot1" runat="server"
        CutoffMaximumInstances="10" CutoffWindowSeconds="50" ResponseMinimumDelaySeconds="5"/>

Örneğimize ayrıca bir etiket (Label) bir de düğme (Button) ekleyeceğiz. Böylece sayfanın PostBack olmasını sağlarken varsa bir hata durumunu da etiket içerisine yazdırabileceğiz. Sayfamızın tam HTML kodu aşağıdaki şekilde sonuçlanıyor.

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <ajaxToolkit:ToolkitScriptManager ID="ScriptManager1" runat="server" />
        <div>
        <asp:Label ID="label1" runat="server"></asp:Label><br />
        <asp:Button ID="Button1" runat="server" Text="TIKLA" /><br />
        <ajaxToolkit:NoBot ID="NoBot1" runat="server"  CutoffMaximumInstances="10"
             CutoffWindowSeconds="50" ResponseMinimumDelaySeconds="5" />

        </div>
    </form>
</body>
</html>

Tüm bu düzenlemeleri tamamladıktan sonra sayfanın geçerli veya geçersiz bir PostBack işlemine uğrayıp uğramadığını kontrol etmek için aşağıdaki kodu yazıyor olacağız.

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Page.IsPostBack Then
            Dim durum As AjaxControlToolkit.NoBotState
            If NoBot1.IsValid(durum) Then
                label1.Text = "Herhangi bir sorun yok"
            Else
                label1.Text = "Sorun var. Sebep:" & durum.ToString
            End If
        End If
    End Sub

Yukarıdaki kod içerisinde özellikle dikkat etmemiz gereken birkaç nokta var. İlk olarak Page_Load durumunda, yani sayfa her yüklendiğinde yukarıdaki kod çalışacağı için Page.IsPostBack ile sayfanın bir PostBack sonucu mu yüklendiğini kontrol ediyoruz. Eğer sayfa bir PostBack sonucu yüklenmiyorsa ilk defa açılıyor demektir ki bu durumda bizim NoBot ile bir kontrol yapmamıza gerek yok. Eğer sayfada PostBack olmuş ise sıra geliyor NoBot'un bize sunacağı raporu incelemeye. NoBot sunucu kontrolüne ait IsValid metodu ile söz konusu PostBack'in geçerli veya geçersiz olup olmadığını öğrenebiliriz fakat ek olarak eğer PostBack geçersiz ise nedenini de öğrenerek kullanıcıya bir uyarı göstermekte fayda var. Bunun için NoBot ile kontrolü yaparken bir NoBotState değişkenini parametre olarak vermemiz gerekiyor. Kodumuzun başında durum adında bir NoBotState değişkeni tanımlayarak IsValid metoduna parametre olarak veriyoruz. Eğer IsValid değil ise, yani PostBack geçersiz ise durum değişkenine ait mesajı ToString metodu ile Label1 içerisine yazdırıyoruz. Artık kodumuzu çalıştırarak denemeler yapmaya başlayabiliriz. Siz örneğinizde NoBot'un ayarlarını değiştirerek farklı denemeler yapabilir ve aldığınız hata mesajlarını değerlendirebilirsiniz.

Yazımın en başında kontrol mekanizmalarından bahsederken ilginç birşeyden daha bahsetmiştim. İstemci taraflı JavaScript işlemleri yaptırarak sunucu tarafında kontrol edebiliriz demiştim. Gelin şimdi bu işlemi NoBot ile nasıl yapabileceğimize göz atalım. Söz konusu düzenlemeyi NoBot kontrolüne ait GenerateChallengeAndResponse durumunda (event) yapmamız gerekiyor.

    Protected Sub NoBot1_GenerateChallengeAndResponse(ByVal sender As Object, ByVal e As AjaxControlToolkit.NoBotEventArgs) Handles NoBot1.GenerateChallengeAndResponse
        Dim gizlipanel As New Panel
        gizlipanel.ID = "gizlipanel"
        Dim rastgele As New Random
        gizlipanel.Height = rastgele.Next(1, 300)
        gizlipanel.Width = rastgele.Next(1, 300)
        gizlipanel.Style.Add(HtmlTextWriterStyle.Visibility, "hidden")
        gizlipanel.Style.Add(HtmlTextWriterStyle.Position, "absolute")
        NoBot1.Controls.Add(gizlipanel)
        e.ChallengeScript = String.Format("var e = document.getElementById('{0}'); e.offsetWidth * e.offsetHeight;", gizlipanel.ClientID)
        e.RequiredResponse = (gizlipanel.Width.Value * gizlipanel.Height.Value).ToString
    End Sub

Yazdığımız kodu detaylıca inceleyelim. İlk satırda gizlipanel adında bir Panel değişkeni yaratıyoruz. Bu aslında bizim araç çubuğundan alıp sayfamıza koyduğumuz panellerden farksız. Önemli nokta bunu kod ile yaratıyor olmamız. Panelimize bir ID verdikten sonra rastgele Random metodu ile 1 ile 300 arasında rastgele sayılar üreterek panelin yükseklik ve genişlik değerlerini düzenliyoruz. Böylece elimizde yüksekliği ve genişliği rastlantısal olarak ayarlanmış bir panel var. Sıra geldi bu paneli görünmez hale getirmeye. Maalesef burada direk panelin Visible özelliğini kullanamayız. Bu paneli istemci tarafında, üzerinde JavaScript ile işlem yapılmak üzere yaratıyoruz. JavaScript'in panele ulaşabilmesi için sayfanın HTML kodu içerisinde yer alması gerekir. Oysa Visible özelliği ile Panel'i saklarsak ASP.NET bunu HTML kodu olarak istemciye yollamayacaktır. Biz bunun yerine CSS özellikleri kullanarak panelimizi saklayacağız. Visibility özelliğini hidden, position özelliğini de absolute yaparak panelimizin sayfada gözükmesini ve yer kaplamasını engellemiş oluyoruz. Son adım olarak da panelimizi NoBot sunucu kontrolünün içine Controls.Add metodu ile ekliyoruz.

        e.ChallengeScript = String.Format("var e = document.getElementById('{0}'); e.offsetWidth * e.offsetHeight;", gizlipanel.ClientID)
        e.RequiredResponse = (gizlipanel.Width.Value * gizlipanel.Height.Value).ToString

Bu iki satırı özellikle tekrar yazdım. Çünkü üzerinde bolca düşünmemiz gerekiyor. Panelimizi yaratıp NoBot kontrolünün içerisine eklediğimizde artık sayfamızda rastlantısal yükseklik ve genişlik değerleri ile oluşturulmuş bir panel var demektir. Şimdi sıra geldi bu paneli kullanarak istemci tarafında JavaScript işlemleri yapmaya. e.ChallengeScript özelliğine istemci tarafında çalışacak JavaScript kodunu aktarıyoruz. JavaScript kodumuzu incelersek, bir e değişkenine söz konusu Paneli bularak aktardığını görebiliriz. document.getElementByID metodunun içerisine Panelin ClientID'si yani sayfadaki HTML ID bilgisi veriliyor ve geriye dönen obje e değişkenine aktarılıyor. Bu adım tamamlandıktan sonra da söz konusu panelin yüksekliği ve genişliği birbiri ile çarpılıyor. İstemci tarafındaki işlemimiz bu kadar. Sıra geldi sunucu tarafına. Sunucu tarafında da aynı işlemi yaptırmamız gerekiyor ki NoBot bu iki işlemin sonuçlarını karşılaştırsın ve bize rapor versin. Sunucu tarafındaki işlemlerin sonucunu e.RequiredResponse özelliğine aktarmalıyız. Bu nedenle direk bizim gizlipanel değişkenimizin genişlik ve yükseklik değerlerini çarparak sonucu aktarıyoruz.

Code-Behind sayfamızın son hali aşağıdaki şekilde sonlanıyor.

Partial Class _Default
    Inherits System.Web.UI.Page
 
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Page.IsPostBack Then
            Dim durum As AjaxControlToolkit.NoBotState
            If NoBot1.IsValid(durum) Then
                label1.Text = "Herhangi bir sorun yok"
            Else
                label1.Text = "Sorun var. Sebep:" & durum.ToString
            End If
        End If
    End Sub
 
    Protected Sub NoBot1_GenerateChallengeAndResponse(ByVal sender As Object, ByVal e As AjaxControlToolkit.NoBotEventArgs) Handles NoBot1.GenerateChallengeAndResponse
        Dim gizlipanel As New Panel
        gizlipanel.ID = "gizlipanel"
        Dim rastgele As New Random
        gizlipanel.Height = rastgele.Next(1, 300)
        gizlipanel.Width = rastgele.Next(1, 300)
        gizlipanel.Style.Add(HtmlTextWriterStyle.Visibility, "hidden")
        gizlipanel.Style.Add(HtmlTextWriterStyle.Position, "absolute")
        NoBot1.Controls.Add(gizlipanel)
        e.ChallengeScript = String.Format("var e = document.getElementById('{0}'); e.offsetWidth * e.offsetHeight;", gizlipanel.ClientID)
        e.RequiredResponse = (gizlipanel.Width.Value * gizlipanel.Height.Value).ToString
    End Sub
End Class

Gördüğünüz gibi tüm düzenlemeleri yaparak ciddi bir kontrol mekanizması kurabildik. Artık istemci tarafındaki JavaScript işlemlerinin sonucu sunucu tarafındaki ile karşılaştırılacak, PostBack süreleri kontrol edilecek ve IP üzerinden de PostBack sayısı kontrolü yapılacak. En güzeli biz tüm bunları yaparken sitemizin ziyaretçisi hiçbirşey hissetmeyecek. Ek olarak siz de isterseniz aşağıdaki şekilde NoBot kontrolünün IsValid metodu ile elde ettiğimiz durum değişkenini farklı hatalar için kontrol ederek Türkçe uyarılar verebilirsiniz.

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Page.IsPostBack Then
            Dim durum As AjaxControlToolkit.NoBotState
            If NoBot1.IsValid(durum) Then
                label1.Text = "Herhangi bir sorun yok"
            Else
                Select Case durum
                    Case AjaxControlToolkit.NoBotState.InvalidAddressTooActive
                        label1.Text = "Sorun var. Sebep:IP adresi üzerinden çok sayıda talep geldi."
                    Case AjaxControlToolkit.NoBotState.InvalidResponseTooSoon
                        label1.Text = "Sorun var. Sebep:Çok hızlı sayfa yenilenme talebi geldi."
                End Select
            End If
        End If
    End Sub

Hepinize kolay gelsin.

Daron Yöndem
MCPD, MCITP, MCTS, MCSD, MCAD
MCDBA, MCP, ACP, ICSD, IEL'03
http://daron.yondem.com