3okunma
Makale14 dk okuma

Sql injection Geniş kapsamlı anlatım

· Slaweally· 14 dk okuma
Sql injection Geniş kapsamlı anlatım

Kapsamlı SQL Injection Kılavuzu: Anatomi, Türler, Zafiyet Analizi ve Korunma Yöntemleri

Web uygulamalarının ve veri tabanı sistemlerinin gelişimiyle birlikte veri güvenliği, dijital dünyanın en kritik unsurlarından biri haline gelmiştir. Bilgilerin statik HTML sayfalarından dinamik dillere (PHP, ASP.NET, Java, Python vb.) ve ilişkisel veri tabanlarına (MySQL, MS SQL Server, Oracle, PostgreSQL vb.) taşınması, performansı ve ölçeklenebilirliği artırırken beraberinde ciddi bir güvenlik riskini de getirmiştir: Uygulama Güvenliği Eksikliği.

Bu kılavuzda, web uygulamalarını tehdit eden en yaygın ve en tehlikeli zafiyetlerden biri olan SQL Injection (SQLi) konusunu tüm detayları, kod örnekleri, mantıksal analizleri ve korunma yöntemleri ile ele alacağız.

1. SQL Injection Nedir?

SQL Injection (SQL Enjeksiyonu), bir web uygulamasının kullanıcıdan aldığı girdileri (input) yeterince doğrulamadan veya filtrelemeden doğrudan SQL sorgularının içerisine dahil etmesi sonucu ortaya çıkan bir güvenlik zafiyetidir.

Kelime anlamından da anlaşılacağı üzere, saldırganın uygulama arayüzünü kullanarak veri tabanı motoruna dışarıdan zararlı SQL komutları "enjekte etmesi" durumudur. Programcının iyi niyetle yazdığı masum bir sorgu, saldırganın müdahalesiyle tamamen farklı bir amaca hizmet edebilir. Bu durum veri sızıntılarına, veri tabanının silinmesine, yetkisiz erişimlere ve hatta hedef sunucunun tamamen ele geçirilmesine yol açabilir.

2. Teknik Temel: Dinamik SQL ve Bind Variable İlişkisi

SQL Injection zafiyetinin temel varoluş sebebi, uygulama katmanında Dinamik SQL (Dynamic SQL) kullanılmasıdır. Yani SQL sorgusunun, dışarıdan gelecek parametrelere göre çalışma zamanında (runtime) bir string birleştirme (concatenation) işlemiyle oluşturulmasıdır.

Önemli Not: "Bind Variable" (Parametrik Sorgu) kullanmanın tek avantajı performans artışı değildir. Veri tabanı güvenliğinin (Database Security) sağlanmasındaki en güçlü kalkan bind variable kullanımıdır.

Zafiyetin çalışma mantığını anlamak için somut bir senaryo oluşturalım. Elimizde müşterilerin bilgilerini tutan customers adında bir tablo olsun.

Veri Tabanı Yapısının Oluşturulması (Oracle SQL)

SQL
CREATETABLE customers ( customer_forname VARCHAR2(30), customer_surname VARCHAR2(30), customer_phone VARCHAR2(30), customer_fax VARCHAR2(30), customer_type NUMBER(10)
);

Örnek Verilerin Eklenmesi

SQL
BEGININSERTINTO customers VALUES ('Ali', 'Yilmaz', '999444888', '999444889', 3); INSERTINTO customers VALUES ('Ahmet', 'Celik', '999555888', '999555889', 2); INSERTINTO customers VALUES ('Veli', 'Gocer', '999777888', '999777889', 1); COMMIT;
END;
/

3. PL/SQL Üzerinde SQL Injection Senaryosu

Müşterinin soyadını parametre olarak alıp, ona ait telefon numarasını ekrana yazdıran hatalı (zafiyet barındıran) bir PL/SQL prosedürünü inceleyelim:

SQL
CREATEOR REPLACE PROCEDURE getCustPhone(p_surname IN VARCHAR2) IS TYPE refCursor ISREFCURSOR; rc refCursor; thePhone customers.customer_phone%TYPE; -- SQL stringi dinamik olarak birleştiriliyor (Zafiyet Noktası!) stmt VARCHAR2(32767) :='SELECT customer_phone FROM customers WHERE customer_surname = '''|| p_surname ||'''';
BEGIN DBMS_OUTPUT.PUT_LINE('Çalıştırılan SQL Metni: '|| stmt); OPEN rc FOR stmt; LOOP FETCH rc INTO thePhone; EXIT WHEN rc%NOTFOUND; -- Orijinal koddaki imleç hatası rc%notfound olarak düzeltilmiştir. DBMS_OUTPUT.PUT_LINE('Müşteri Telefonu: '|| thePhone); END LOOP; CLOSE rc;
END getCustPhone;
/

Normal ve Meşru Kullanım

Prosedüre beklenen kurallara uygun bir soyadı (Yilmaz) gönderildiğinde sistem hatasız çalışır:

SQL
BEGIN getCustPhone('Yilmaz');
END;
/

Çıktı:

Plaintext
Çalıştırılan SQL Metni: SELECT customer_phone FROM customers WHERE customer_surname = 'Yilmaz'
Müşteri Telefonu: 999444888

İstismar Senaryosu 1: Tüm Verileri İfşa Etme

Bir saldırgan, tek bir müşterinin telefonu yerine tüm müşterilerin numaralarını çekmek isterse, parametreyi SQL mantığını bozacak şekilde manipüle edebilir.

Gönderilen zararlı parametre: Yilmaz' OR EXISTS (SELECT 1 FROM sys.dual) AND 'x' = 'x

SQL
DECLARE cust VARCHAR2(200) :='Yilmaz'' OR EXISTS (SELECT 1 FROM sys.dual) AND ''x'' = ''x';
BEGIN getCustPhone(cust);
END;
/

Çıktı:

Plaintext
Çalıştırılan SQL Metni: SELECT customer_phone FROM customers WHERE customer_surname = 'Yilmaz' OR EXISTS (SELECT 1 FROM sys.dual) AND 'x' = 'x'
Müşteri Telefonu: 999444888
Müşteri Telefonu: 999555888
Müşteri Telefonu: 999777888

Analiz: Saldırgan, girdi alanına tek tırnak (') koyarak programcının açtığı string ifadesini erken kapattı. Ardından OR mantıksal operatörünü ekleyerek her zaman doğru (TRUE) dönecek bir koşul enjekte etti. En sondaki 'x'='x' ifadesi ise sorgunun sağ tarafında açıkta kalan orijinal tek tırnağı kapatmak ve sentaks hatası (Syntax Error) almamak için eklenmiştir.

İstismar Senaryosu 2: UNION Tabanlı Bilgi Toplama

Saldırgan, veri tabanına bağlantı sağlayan mevcut aktif kullanıcıyı öğrenmek için UNION operatörünü kullanabilir.

Gönderilen zararlı parametre: Yilmaz' UNION SELECT sys.login_user FROM sys.dual WHERE 'x'='x

SQL
DECLARE cust VARCHAR2(200) :='Yilmaz'' UNION SELECT sys.login_user FROM sys.dual WHERE ''x''=''x';
BEGIN getCustPhone(cust);
END;
/

Çıktı:

Plaintext
Çalıştırılan SQL Metni: SELECT customer_phone FROM customers WHERE customer_surname = 'Yilmaz' UNION SELECT sys.login_user FROM sys.dual WHERE 'x'='x'
Müşteri Telefonu: 999444888
Müşteri Telefonu: CUSTINFOUSER

Bu yöntemle saldırgan veri tabanı şemasını, tablo isimlerini, diğer veri tabanlarına giden DB Link (Database Link) bilgilerini ve hassas sistem verilerini tamamen dışarı sızdırabilir.

4. Web Uygulamalarında SQL Injection Anatomisi

Web uygulamaları kullanıcılar ile sürekli etkileşim halindedir. Girdiler form alanlarından, URL sorgu dizilerinden (QueryString), çerezlerden (Cookies) veya HTTP Header bilgilerinden gelebilir.

Zafiyetin web üzerindeki etkileri, veri tabanı yönetim sisteminin (DBMS) yeteneklerine ve uygulamanın çalıştığı işletim sistemi izinlerine bağlı olarak şu felaketlere yol açabilir:

  • Kimlik doğrulama mekanizmalarını şifresiz geçmek (Auth Bypass).

  • Veri tabanındaki tüm tabloları okumak, değiştirmek veya silmek (DROP, DELETE, UPDATE).

  • Veri tabanı sunucusu üzerinde işletim sistemi komutları çalıştırmak (Örn: MS SQL Server'da xp_cmdshell).

  • Yerel ağdaki diğer sunuculara sızmak için bir sıçrama tahtası olarak kullanılması.

Ne Kadar Sıklıkta Görülür?

  • Dinamik web siteleri ve API'ler için tarihten bugüne en sık rastlanan kritik güvenlik açıklarının başında gelir (OWASP Top 10 listesinin her dönem üst sıralarındadır).

  • Veri tabanı veya web sunucusunun bir hatası değil, tamamen yazılımcının kodlama hatasıdır.

  • Pek çok geliştirici, modern framework'lerin arkasındaki ham SQL yapılarını kontrol etmediği için bu risk halen güncelliğini korumaktadır.

Tehdit Altındaki Hedef Sistemler

Herhangi bir dil veya veri tabanı ayrımı yoktur. SQL standartlarını kullanan tüm yapılar bu tehditle karşı karşıyadır:

  • Veri Tabanları: MS SQL Server, Oracle, MySQL, PostgreSQL, MariaDB, MS Access, DB2, Sybase, Informix vb.

  • Diller ve Teknolojiler: PHP, ASP, ASP.NET, JSP/Java, Python, Node.js, Perl, CGI, XML/XSL, Javascript (Node/NoSQL enjeksiyonları dahil), ODBC/ADO.NET tabanlı API'ler.

5. Kimlik Doğrulama Atlama (Authentication Bypass)

En sık rastlanan SQL Injection türlerinden biri, kullanıcı giriş formlarını herhangi bir şifre bilmeden aşmaktır.

Zafiyetli Arka Plan Kod Mantığı (Örnek: Classic ASP / JScript)

Giriş formundan gelen kullanıcı adı (formusr) ve şifre (formpwd) değişkenlerinin SQL içerisine doğrudan eklendiğini varsayalım:

JavaScript
var query = "SELECT * FROM users WHERE login = '" + formusr + "' AND password = '" + formpwd + "'";

Saldırının Gerçekleştirilmesi

Saldırgan kullanıcı adı alanına aşağıdaki ifadeyi girer:

  • Kullanıcı Adı: admin' OR 1=1 --

  • Şifre: (Rastgele herhangi bir şey)

Oluşan nihai SQL sorgusu şu şekle dönüşür:

SQL
SELECT*FROM users WHERE login ='admin'OR1=1--' AND password = 'xyz'

Analiz: Birçok veri tabanında (MS SQL, PostgreSQL, Oracle) -- karakteri o satırın geri kalanını yorum satırı (comment) haline getirir ve veri tabanı motoru tarafından işlenmesini engeller. Böylece AND password = ... kontrolü tamamen devre dışı kalır. Sorgu WHERE login = 'admin' OR 1=1 haline geldiği için, 1=1 koşulu her zaman doğru kabul edilir ve saldırgan şifre doğrulaması yapılmadan admin kullanıcısı olarak sisteme giriş yapar.

6. Nümerik Alanlarda SQL Injection Tehdidi

Geliştiricilerin düştüğü en büyük yanılgılardan biri, sadece string (metinsel) alanlarda tek tırnak kullanımıyla SQL Injection oluşabileceğini sanmalarıdır. Veri tabanında veri tipi NUMBER, INT veya BIGINT olan alanlarda tek tırnak işareti kullanılmadığı için zafiyet çok daha doğrudan tetiklenir.

Zafiyetli PHP / MySQL Yapısı

Veri tabanı şeması:

SQL
CREATETABLE IF NOTEXISTS `accounts` ( `id` BIGINT(20) UNSIGNED NOTNULL AUTO_INCREMENT, `account` BIGINT(20) NOTNULLDEFAULT'0', `pin` BIGINT(20) NOTNULLDEFAULT'0', `user_name` VARCHAR(128) NOTNULLDEFAULT'', PRIMARY KEY (`id`)
) ENGINE=MyISAM; INSERTINTO `accounts` VALUES (1, 123, 123, 'ULKER');
INSERTINTO `accounts` VALUES (2, 456, 456, 'MUSA');

Zafiyet barındıran PHP kodu (login.php):

PHP
<?php// Zafiyet analizi amaçlı temizlenmiş ve optimize edilmiş kod bloğurequire('classes.php');
session_start();
$db = new db("root", "password", "sql_demo", "localhost");
error_reporting(0); if (isset($_POST['account']) && isset($_POST['pin'])) { $account = $_POST['account']; $pin = $_POST['pin']; -- Nümerik alanlarda girdi tırnak içine alınmadan doğrudan sorguya gömülüyor $query = "SELECT * FROM accounts WHERE account = " . $account . " AND pin = " . $pin . " LIMIT 1"; if ($user = $db->get_row($query)) { echo"Hoş geldiniz, " . htmlspecialchars($user->user_name); } else { echo"Erişim engellendi !!"; }
} else {
?>
<html>
<head><title>Zafiyetli Giriş Test Sayfası</title></head>
<body> <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST"> Kullanıcı Hesap No: <input type="text" name="account" /> <br/> PIN Kodu: <input type="password" name="pin" /> <br/> <input type="submit" value="Gönder" /> </form>
</body>
</html>
<?php } ?>

Nümerik Alana Enjeksiyon Mantığı

Saldırgan bu kez tek tırnak kullanmak zorunda değildir. Gönderilen veriler:

  • Kullanıcı Hesap No: 1 OR 1=1 #

  • PIN: 1111

Oluşan SQL sorgusu:

SQL
SELECT*FROM accounts WHERE account =1OR1=1 # AND pin =1111 LIMIT 1

Analiz: MySQL tabanlı sistemlerde # (diyez) karakteri satır içi yorum anlamına gelir. Sorgu account = 1 OR 1=1 mantığına indirgenir. Koşul her halükarda doğru döneceğinden, tablodaki ilk kayıt (ULKER) veri tabanından çekilir ve oturum açılır.

7. Kritik SQL Injection Karakterleri ve Fonksiyonları

Aşağıdaki karakterler ve ifadeler, girdi denetimlerinde özel olarak incelenmeli veya parametrik yapılarla izole edilmelidir:

Karakter / İfadeKullanım Amacı ve Fonksiyonu
' veya "String (metin) sınırlarını belirler, enjeksiyonun kapısını açar.
--MS SQL, Oracle, PostgreSQL sistemlerinde satırı yorum haline getirir.
#MySQL sistemlerinde satırı yorum haline getirir.
/* ... */Çok satırlı yorum bloğu. Filtreleri aşmak amacıyla boşluk yerine kullanılabilir.
+MS SQL'de string birleştirme (concatenate) amacıyla kullanılır.
||Oracle ve PostgreSQL sistemlerinde string birleştirme amacıyla kullanılır.
%LIKE sorgularında joker (wildcard) karakter olarak kullanılır.
CHAR() / CHR()Karakter kodlarını kullanarak filtreleme mekanizmalarını (WAF) atlatmayı sağlar.

8. Gelişmiş Atlatma (Evasion) Teknikleri: ASCII Karakter Tehdidi

Birçok web geliştiricisi, gelen input içerisindeki tek tırnak (') karakterlerini temizlemenin veya çift tırnağa çevirmenin tam koruma sağlayacağını düşünür. Ancak saldırganlar, string ifadeleri doğrudan yazmak yerine veri tabanının ham ASCII fonksiyonlarını kullanarak filtreleri kolayca aşabilirler.

ASCII Kod Tablosu Karşılıkları

  • 32$\rightarrow$ Boşluk (Space)

  • 39$\rightarrow$ Tek Tırnak (')

  • 45$\rightarrow$ Tire (-)

  • 49$\rightarrow$1

  • 61$\rightarrow$ Eşittir (=)

  • 79$\rightarrow$O

  • 82$\rightarrow$R

Filtrelerin ASCII ile Aşılması (MS SQL Örneği)

Girdi alanına doğrudan metin yazmak yerine, veri tabanında birleştirilmek üzere şu fonksiyon dizisi enjekte edilebilir:

SQL
SELECTCHAR(49) +CHAR(39) +CHAR(32) +CHAR(79) +CHAR(82) +CHAR(32) +CHAR(49) +CHAR(61) +CHAR(49) +CHAR(45) +CHAR(45)

Bu ifade veri tabanı tarafından işlendiğinde dinamik olarak şu anlama gelir:

Plaintext
1' OR 1=1--

Görüldüğü üzere, uygulama katmanında kelime bazlı filtreleme (Blacklisting) yapılsa dahi, veri tabanı motoru enjekte edilen fonksiyonları yorumlayarak zararlı komutu arka planda yeniden inşa edebilir. Bu nedenle ham filtreleme yaklaşımları tek başına yetersizdir.

9. SQL Injection Bulunabilecek Kritik Noktalar

Güvenlik denetimi yaparken uygulamaya dışarıdan girdi sağlayan tüm kanallar potansiyel birer hedef olarak ele alınmalıdır:

  1. Form Alanları: Arama kutuları, kullanıcı giriş panelleri, kayıt formları, geri bildirim alanları.

  2. URL Sorgu Parametreleri (QueryString): detay.php?id=5 gibi adres satırındaki tüm parametreler.

  3. HTTP Çerezleri (Cookies): Oturum bilgileri, sepet verileri veya kullanıcı tercihleri veri tabanında sorgulanıyorsa manipüle edilebilir.

  4. HTTP Header Bilgileri: User-Agent, X-Forwarded-For veya Referer değerleri loglama amacıyla veri tabanına doğrudan yazılıyorsa SQLi zafiyetine yol açabilir.

10. Korunma Yaklaşımları ve Güvenlik Kontrol Listesi

SQL Injection zafiyetinden korunmak için geliştiricilerin uygulayabileceği mantıksal yaklaşımlar ve teknik adımlar şunlardır:

10.1. Mantıksal Yaklaşımlar

  • Sadece İyileri Kabul Etmek (Beyaz Liste - Whitelisting): Kullanıcının girebileceği karakter kümesini katı bir şekilde sınırlandırmaktır (Örn: Sadece [a-zA-Z0-9] izin vermek). Özellikle finans ve bankacılık uygulamalarında tercih edilen en güvenli yöntemdir.

  • Kötüleri Reddetmek (Kara Liste - Blacklisting): SELECT, UNION, DROP, --, OR gibi tehlikeli kelimeleri yasaklamaktır. Güvenli sayılabilir ancak saldırganların farklı encoding veya obfuskasyon (kod gizleme) teknikleriyle bu filtreleri aşması olasıdır.

  • Kötüleri Filtreleyerek Kabul Etmek (Sanitization): Gelen veri içerisindeki zararlı karakterleri temizleyerek (Örn: tek tırnakları escape ederek) işleme almaktır. Temel düzeyde bir koruma sağlar ancak karmaşık sorgularda açık kapı bırakabilir.

10.2. Teknik Korunma ve Güvenlik Kontrol Listesi (Cheat Sheet)

  1. Stored Procedure veya Parametrik Sorgular (Prepared Statements) Kullanın: Sorgu şablonu ile kullanıcıdan gelen veri tamamen ayrıştırılmalıdır. Bu durumda veri tabanı, girdiyi bir SQL komutu olarak değil, sadece ham bir veri (literal) olarak işler.

    • PHP için: PDO (PHP Data Objects) parametrik yapıları kullanılmalıdır.

    • Sorgu Örneği: SELECT * FROM users WHERE username = :user

  2. Tüm Girdileri Doğrulayın: Aksini ispatlayana kadar dış dünyadan gelen her veriyi zararlı (untrusted) kabul edin.

  3. Nümerik Alanları Sıkı Denetleyin: Gelen verinin gerçekten sayı olup olmadığını dilin yerleşik fonksiyonlarıyla denetleyin (Örn: PHP'de is_numeric(), filter_var(), C#'ta int.TryParse()).

  4. Düzenli İfadeler (Regular Expressions) Kullanın: Girdilerin belirli bir şablona (E-posta, Telefon formatı vb.) uyup uymadığını Regex ile kontrol edin.

  5. En Düşük Yetki İlkesini (Principle of Least Privilege) Uygulayın: Uygulamanın veri tabanına bağlandığı kullanıcının yetkilerini kısıtlayın.

    • MS SQL kullanırken uygulamayı asla sa (system administrator) yetkisiyle bağlamayın.

    • MySQL kullanırken uygulamayı asla root yetkisiyle çalıştırmayın.

    • Uygulama kullanıcısının DROP TABLE, ALTER TABLE veya sistem prosedürlerini çalıştırma yetkilerini elinden alın.

  6. Hata Yönetimini Güvenli Hale Getirin: Detaylı veri tabanı hata mesajlarını (Database Error Messages) son kullanıcıya göstermeyin. Saldırganlar bu hatalara bakarak veri tabanının mimarisini analiz ederler (Error-based SQLi). Hataları sunucu tarafında loglayın, kullanıcıya ise jenerik bir "Hata Oluştu" mesajı gösterin.

Sonuç

SQL Injection, uygulama katmanında yapılan küçük bir dikkatsizliğin tüm bilgi sistemlerini çökertebileceğinin en somut kanıtıdır. Güvenli yazılım geliştirme prensiplerine (Secure Coding) uyularak, dinamik SQL üretiminden vazgeçilmesi ve parametrik sorguların standart hale getirilmesiyle bu zafiyet %100 oranında engellenebilmektedir. Eski projelerinizi bu kılavuzdaki kontrol listesine göre gözden geçirmek, veri güvenliğiniz açısından hayati bir öneme sahiptir.


comments[] (7)

  1. Talha K.

    Gene döktürmüşsün hocam bayada uzun olmuş 7-8 dakikada zor bitirdim

    1. Ali Çömez

      Eyvallah moruk

  2. HastaneBul

    Sql Injection’dan korunmanın en sabit yöntemi, sorgularınızı direk değildide veritabanına parametre olarak göndermek.

    1. Ali Çömez

      Sorguları laaaap diyo gönderirsen birileri de sana laaaap diye iteler dimi hacı

  3. WebTasarimci

    PDO prepared statements kullanmayan kaldı mı ya bu devirde hala düz query yazıp sitede açık bırakan varsa hacklenmeyi hakediyordur zaten.

    1. Ali Çömez

      Hacı sen öyle diyorsun da piyasada hala kaç yıllık scriptleri kullanan eski siteler var adam farkında bile değil açık olduğunun 😄

  4. caner_34

    Eline sağlık ali abi, güvenlik makalelerin çok faydalı oluyor. Sırada XSS koruması gelse çok iyi olur.

    1. Ali Çömez

      Eyvallah Caner, xss listemde var bir ara çay sigara eşliğinde onu da patlatırım blogda pamp 😄

  5. Güvenlikçi_Mert

    Yalnız yazıda addslashes fonksiyonundan bahsetmişsin ama o tam çözüm değil, mysql_real_escape_string veya direkt PDO kullanmak şart.

    1. Ali Çömez

      Aynen hacım haklısın, addslashes eski usul kalıyor biraz ama mantığı anlaşılsın diye araya sıkıştırdım tek başına kurtarmaz zaten.

  6. Burak

    Yazıyı yer imlerine ekledim, baştan sona tam bir başvuru kaynağı olmuş. Teşekkürler ali abi.

    1. Ali Çömez

      Rica ederim Burak, işine yaradıysa ne mutlu bana.

  7. SiberSavaşçı

    Hocam dork taraması yapıp sql açığı bulan botlar var ya, bu aralar benim siteye çok dadandılar. Yazıdaki koruma adımları işe yarar mı?

    1. Ali Çömez

      İşe yarar moruk, kod tarafını sağlama alırsan botlar istedikleri kadar tarasın hata alıp dönerler anca sjsjsj 😄

Yorum Yaz

stats
site.metrics
499bugün ziyaretçi
1811bugün görüntülenme
14282toplam ziyaretçi
56429toplam görüntülenme
148içerik
734yorum