Makale Özeti

Bu makalemde internet sitelerinde form doldururken veya login olurken flood saldırılarına karşı kullanımı gittikçe artmaya başlayan güvenlik resimlerinin C# ile nasıl oluşturulabileceğine dair örnek bir uygulama yapacağım. Ardından da örnek bir ASP uygulamasında kullanımını özetleyeceğim. Uygulamamız bittiğinde aşağıdaki resme benzer resimler oluşturabilir hale gelecek ve böylece sitemizdeki formlar verilerin otomatik olarak post edilmesini sağlayan programlar ile yapılan flood saldırılarına karşı korunacak

Makale

    Merhabalar,

    Bu makalemde internet sitelerinde form doldururken veya login olurken flood saldırılarına karşı kullanımı gittikçe artmaya başlayan güvenlik resimlerinin C# ile nasıl oluşturulabileceğine dair örnek bir uygulama yapacağım. Ardından da örnek bir ASP uygulamasında kullanımını özetleyeceğim. Uygulamamız bittiğinde aşağıdaki resme benzer resimler oluşturabilir hale gelecek ve böylece sitemizdeki formlar verilerin otomatik olarak post edilmesini sağlayan programlar ile yapılan flood saldırılarına karşı korunacak:



    Resimde rastgele seçilecek olan dört arkaplana yerleştirilmiş altı rakam ve/veya harften oluşan bir yazı, bir poligon ve beş çizgi olacak ve bu bahsedilen öğelerden hiçbiri resmin sınırlarının dışında kalmayacak.


    Şimdi önce çizgileri ve poligonu oluşturduğumuz CizgilerVePoligonuOlustur metodunu inceleyelim:

        private void CizgilerVePoligonuOlustur(Image img, Random rnd)
        {
            Graphics
g = Graphics.FromImage(img); //Graphics nesnemizi oluşturuyoruz.
           
//ilk olarak 5 tane çizgimizi oluşturalım:
           
for (int i = 0; i < 5; i++)
            {
                int
x1 = rnd.Next(0, img.Width);
                int
x2 = rnd.Next(0, img.Width);
                int
y1 = rnd.Next(0, img.Height);
                int
y2 = rnd.Next(0, img.Height);
                Pen
p2 = new Pen(Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255)), 3);
                g.DrawLine(p2, x1, y1, x2, y2);
            }
           
//poligonu oluşturalım:
           
List<Point> points = new List<Point>();
            for (int i = 0; i < rnd.Next(3, 10); i++)
            {
                Point
po = new Point(rnd.Next(0, img.Width), rnd.Next(0, img.Height));
                points.Add(po);
            }
            Pen
p = new Pen(Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255)), 2);
            g.DrawPolygon(p, points.ToArray());
        }

    Metoddaki ilk döngüde 5 Graphics nesnesinin DrawLine metodunu kullanarak 5 tane çizgi oluşturacağız. Bu yüzden öncelikle metoda parametre gönderdiğimiz Image tipinden olan img değişkeni ile Graphics nesnemizi oluşturuyoruz (g).

    DrawLine metodunun overload'larından bir tanesini kullanarak da çizgilerimizi oluşturmaya başlayacağız. Bunun için çizginin renginin ve kalınlığının belirlendiği bir Pen nesnesini p2 değişkeni ile tanımlıyoruz. Renklerin random olarak tanımlanması için Color.FromArgb metodunun içinde random olarak 0-255 değerleri arasında red, green ve blue değerlerini atıyoruz.

    Çizginin başlayıp biteceği iki noktanın x ve y koordinatlarını oluşturmak için de x1, y1, x2, y2 noktalarını tanımlıyoruz. Burada önemli olan bir nokta çizeceğimiz çizgilerin resmimizin dışında oluşturulmamaları gerektiğidir. Bu yüzden noktalara random değer atarken maximum değerlerine resmimizin genişlik ve yükseklik değerlerini veriyoruz.

    Beş çizgiyi oluşturmak için bir for döngüsü içinde 4 koordinatı ve Pen nesnesini (p2) oluşturuyoruz.

    Bir sonraki döngüde ise poligonumuzu oluşturacağız. En az 3, en fazla 10 köşeli bir poligon oluşturacağız ve kaç köşeli olacağı random olarak döngüye girmeden önce belirlenecek.

    Döngünün dışında Point nesnesi alan bir list tanımlıyoruz. Döngünün içinde poligonun random olarak belirlenecek köşe sayısı kadar nokta oluşturuyoruz ve tanımladığımız list'e ekliyoruz (points).

    Ardından poligonun rengini ve kenar kalınlığının set edileceği Pen nesnesini (p) tanımlıyor ve Graphics nesnesinin (g) DrawPolygon metod'u ile poligonumuzu da oluşturuyoruz.

    Güvenlik resminiz için Graphics nesnesinin diğer Draw... ile başlayan metodlarını kullanarak elips, dikdörtgen gibi başka şekiller de oluşturabilirsiniz.

    Artık sıra güvenlik resmimizin yazısını oluşturmaya geldi. Yazıyı oluştururken dikkat edilmesi gereken bazı noktalar bulunmakta:
    * Oluşturduğumuz yazının içindeki hiçbir harf resmin sınırları dışına taşmamalı, resmin sınırları dışında bir yerde oluşturulmamalı,
    * Herbir harfin lokasyonu diğer harflerinkinden farklı olmalı yani hiçbir harf bir diğeriyle üst üste gelecek şekilde resmin üstüne yazdırılmamalı

    Bu noktaları da gözönünde bulundurursak bize her bir harfin resim üstündeki pozisyonunu ve harfin kendisini taşıyan bir nesne oluşturmak ve bu nesnenin property'leri üzerinden işlemlerimizi gerçekleştirmek faydalı olur. Bu yüzden Nokta adında bir class oluşturuyorum ve bu class'ın taşıyacağı Nokta tipinden değişkenin x property'sini resmimizdeki her harfin bulunduğu noktanın x değerine atamak için oluşturuyorum. Resmimizin içindeki harflerin üstüste gelmelerini engellemek için yani x değeri üstünden ne kadar sonrasına başka bir harfi yerleştirebileceğimizi bilebilmemiz için her harfin bulunduğu noktanın x değerinin yanısıra harfin genişliğini de bilmemiz gerekir. Bunun için Nokta class'ına integer tipinden Width property'sini de eklemek uygun olacaktır. Son olarak güvenlik resminin yazısına ulaşmak için resimlerdeki her harfin kendisini taşıyan bir Text property'si oluşturuyoruz ve class'ımızın x ve width parametrelerini alan constructor'ını oluşturuyoruz:

        public class Nokta

        {
            public
Nokta(int x, int width)
            {
                _x = x;
                _width = width;
            }
            private
int _x;
            public
int X
            {
                get
{ return _x; }
                set
{ _x = value; }
            }
            private
int _width;
            public
int Width
            {
                get
{ return _width; }
                set
{ _width = value; }
            }
            private
string _Text = "";
            public
string Text
            {
                get
{ return _Text; }
                set
{ _Text = value; }
            }
        }

    Güvenlik resmimize eklediğimiz harflerin üst üste gelip gelmediklerini kontrol etmek için boolean tipinden sonuç döndüren bir fonksiyon yazacağız. Bu fonksiyon parametre olarak Nokta tipinden bir değişken ve Nokta tipinden değişkenlerden oluşan bir List alacak. Nokta tipinden değişken, resmimize yeni eklenecek olan harfin x pozisyonunu, genişliğini ve harfin kendisini taşıyacak ve List de önceden resmimize eklenmiş olan harflerin x pozisyonlarını, genişliklerini ve harflerini taşıyan diğer Nokta tipinden değişkenleri taşıyacak. Bir foreach döngüsü içinde yeni eklenecek olan harf, listedeki diğer harflerle üst üste geliyor mu diye kontrol edilecek.

    Bir harfin bir diğer harfle üst üste gelmesi ya da yerlerinin çakışıp çakışmadığını 4 koşulda kontrol edeceğiz. Bunlar:

1- Bir noktanın x değeri diğerinin x değerinden küçükse ve x değeri küçük olan noktanın x değeri ile genişliğinin toplamı, diğerinin x değerinden büyükse (1),(Eklenecek olan harfin x pozisyonu listedeki harflerin birinin x pozisyonundan küçükse ve eklenecek olan harfin x değeri ile genişliğinin toplamı, listedeki harflerin birinin x noktası değerinden büyükse (1),)
2- Bir noktanın x değeri ve genişliği toplamı diğerinin x değerinden büyükse ve x değeri büyük olan noktanın x değeri ve genişliği toplamı diğer noktanın x değeri ve genişliği toplamından büyükse (2),
3- Bir noktanın x değeri diğerinden büyükse ve x değeri büyük olan noktanın x değeri ve genişliği toplamı diğer noktanın x değeri ve genişliği toplamından küçükse (3),
4- Bir noktanın x değeri diğerinden küçükse ve x değeri küçük olan noktanın x değeri ve genişliği toplamı diğer noktanın x değeri ve genişliği toplamından büyükse (4)
bu noktalar çakışıyor demektir ve harflerimizi bu noktalar dışında oluşturmamız gerekiyor demektir. Yazacağımız fonksiyon yukarıdaki 4 koşuldan biri sağlandığında true, hiçbiri sağlanmadığında false değer döndürecektir:

   
   

private bool pointCheck(Nokta n, List<Nokta> nList)
        {
            foreach
(Nokta m in nList)
            {
                if
(m.X <= n.X && m.Width + m.X >= n.X) //1
               
{
                    return
true;
                }
                else
if (n.X + n.Width >= m.X && m.X + m.Width >= n.X + n.Width) //2
               
{
                    return
true;
                }
                else
if (n.X >= m.X && n.X + n.Width <= m.X + m.Width) //3
               
{
                    return
true;
                }
                else
if (n.X <= m.X && n.X + n.Width >= m.X + m.Width) //4
               
{
                    return
true;
                }
            }
            return false;
        }

    Şimdi de güvenlik resminin yazısını oluşturacak kodu inceleyelim:

   

private string YaziyiOlustur(Image img, Graphics g, Random rnd)
        {
            string
[] alfabe = new string[] { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "r", "s", "t", "u", "v", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
            SizeF
sf = new SizeF();
            List
<Nokta> plist = new List<Nokta>();
            string
s = "";

            for (int i = 0; i < 6; i++)
            {
                Font
f = new Font("Verdana", rnd.Next(18, 24), FontStyle.Bold);
                s = alfabe[rnd.Next(1, 33)];
                sf = g.MeasureString(s, f);
                PointF
pp = new PointF();
                Nokta
n = null;
                bool
Available = false;
                while
(!Available)
                {
                    pp = new PointF(rnd.Next(0, img.Width - Convert.ToInt32(sf.Width)), rnd.Next(0, img.Height - Convert.ToInt32(sf.Height)));
                    n = new Nokta(Convert.ToInt32(pp.X), Convert.ToInt32(sf.Width));
                    n.Text = s;
                    if
(!pointCheck(n, plist))
                    {
                        Available = true;
                    }
                }

                Color colorFore = Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
                Color
colorBack = Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
                System.Drawing.Drawing2D.HatchBrush hb = new System.Drawing.Drawing2D.HatchBrush(System.Drawing.Drawing2D.HatchStyle.NarrowVertical, colorFore, colorBack);
                g.DrawString(s, f, hb, pp);

                plist.Add(n);
            }

            plist.Sort(delegate(Nokta p1, Nokta p2) { return p1.X.CompareTo(p2.X); });
           
            string
result = "";

            foreach (Nokta n in plist)
            {
                result += n.Text;
            }

            return result;
        }

    Güvenlik resminin yazısını oluşturmak için yukarıdaki YaziyiOlustur fonksiyonunu kullanacağız. YaziyiOlustur fonksiyonunda ilk olarak alfabe adında harfler ve rakamlardan oluşan bir string array tanımlıyoruz.

   Ardından resimdeki harflerin yükseklik ve genişliklerini ölçebilmemiz için SizeF tipinden bir değişken tanımlıyoruz (sf). Ardından da resimdeki tüm harflerin Nokta tipinden özelliklerinin ekleneceği List'i (plist) ve noktaların Text property'lerine atayacağımız string değişkeni tanımlıyoruz (s).

    Güvenlik resminde 6 harf olacak ve bu yüzden bir for döngüsünün içinde 6 kere dolaşarak yukarıda da belirttiğimiz kriterlere uyacak yazıyı oluşturacağız.

    İlk olarak oluşturulacak olan harfin fontunu belirliyoruz (f). Yazıdaki harflerin herbiri boyutları random olarak 18-24 pt arası berlirlenmiş, bold Verdana fontta olacaklar.

    Hangi harfin yazıda olacağını da s değişkenine alfabe array'indeki random seçilen bir index'teki harfi atayarak belirliyoruz.

    Sonraki aşamada fonksiyona parametre geçirdiğimiz Graphics nesnesinin (g) MeasureString metodunu kullanarak f fontunda oluşturulmuş olan s değişkeninin SizeF tipinden değerini alıyoruz ve bu değeri sf'ye eşitliyoruz.

    Ardından Available adında boolean bir değişken tanımlıyoruz. Bu değişkeni tanımlamaktaki amacımız bir while döngüsü içinde oluşturduğumuz harflerin pozisyonlarını (Nokta tipinden değerlerinin) daha önceden eklenmiş olan harflerin pozisyonlarıyla çakışma olmayana kadar biraz önce yazdığımız PointCheck fonksiyonuyla kontrol etmek ve çakışma yoksa Nokta listesine eklemek.

    While döngüsünü daha detaylı incelediğimizde yaptığımız ilk işin yeni oluşturacağımız harfin resimdeki pozisyonunu belirlemek için döngünün dışında tanımladığımız pp nesnesinin bir instance'ını almak olduğu görülüyor. Harfin diğer harflerle çakışmaması için PointF'in x pozisyonuna 0 ile resmin genişliğinin harfin genişliğinden çıkarılması sonucu elde edilen sayısı arasında random bir değer atıyoruz. y pozisyonuna ise 0 ile resmin yüksekliğinden harfin yüksekliğinin çıkarıldığı zaman elde edilen sayı arasında random olarak belirlenen bir sayıyı atıyoruz.

    Döngünün dışında tanımladığımız Nokta türünden n nesnesinin de x değerine pp'nin x değerini, genişliğine sf'nin genişliğini atıyoruz, text'ine de s'yi atıyoruz.

    Bir sonraki adımda pointCheck fonksiyonunda oluşturduğumuz nesnenin listede olup olmadığını kontrol ediyoruz. Eğer pointCheck fonksiyonu false değer döndürürse bu n nesnesinin listeye eklenebileceğini belirtir ve Available değişkeni true olur.     Available true olduğu için artık while döngüsüne girmez ve g nesnesinin DrawString metodunu kullanarak harfimizi resme yazdırabiliriz. DrawString metodunun sırasıyla string, font, brush, pointF tipinden nesnelerini alan overload'ını kullanacağız. Elimizde tanımlamış olduğumuz string (s), font (f) ve pointF (pp) nesneleri bulunmakta olduğundan sadece yeni bir brush nesnesi oluşturuyoruz (hb). DrawString ile harfi yazdırdıktan sonra listemize (plist) ekliyoruz.

    Son olarak sıra resmimizin yazısını okumaya geldi. Listedeki Nokta nesnelerini x değerlerine göre sıralarsak soldan sağa doğru yazılmış şekilde yazımızı elde edebiliriz. List'in sort metoduyla bu sıralamayı yaptıktan sonra listedeki her Nokta nesnesinin Text property'sini tanımlamış olduğumuz result değişkenine atıyoruz ve resmimizdeki yazıyı da elde etmiş oluyoruz.

    Burada belirtmem gereken bir nokta bulunuyor. Resmimizi oluştururken altı karakterden fazla kullanmak istersek veya fontun boyutunu artırırsak harflerin tamamının resme sığmama olasılığı yükselir ve metodumuz while döngüsü içinde sonsuz döngüye girebilir. Bu yüzden daha fazla karakter kullanmak veya font boyutunu artırmak için resmin boyutunun da artırılması gerekir.

    Şimdi
Windows uygulamamızdaki forma picturebox, label ve buton ekleyelim. Butona bastığımızda resim oluşturulacak ve yazı label'da gösterilecek. Butonun click eventine aşağıdaki kodu yazıyoruz:

private void btnResmiOlustur_Click(object sender, EventArgs e)
        {
            Random
rnd = new Random();
            Image
img = Image.FromFile(@"Arkaplanlar\arkaplan" + rnd.Next(1, 5) + ".jpg");
            pictureBox1.Image = img;
            CizgilerVePoligonuOlustur(img, rnd);
            Graphics g = Graphics.FromImage(img);
            lblYazi.Text = YaziyiOlustur(img, g, rnd);
        }



    Görüldüğü üzere kodda random bir değişken (rnd) tanımlıyoruz ve projemizin debug klasöründe yeralan Arkaplanlar klasörü içindeki resimlerden bir tanesini güvenlik resimimize arkaplan olarak atıyoruz. Sonrasında CizgilerVePoligonuOlustur metodunu çağırıyoruz ve resmimizdeki 5 çizgi ve poligonun çizilmesini sağlıyoruz. Ardından da Graphics nesnesini oluşturup (g) biraz önce yazdığımız YaziyiOlustur fonksiyonunu çağırıyoruz ve fonksiyonun döndürdüğü string değeri yazı label'ının Text property'sine atıyoruz.

    Uygulamamızı çalıştırıp Resmi Oluştur butonuna tıkladığımızda güvenlik resmimiz oluşacaktır:



     Şimdi örnek bir ASP uygulamasında resmimizi oluşturalım. Bunun için yeni bir class project oluşturdum ve GuvenlikResmi adında bir class'a kodu taşıdım. Bu class Image(resim için) ve string(resim yazısı için) tipinde iki adet readonly property taşıyor. Resmin constructor'ında arkaplan resimlerinin bulunduğu klasörün yolunu parametre geçirerek resmi oluşturuyoruz. Resim oluşturulduktan sonra Yazı property'sini Session'da tutacağız ve textbox'a girilen değer Session'da tutulan değerle aynıysa kabul edeceğiz.

    Projemize ilk olarak resmin oluşturulacağı Resim.aspx sayfasını ekliyorum ve Load'ında aşağıdaki kodu yazıyorum:

   

protected void Page_Load(object sender, EventArgs e)
    {
        GuvenlikResmim.GuvenlikResmi gr = new GuvenlikResmim.GuvenlikResmi(@"C:\Arkaplanlar\");
        Session["ResimYazisi"] = gr.Yazi;
        Response.ContentType = "image/jpeg";
        gr.Resim.Save(Response.OutputStream, ImageFormat.Jpeg);
    }



Şimdi sıra güvenlik resminini formumuzda kullanmaya geldi. Başka bir sayfada image, textbox ve buton'dan oluşan bir form oluşturalım. Image kontrolünün ImageUrl property'sine Resim.aspx'i atayalım ve buton'un click event'ine aşağıdaki kodu yazalım:

    protected void btnGonder_Click(object sender, EventArgs e)
    {
        if
(txtResimYazisi.Text != Session["ResimYazisi"].ToString())
        {
            Response.Write("Hata!");
            txtResimYazisi.Text = "";
        }
       
else
       
{
            Response.Write("Tamam");
            imgGuvenlikResmi.Visible = false;
            txtResimYazisi.Visible = false;
            btnGonder.Visible = false;
        }
    }





    Kodda da görüldüğü gibi textbox'da yazan değer Session'da tuttuğumuz değerle aynıysa kabul edilecek, farklıysa Hata yazısı çıkacak ve başka bir resim oluşturulacak:



    Bu makalemde güvenlik resmi oluşturulması ve kullanımı hakkında bilgi vermeye çalıştım. Projenin kodunu indirip inceleyebilirsiniz.

    Faydalı olmasını umarım.

    Işıl Orhanel.
Örnek Kodlar