Arkadaşlar, bu aralar hepimizin başına dert olan, hatta bazen uykularımızı kaçıran bir konu var: Güvenlik açıklarımız. Özellikle de web uygulamalarımızdaki o meşhur ‘SQL Injection’ denen illet. Hatırlıyorum da, yıllar önce ilk projelerimde bunu ne kadar hafife alırdım. ‘Bana ne ya, benim veritabanım ne kadar önemli ki?’ diye düşünürdüm. Ne kadar da yanılmışım… Gerçekten de, en küçük bir açık bile tüm sisteminizi altüst edebiliyor. İşte tam da bu yüzden, bugün sizlere SQL Injection açığını kapatma hikayemi, yani biraz da kendi hatalarımdan ders çıkarma serüvenimi anlatacağım. Belki siz de benim düştüğüm hatalara düşmezsiniz, kim bilir? 🙂
Şimdi, olayın özüne inmeden önce, SQL Injection nedir diye kısaca bir değinelim. Basitçe anlatmak gerekirse, kötü niyetli kişilerin, bir web sitesinin veya uygulamanın kullanıcı giriş alanlarına özel olarak hazırlanmış SQL komutları göndererek, veritabanına sızmasını sağlayan bir saldırı türü. Hani bir kapıdan anahtarla girersin ya, bu onun biraz daha ‘kaba kuvvet’ ve ‘hileli’ hali gibi düşünün. Direkt veritabanına erişip istediği bilgiyi çekebiliyor, hatta daha kötüsü, verileri silebiliyor veya değiştirebiliyor. Ne güzel değil mi? Hele ki o hassas müşteri bilgileriniz falan varsa, vay halinize.
Bu tür saldırılarla ilk ciddi olarak uğraşmam, sanırım bundan 5-6 yıl kadar önceydi. O zamanlar üzerinde çalıştığım bir projede, kullanıcıların bazı bilgilerini bir form aracılığıyla alıp veritabanına kaydediyorduk. Her şey gayet güzel çalışıyordu, testler falan da yapılmıştı. Ama işte, o ‘güvenlik’ kısmı hep biraz arkada kalıyordu. Ne diyeyim, kendi programım sınıfta kaldı o günlerde.
Sonra bir gün, bir baktık ki, sistemde garip bir hareketlilik var. Kullanıcılarımızın bazı bilgileri sanki başkası tarafından girilmiş gibiydi. İlk başta ‘Aman, bir bug falan vardır’ dedik. Ama sonra detaylı inceleyince, durumun ciddiyetini anladık. Meğerse, bizim o basit görünen giriş alanlarımızdan biri, aslında tam bir ‘açık kapı’ymış. Birisi, kullanıcı adı alanına normal bir isim yerine, garip bir SQL komutu yazmış. İşte o komut sayesinde, veritabanımızdaki tüm kullanıcıların isimleri, adresleri, hatta şifreleri bile (o zamanlar şifreleri de plaintext olarak saklıyorduk, düşünebiliyor musunuz?) sorgulanabilmiş.
İşte o an anladım ki, bu iş öyle ‘güzel çalışıyor’ demekle bitmiyor. Geliştirdiğimiz her ne olursa olsun, bir de güvenlik gözüyle bakmak lazım. Bu arada, o saldırgan kimdi, nasıl buldu falan hiç bilemedik açıkçası. Belki de bizim gibi acemi birini bulmuştur, kim bilir.
SQL Injection’dan Korunma Yolları: Pratik Yaklaşımlar
Neyse efendim, o olaydan sonra SQL Injection’ı nasıl engelleyebiliriz diye derinlemesine bir araştırma yaptım. İlk aklıma gelen şey, tabii ki parametreli sorgular kullanmaktı. Yani, SQL komutlarımızı doğrudan string olarak birleştirmek yerine, veritabanı sürücüsünün sunduğu güvenli yolları kullanmak. Bu, gelen veriyi sanki ‘veri’ olarak alıp, SQL komutunun bir parçası olarak değil de, bir değer olarak işlemesini sağlıyor. Mesela, siz kullanıcı adını ‘Ali’ olarak girerseniz, sistem bunu ‘Ali’ olarak görür, ‘Ali’yi getir’ diye bir SQL komutu çalıştırmaya kalkmaz. Bu, aslında en temel ve en etkili yöntemlerden biri. Hani derler ya, ‘en basit çözüm genelde en iyisidir’ diye, işte tam da öyle bir şey.
Bu arada, benim C# ile geliştirdiğim API’larda kullandığım Dapper ORM, bu konuda bize bayağı yardımcı oluyor. Dapper, kullandığınız veritabanına göre (PostgreSQL, MySQL, SQL Server vs.) parametreli sorguları sizin yerinize hazırlıyor. Siz sadece veriyi veriyorsunuz, gerisini Dapper hallediyor. Mesela şöyle bir şey düşünün:
// YANLIŞ YAKLAŞIM (SQL Injection'a Açık!) var username = Console.ReadLine(); var query = $"SELECT * FROM Users WHERE Username = '{username}'"; // String birleştirme, tehlikeli! // Bu sorguya ' OR '1'='1 gibi bir şey girilirse, tüm kullanıcılar döner!// DOĞRU YAKLAŞIM (Dapper ile Parametreli Sorgu) var username = Console.ReadLine(); var query = "SELECT * FROM Users WHERE Username = @Username"; var user = connection.QueryFirstOrDefault(query, new { Username = username }); // Dapper bu @Username'ı güvenli bir şekilde işler.
Gördüğünüz gibi, ilk örnekte kullanıcıdan aldığımız ‘username’ değişkenini direkt SQL sorgusuna ekliyoruz. Bu, tam bir felaket senaryosu! Ama ikinci örnekte, Dapper’ın sunduğu ‘@Username’ gibi parametreleri kullanarak, bu değişkeni güvenli bir şekilde veritabanına iletiyoruz. Dapper, bu parametreyi veritabanına gönderirken gerekli tüm güvenlik önlemlerini alıyor. Yani, siz ‘OR 1=1’ falan yazsanız bile, veritabanı bunu bir kullanıcı adı olarak algılıyor, bir komut olarak değil. Bu, gerçekten hayat kurtarıcı bir özellik.
Bir diğer önemli nokta da, girdiğimiz tüm verileri doğrulamak (validation). Kullanıcıdan gelen her türlü bilgiyi, beklediğimiz formatta mı, olması gereken uzunlukta mı, geçersiz karakterler içeriyor mu gibi kontrollerden geçirmeliyiz. Hani bir yere bir şey yazarken ‘sadece harf giriniz’ gibi uyarılar görürsünüz ya, işte o uyarılar aslında birer güvenlik bariyeri. Eğer bir girdi beklediğinizden farklıysa, o zaman onu işleme almadan reddetmelisiniz. Bu tabii ki SQL Injection’ı tamamen engellemese de, saldırı yüzeyini daraltmaya yardımcı oluyor.
Bu arada, geçtiğimiz günlerde denk geldiğim bir YouTube videosunda, bu konunun farklı yönlerine de değinilmişti. Eğer merak ederseniz, SQL Injection hakkında birçok kaynak bulabilirsiniz. Ben de o videolardan ve okuduğum makalelerden epey şey öğrendim sanırım.
Sonuç olarak, SQL Injection gibi güvenlik açıklarını kapatmak, yazılım geliştirmenin ayrılmaz bir parçası. Bu işleri biraz ‘sonradan ekleme’ olarak görmek yerine, en başından itibaren güvenlik prensiplerini benimsemek en doğrusu. Hani derler ya, ‘önlem almak, tedavi olmaktan iyidir’ diye, bu güvenlik konuları için de geçerli. Unutmayın, küçük bir ihmal, büyük sorunlara yol açabilir.
Neyse efendim, bu konuyu daha fazla uzatmadan toparlayalım. Unutmayın, kod yazarken her zaman bir adım ötesini düşünmek, potansiyel riskleri öngörmek bizim işimiz. Ve tabi ki, hepimiz zaman zaman hatalar yapabiliriz. Önemli olan bu hatalardan ders çıkarmak ve daha güvenli sistemler inşa etmek.
Bu arada, aklıma geldi, geçenlerde bir kamp gezisi planlıyorduk eşimle. Bursa’nın köylerinden birine gidecektik. Hava mis gibiydi, tam karar verdik derken, bir baktım ki benim laptopumda bir arıza var. Ekranı gidip geliyor, ne yapsam düzelmedi. Dedim “Tamam, bu kamp iptal.” Laptop tamiriyle uğraşırken, bir yandan da ‘Acaba bu bilgisayarın anakartında bir sorun mu var?’ diye düşünüyordum. Meğerse kablosunda bir temassızlık varmış, basit bir dokunuşla düzeldi. Yani, bazen en karmaşık görünen sorunların çözümü en basit şey olabiliyor. Tıpkı SQL Injection’ı parametreli sorgularla kapatmak gibi. 🙂