<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SQL güvenlik açığı arşivleri - Ali Karahisar</title>
	<atom:link href="https://alikarahisar.com/blog/tag/sql-guvenlik-acigi/feed/" rel="self" type="application/rss+xml" />
	<link>https://alikarahisar.com/blog/tag/sql-guvenlik-acigi/</link>
	<description>Yazılım ve AI Hakkında Karalamalar</description>
	<lastBuildDate>Thu, 29 Oct 2026 07:29:15 +0000</lastBuildDate>
	<language>tr</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.4</generator>

<image>
	<url>https://alikarahisar.com/blog/wp-content/uploads/2026/08/cropped-Code_Generated_Image-32x32.png</url>
	<title>SQL güvenlik açığı arşivleri - Ali Karahisar</title>
	<link>https://alikarahisar.com/blog/tag/sql-guvenlik-acigi/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>SQL Injection Nedir? Saldırıyı Anlamak ve Önlemek İçin Pratik Rehber</title>
		<link>https://alikarahisar.com/blog/sql-injection-nedir/</link>
					<comments>https://alikarahisar.com/blog/sql-injection-nedir/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Thu, 29 Oct 2026 07:29:14 +0000</pubDate>
				<category><![CDATA[Siber Güvenlik]]></category>
		<category><![CDATA[Yazılım]]></category>
		<category><![CDATA[Blind SQL Injection]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[OWASP SQL Injection]]></category>
		<category><![CDATA[Parametreli SQL sorguları]]></category>
		<category><![CDATA[PreparedStatement Java]]></category>
		<category><![CDATA[SQL enjeksiyonu]]></category>
		<category><![CDATA[SQL güvenlik açığı]]></category>
		<category><![CDATA[SQL Injection güvenlik testi]]></category>
		<category><![CDATA[SQL Injection nasıl çalışır]]></category>
		<category><![CDATA[SQL Injection nasıl önlenir]]></category>
		<category><![CDATA[SQL Injection nedir]]></category>
		<category><![CDATA[SQL Injection örnekleri]]></category>
		<category><![CDATA[SQL Injection saldırısı]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=113</guid>

					<description><![CDATA[<p>Bir web uygulamasının giriş ekranına kullanıcı adınızı yazdığınızı düşünün. Uygulama bu bilgiyi veritabanına gönderiyor ve hesabınızı arıyor. Buraya kadar her şey normal. Peki, kullanıcı adı alanına normal bir isim yerine SQL komutu yazarsanız ne olur? Uygulama bu girdiyi doğrudan SQL sorgusuna ekliyorsa veritabanı beklediğinizden farklı bir komut çalıştırabilir. Böylece basit bir metin alanı, veritabanı güvenliğini [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/sql-injection-nedir/">SQL Injection Nedir? Saldırıyı Anlamak ve Önlemek İçin Pratik Rehber</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Bir web uygulamasının giriş ekranına kullanıcı adınızı yazdığınızı düşünün. Uygulama bu bilgiyi veritabanına gönderiyor ve hesabınızı arıyor. Buraya kadar her şey normal.</p>



<p class="wp-block-paragraph">Peki, kullanıcı adı alanına normal bir isim yerine SQL komutu yazarsanız ne olur?</p>



<span id="more-113"></span>



<p class="wp-block-paragraph">Uygulama bu girdiyi doğrudan SQL sorgusuna ekliyorsa veritabanı beklediğinizden farklı bir komut çalıştırabilir. Böylece basit bir metin alanı, veritabanı güvenliğini tehdit eden bir noktaya dönüşür.</p>



<p class="wp-block-paragraph">Bu güvenlik açığına <strong>SQL Injection (SQL enjeksiyonu)</strong> adını veriyoruz.</p>



<p class="wp-block-paragraph">SQL Injection yalnızca eski web uygulamalarının sorunu değil. Modern Java uygulamaları, REST API&#8217;ler, mikroservisler ve ORM kullanan sistemler de hatalı sorgu oluşturduklarında aynı riskle karşılaşır.</p>



<p class="wp-block-paragraph">Bu rehberde SQL Injection&#8217;ın nasıl çalıştığını, hangi hataların güvenlik açığı oluşturduğunu ve Java ile güvenli veritabanı sorgularını nasıl geliştirebileceğinizi inceleyeceğiz.</p>



<h2 class="wp-block-heading">SQL Injection Nedir?</h2>



<p class="wp-block-paragraph">SQL Injection, saldırganın uygulamanın SQL sorgusuna kendi girdisi üzerinden müdahale etmesini sağlayan bir güvenlik açığıdır.</p>



<p class="wp-block-paragraph">Sorunun temelinde SQL komutları ile kullanıcı verilerinin birbirine karışması yatar.</p>



<p class="wp-block-paragraph">Bir uygulama kullanıcı girdisini SQL sorgusunun metnine doğrudan eklediğinde, saldırgan sorgunun mantığını değiştirebilir.</p>



<p class="wp-block-paragraph">Bu durum saldırgana uygulamanın veritabanı izinleri ve sorgunun yapısına bağlı olarak şu fırsatları verebilir:</p>



<ul class="wp-block-list">
<li>Normalde erişemeyeceği verilere ulaşmak</li>



<li>Kullanıcı hesapları ve müşteri bilgilerini okumak</li>



<li>Yetkisiz veri değişiklikleri yapmak</li>



<li>Kayıtları silmek veya bozmak</li>



<li>Kimlik doğrulama kontrollerini aşmak</li>



<li>Veritabanı hizmetinin çalışmasını olumsuz etkilemek</li>
</ul>



<p class="wp-block-paragraph">Bazı veritabanı özellikleri ve yüksek yetkili hesaplar, saldırının etkisini veritabanının dışına da taşıyabilir.</p>



<p class="wp-block-paragraph">SQL Injection, OWASP Top 10:2025 listesindeki <strong>A05:2025 – Injection</strong> kategorisinin kapsamına girer.</p>



<p class="wp-block-paragraph">Burada önemli nokta şu: SQL dili tek başına güvenlik açığı oluşturmaz. Açığı, uygulamanın güvenilmeyen verileri SQL sözdiziminin parçası hâline getirmesi oluşturur.</p>



<h2 class="wp-block-heading">SQL Injection Nasıl Çalışır?</h2>



<p class="wp-block-paragraph">Bir e-ticaret uygulamasında kullanıcıların e-posta adresleri üzerinden hesap bilgilerini aradığımızı düşünelim.</p>



<p class="wp-block-paragraph">Normal koşullarda uygulama şu sorguyu çalıştırır:</p>



<pre class="wp-block-code language-sql"><code>SELECT id, email, full_name
FROM customers
WHERE email = 'ali@example.com';</code></pre>



<p class="wp-block-paragraph">Veritabanı yalnızca ilgili e-posta adresini taşıyan kaydı döndürür.</p>



<p class="wp-block-paragraph">Şimdi Java tarafında SQL sorgusunu nasıl oluşturduğumuza bakalım.</p>



<h3 class="wp-block-heading">Güvensiz Java Kod Örneği</h3>



<pre class="wp-block-code language-java"><code>public Customer findByEmail(
        Connection connection,
        String email
) throws SQLException {

    String sql = "SELECT id, email, full_name " +
                 "FROM customers WHERE email = '" +
                 email + "'";

    try (Statement statement =
             connection.createStatement();
         ResultSet rs = statement.executeQuery(sql)) {

        if (rs.next()) {
            return new Customer(
                rs.getLong("id"),
                rs.getString("email"),
                rs.getString("full_name")
            );
        }

        return null;
    }
}</code></pre>



<p class="wp-block-paragraph">İlk bakışta kod oldukça basit. Geliştirici e-posta adresini SQL sorgusuna ekliyor ve sorguyu çalıştırıyor.</p>



<p class="wp-block-paragraph">Ancak şu satır ciddi bir güvenlik problemi yaratıyor:</p>



<pre class="wp-block-code language-sql"><code>"FROM customers WHERE email = '" + email + "'"</code></pre>



<p class="wp-block-paragraph">Uygulama, <code>email</code> değişkeninin içeriğini SQL komutunun bir parçası hâline getiriyor.</p>



<p class="wp-block-paragraph">Geliştirici burada SQL&#8217;in nerede bittiğini ve kullanıcı verisinin nerede başladığını veritabanına güvenilir şekilde anlatmıyor.</p>



<h3 class="wp-block-heading">Saldırgan Sorguyu Nasıl Değiştirir?</h3>



<p class="wp-block-paragraph">Saldırganın e-posta alanına şu ifadeyi yazdığını düşünelim:</p>



<pre class="wp-block-code language-sql"><code>' OR '1'='1'</code></pre>



<p class="wp-block-paragraph">Uygulama bu ifadeyi sorguya doğrudan eklerse ortaya şu SQL çıkar:</p>



<pre class="wp-block-code language-sql"><code>SELECT id, email, full_name
FROM customers
WHERE email = '' OR '1'='1';</code></pre>



<p class="wp-block-paragraph">Sorgunun mantığı artık değişti.</p>



<p class="wp-block-paragraph"><code>'1'='1'</code> ifadesi her zaman doğru sonuç verir. Dolayısıyla <code>WHERE</code> koşulu bütün satırlar için doğru olur.</p>



<p class="wp-block-paragraph">Veritabanı sorgusu tek bir müşteri yerine birden fazla müşteriyi döndürebilir.</p>



<p class="wp-block-paragraph">Örnekteki Java metodu yalnızca ilk kaydı okuyacak olsa da saldırgan arama sonucunun kapsamını değiştirmiş olur. Uygulamanın geri kalan mantığı bu davranışı hesap erişimi veya yetkilendirme için kullanıyorsa daha ciddi sorunlar ortaya çıkabilir.</p>



<p class="wp-block-paragraph">Bu örnek, SQL Injection&#8217;ın temel mekanizmasını gösterir: <strong>Saldırgan veri göndermek yerine sorgunun mantığını değiştirir.</strong></p>



<h2 class="wp-block-heading">SQL Injection Neden Tehlikelidir?</h2>



<p class="wp-block-paragraph">Saldırının etkisini yalnızca sorgu sonucunda kaç kayıt gördüğümüzle ölçemeyiz.</p>



<p class="wp-block-paragraph">Veritabanı kullanıcısının yetkileri, uygulamanın işlevleri ve verinin niteliği de sonuçları belirler.</p>



<h3 class="wp-block-heading">Veri Gizliliğinin Kaybı</h3>



<p class="wp-block-paragraph">Bir uygulama müşteri adreslerini, e-posta bilgilerini veya finansal kayıtları saklıyor olabilir.</p>



<p class="wp-block-paragraph">SQL Injection açığı, saldırganın bu verilere yetkisiz erişim elde etmesine yol açabilir.</p>



<p class="wp-block-paragraph">Güvenlik açısından sorun yalnızca verinin dışarı çıkması değildir. Uygulamanın normalde göstermediği bilgilerin başka bir kullanıcıya ulaşması da gizlilik ihlalidir.</p>



<h3 class="wp-block-heading">Veri Bütünlüğünün Bozulması</h3>



<p class="wp-block-paragraph">Yeterli veritabanı yetkisine sahip bir uygulama üzerinden saldırgan bazı kayıtları değiştirebilir veya silebilir.</p>



<p class="wp-block-paragraph">Örneğin ürün fiyatları, sipariş durumları ve müşteri hesap bilgileri saldırgan için hedef oluşturabilir.</p>



<p class="wp-block-paragraph">Bu tür değişiklikler yalnızca teknik hata yaratmaz. İş süreçlerini ve raporların doğruluğunu da etkiler.</p>



<h3 class="wp-block-heading">Hizmet Kesintileri</h3>



<p class="wp-block-paragraph">Bazı SQL sorguları çok fazla işlemci, bellek veya disk kaynağı tüketebilir.</p>



<p class="wp-block-paragraph">Saldırganın sorgu davranışını değiştirmesi, veritabanının performansını düşürebilir. Ağır durumlarda uygulama hizmet veremez hâle gelebilir.</p>



<h3 class="wp-block-heading">Yetkilendirme Sorunları</h3>



<p class="wp-block-paragraph">SQL Injection, kimlik doğrulama ve yetkilendirme mekanizmalarını etkileyebilir.</p>



<p class="wp-block-paragraph">Ancak her SQL Injection açığı otomatik olarak giriş ekranını aşma imkânı vermez.</p>



<p class="wp-block-paragraph">Saldırganın hangi işlemleri gerçekleştirebileceğini sorgunun konumu, uygulama mantığı ve veritabanı yetkileri belirler.</p>



<h2 class="wp-block-heading">SQL Injection Türleri</h2>



<p class="wp-block-paragraph">Güvenlik uzmanları SQL Injection saldırılarını çoğunlukla uygulamanın veritabanından nasıl yanıt aldığına göre sınıflandırır.</p>



<h3 class="wp-block-heading">1. In-Band SQL Injection</h3>



<p class="wp-block-paragraph">Bu yöntemde saldırgan aynı iletişim kanalı üzerinden hem sorguyu etkiler hem de sonucu görür.</p>



<p class="wp-block-paragraph">İki yaygın alt türü bulunur.</p>



<p class="wp-block-paragraph"><strong>Error-Based SQL Injection</strong></p>



<p class="wp-block-paragraph">Saldırgan, veritabanının ürettiği hata mesajlarından teknik bilgi elde etmeye çalışır.</p>



<p class="wp-block-paragraph">Hata mesajları bazen tablo isimleri, sütunlar veya SQL sözdizimi hakkında ipuçları verebilir.</p>



<p class="wp-block-paragraph"><strong>Union-Based SQL Injection</strong></p>



<p class="wp-block-paragraph">Saldırgan, uygun koşullarda <code>UNION</code> operatörünü kullanarak farklı sorguların sonuçlarını birleştirmeye çalışır.</p>



<p class="wp-block-paragraph">Bu yöntem belirli sorgu yapısı ve veri tipi uyumluluğu koşullarını gerektirir.</p>



<h3 class="wp-block-heading">2. Blind SQL Injection</h3>



<p class="wp-block-paragraph">Bazı uygulamalar sorgu sonucunu veya veritabanı hatalarını doğrudan göstermez.</p>



<p class="wp-block-paragraph">Bu durum SQL Injection riskini ortadan kaldırmaz.</p>



<p class="wp-block-paragraph">Saldırgan, uygulamanın davranışındaki küçük farkları gözlemleyerek bilgi elde etmeye çalışabilir.</p>



<p class="wp-block-paragraph"><strong>Boolean-Based Blind SQL Injection</strong></p>



<p class="wp-block-paragraph">Saldırgan doğru ve yanlış koşullara karşılık uygulamanın farklı tepkiler verip vermediğini inceler.</p>



<p class="wp-block-paragraph"><strong>Time-Based Blind SQL Injection</strong></p>



<p class="wp-block-paragraph">Saldırgan veritabanının yanıt süresindeki farklılıklardan çıkarım yapmaya çalışır.</p>



<p class="wp-block-paragraph">Ancak ağ gecikmeleri ve sistem yükü, zaman ölçümlerini yanıltabilir.</p>



<h3 class="wp-block-heading">3. Out-of-Band SQL Injection</h3>



<p class="wp-block-paragraph">Bazı senaryolarda saldırgan, veritabanının farklı bir iletişim kanalı üzerinden dış sistemlerle etkileşim kurmasını hedefler.</p>



<p class="wp-block-paragraph">Bu yaklaşım özel veritabanı özellikleri, yapılandırmalar ve ağ erişim koşulları gerektirir.</p>



<h3 class="wp-block-heading">4. Second-Order SQL Injection</h3>



<p class="wp-block-paragraph">Bu türü özellikle gözden kaçırmak kolaydır.</p>



<p class="wp-block-paragraph">Uygulama saldırganın girdisini önce veritabanına kaydeder. Daha sonra başka bir işlem bu veriyi okuyup güvensiz biçimde yeni bir SQL sorgusuna ekler.</p>



<p class="wp-block-paragraph">Dolayısıyla güvenlik açığı ilk veri girişinde değil, verinin sonraki kullanımında ortaya çıkar.</p>



<p class="wp-block-paragraph">Bu nedenle geliştirici yalnızca HTTP isteklerini değil, veritabanından, dosyalardan ve diğer servislerden gelen verileri de sorgu oluştururken güvenilmeyen girdi olarak ele almalıdır.</p>



<h2 class="wp-block-heading">SQL Injection Nasıl Önlenir?</h2>



<p class="wp-block-paragraph">SQL Injection&#8217;a karşı en güçlü temel savunma, SQL komutlarının yapısını kullanıcı verilerinden ayrı tutmaktır.</p>



<p class="wp-block-paragraph">OWASP, veri değerleri için parametreli sorguları temel savunma olarak önerir.</p>



<h3 class="wp-block-heading">1. PreparedStatement ve Parametreli Sorgular Kullanın</h3>



<p class="wp-block-paragraph">Java JDBC API, <code>PreparedStatement</code> sınıfıyla parametreli sorgular oluşturmanıza imkân verir.</p>



<p class="wp-block-paragraph">Önceki güvensiz örneği yeniden yazalım.</p>



<pre class="wp-block-code language-java"><code>public Customer findByEmail(
        Connection connection,
        String email
) throws SQLException {

    String sql = """
        SELECT id, email, full_name
        FROM customers
        WHERE email = ?
        """;

    try (PreparedStatement statement =
             connection.prepareStatement(sql)) {

        statement.setString(1, email);

        try (ResultSet rs =
                 statement.executeQuery()) {

            if (rs.next()) {
                return new Customer(
                    rs.getLong("id"),
                    rs.getString("email"),
                    rs.getString("full_name")
                );
            }

            return null;
        }
    }
}</code></pre>



<p class="wp-block-paragraph">Burada iki önemli değişiklik yaptık.</p>



<p class="wp-block-paragraph">İlk olarak kullanıcı girdisini SQL metnine eklemek yerine <code>?</code> yer tutucusunu kullandık.</p>



<p class="wp-block-paragraph">İkinci olarak <code>setString()</code> metoduyla parametre değerini sorguya bağladık.</p>



<p class="wp-block-paragraph">JDBC sürücüsü, parametre bağlama mekanizması üzerinden SQL yapısıyla veri değerini ayrı ele alır.</p>



<p class="wp-block-paragraph">Artık kullanıcı şu ifadeyi gönderse bile:</p>



<pre class="wp-block-code language-sql"><code>' OR '1'='1'</code></pre>



<p class="wp-block-paragraph">Veritabanı bunu sorgunun mantığını değiştiren bir SQL ifadesi olarak değil, <code>email</code> sütununda aranacak bir değer olarak değerlendirir.</p>



<p class="wp-block-paragraph"><strong>PreparedStatement neden güvenlidir?</strong></p>



<p class="wp-block-paragraph">Çünkü SQL&#8217;in komut yapısı ile dışarıdan gelen veri arasında açık bir sınır kurar.</p>



<p class="wp-block-paragraph">Tek başına kullanıcı girdisindeki özel karakterleri yasaklamamız gerekmez.</p>



<p class="wp-block-paragraph">Örneğin <code>O'Connor</code> gibi tek tırnak içeren gerçek isimler de güvenli biçimde işlenebilir.</p>



<p class="wp-block-paragraph">Ancak geliştirici SQL yapısını kullanıcı girdisiyle birleştirmeye devam ederse yalnızca sorgunun başka bir bölümünde PreparedStatement kullanması yeterli olmaz.</p>



<h3 class="wp-block-heading">2. Dinamik SQL Yapıları İçin Allowlist Kullanın</h3>



<p class="wp-block-paragraph">Parametreli sorguların önemli bir sınırı bulunur.</p>



<p class="wp-block-paragraph"><code>?</code> yer tutucusu tablo ismi, sütun ismi veya <code>ASC</code> ve <code>DESC</code> gibi SQL anahtar kelimeleri yerine geçmez.</p>



<p class="wp-block-paragraph">Bir e-ticaret uygulamasında kullanıcılar ürünleri fiyat veya isim üzerinden sıralamak isteyebilir.</p>



<p class="wp-block-paragraph">Şu yaklaşım güvenli değildir:</p>



<pre class="wp-block-code language-sql"><code>String sql =
    "SELECT id, name, price FROM products ORDER BY "
    + sortBy;</code></pre>



<p class="wp-block-paragraph">Çünkü uygulama dışarıdan gelen <code>sortBy</code> değerini doğrudan SQL yapısına ekler.</p>



<p class="wp-block-paragraph">Bunun yerine izin verdiğimiz seçenekleri kodda tanımlayalım.</p>



<pre class="wp-block-code language-java"><code>public String createProductQuery(
        String sortBy,
        String direction
) {
    String column = switch (sortBy) {
        case "price" -&gt; "price";
        case "name"  -&gt; "name";
        default -&gt; throw new
            IllegalArgumentException(
                "Unsupported sort field"
            );
    };

    String order = switch (direction) {
        case "asc"  -&gt; "ASC";
        case "desc" -&gt; "DESC";
        default -&gt; throw new
            IllegalArgumentException(
                "Unsupported direction"
            );
    };

    return """
        SELECT id, name, price
        FROM products
        ORDER BY %s %s
        """.formatted(column, order);
}</code></pre>



<p class="wp-block-paragraph">Burada kullanıcı yalnızca önceden tanımladığımız seçenekler arasından seçim yapabilir.</p>



<p class="wp-block-paragraph">Uygulama SQL sorgusuna doğrudan kullanıcı girdisini değil, kendi belirlediği güvenilir SQL ifadelerini ekler.</p>



<p class="wp-block-paragraph">Dinamik yapıları bu şekilde sınırlandırırken diğer veri değerleri için yine parametreli sorgular kullanmalıyız.</p>



<h3 class="wp-block-heading">3. Girdi Doğrulama Uygulayın</h3>



<p class="wp-block-paragraph">Bir e-posta alanı e-posta biçimini, bir ürün kimliği ise beklenen veri tipini taşımalıdır.</p>



<p class="wp-block-paragraph">Girdi doğrulama uygulamanın beklenmedik verilerle karşılaşmasını azaltır.</p>



<p class="wp-block-paragraph">Ancak doğrulama SQL Injection&#8217;a karşı tek başına yeterli değildir.</p>



<p class="wp-block-paragraph">Örneğin sadece tek tırnak karakterini engellemek güvenilir bir savunma oluşturmaz.</p>



<p class="wp-block-paragraph">Farklı SQL sözdizimleri, karakter dönüşümleri ve sorgu bağlamları bu tür filtreleri yetersiz bırakabilir.</p>



<p class="wp-block-paragraph">Doğru yaklaşım şudur:</p>



<ul class="wp-block-list">
<li>Girdi doğrulamayı iş kuralları için kullanın.</li>



<li>Parametreli sorgularla SQL yapısını koruyun.</li>



<li>Dinamik SQL parçalarını allowlist ile sınırlandırın.</li>
</ul>



<h3 class="wp-block-heading">4. Veritabanında En Az Ayrıcalık İlkesini Uygulayın</h3>



<p class="wp-block-paragraph">Bir uygulamanın yalnızca müşteri listesini okuması gerekiyorsa veritabanı hesabına tüm tabloları değiştirme yetkisi vermemelisiniz.</p>



<p class="wp-block-paragraph">Örneğin PostgreSQL üzerinde yalnızca ürün okuma görevi olan bir hesap için gerekli erişimi sınırlayabilirsiniz.</p>



<pre class="wp-block-code language-sql"><code>REVOKE ALL ON TABLE products
FROM product_reader;

GRANT SELECT ON TABLE products
TO product_reader;</code></pre>



<p class="wp-block-paragraph">Bu örnek ilgili tablo üzerindeki yetkileri sınırlar. Gerçek sistemde rol üyeliklerini, şema erişimini, varsayılan yetkileri ve diğer tabloları da kontrol etmelisiniz.</p>



<p class="wp-block-paragraph">En az ayrıcalık ilkesi SQL Injection açığını doğrudan engellemez.</p>



<p class="wp-block-paragraph">Ancak saldırgan bir açık bulsa bile veritabanı hesabının yapabileceği işlemleri sınırlar.</p>



<h3 class="wp-block-heading">5. ORM Kullanırken Dinamik Sorgulara Dikkat Edin</h3>



<p class="wp-block-paragraph">Hibernate veya JPA kullanmak SQL Injection riskini otomatik olarak ortadan kaldırmaz.</p>



<p class="wp-block-paragraph">Örneğin şu JPQL oluşturma biçimi risklidir:</p>



<pre class="wp-block-code language-sql"><code>String jpql =
    "SELECT u FROM User u WHERE u.email = '"
    + email + "'";

var query = entityManager.createQuery(
    jpql,
    User.class
);</code></pre>



<p class="wp-block-paragraph">Güvenli yaklaşımda parametre bağlamalıyız:</p>



<pre class="wp-block-code language-java"><code>var query = entityManager.createQuery(
    """
    SELECT u
    FROM User u
    WHERE u.email = :email
    """,
    User.class
);

query.setParameter("email", email);

List&lt;User&gt; users = query.getResultList();</code></pre>



<p class="wp-block-paragraph">ORM araçları geliştiricinin işini kolaylaştırır. Ancak sorgu oluşturma kararının güvenliği yine geliştiricinin sorumluluğundadır.</p>



<p class="wp-block-paragraph">Özellikle native SQL, dinamik JPQL/HQL ve elle oluşturduğunuz sıralama koşullarını dikkatle incelemelisiniz.</p>



<h3 class="wp-block-heading">6. Stored Procedure Kullanımını da Denetleyin</h3>



<p class="wp-block-paragraph">Stored procedure kullanımı SQL Injection riskini tek başına ortadan kaldırmaz.</p>



<p class="wp-block-paragraph">Bir procedure içinde kullanıcı verilerini birleştirerek dinamik SQL oluşturursanız aynı güvenlik açığını yeniden yaratabilirsiniz.</p>



<p class="wp-block-paragraph">Procedure çağrısında parametre bağlamak önemlidir. Ancak procedure içindeki dinamik sorgular için de aynı güvenlik kuralını uygulamalısınız.</p>



<p class="wp-block-paragraph">Statik SQL tercih edin. Dinamik SQL gerektiğinde veri parametrelerini bağlayın ve SQL yapılarını güvenilir seçeneklerle sınırlandırın.</p>



<h3 class="wp-block-heading">7. Hata Mesajlarını ve Kayıtları Kontrol Edin</h3>



<p class="wp-block-paragraph">Geliştirme sırasında ayrıntılı SQL hataları sorunları çözmenize yardımcı olur.</p>



<p class="wp-block-paragraph">Ancak canlı ortamda ham veritabanı hatalarını istemciye göndermek gereksiz bilgi açığa çıkarabilir.</p>



<p class="wp-block-paragraph">Uygulama, kullanıcıya genel bir hata yanıtı vermeli ve teknik ayrıntıları yetkili ekiplerin erişebileceği güvenli log sisteminde tutmalıdır.</p>



<p class="wp-block-paragraph">Log kayıtlarına parola, erişim belirteci veya hassas kişisel veri eklemeyin.</p>



<p class="wp-block-paragraph">Ayrıca veritabanı kullanıcısının izinlerini ve uygulamanın hata yönetimini düzenli olarak inceleyin.</p>



<h2 class="wp-block-heading">SQL Injection Açıklarını Nasıl Test Edebilirsiniz?</h2>



<p class="wp-block-paragraph">SQL Injection kontrollerini yalnızca sahibi olduğunuz veya açıkça test izni aldığınız sistemlerde gerçekleştirin.</p>



<p class="wp-block-paragraph">Güvenli bir geliştirme ortamında aşağıdaki yaklaşımı kullanabilirsiniz.</p>



<h3 class="wp-block-heading">Kod İncelemesi</h3>



<p class="wp-block-paragraph">Öncelikle SQL sorgularını oluşturan kodu inceleyin.</p>



<p class="wp-block-paragraph">Özellikle şu kalıplara dikkat edin:</p>



<ul class="wp-block-list">
<li>SQL metniyle kullanıcı girdisinin birleştirilmesi</li>



<li><code>Statement</code> ile dinamik sorgu çalıştırma</li>



<li>Native SQL ve dinamik JPQL/HQL oluşturma</li>



<li>Stored procedure içinde dinamik SQL kullanımı</li>



<li><code>ORDER BY</code> ve tablo isimleri gibi yapısal alanlarda kullanıcı girdisi kullanımı</li>
</ul>



<p class="wp-block-paragraph">Her <code>Statement</code> kullanımı otomatik olarak açık anlamına gelmez. Asıl risk, güvenilmeyen girdinin SQL komutunun yapısını değiştirebilmesidir.</p>



<h3 class="wp-block-heading">Otomatik Güvenlik Testleri</h3>



<p class="wp-block-paragraph">Statik uygulama güvenliği testleri (SAST), kaynak kodda şüpheli sorgu oluşturma kalıplarını bulmanıza yardımcı olabilir.</p>



<p class="wp-block-paragraph">Dinamik uygulama güvenliği testleri (DAST), çalışan uygulamanın girdilere verdiği tepkileri analiz eder.</p>



<p class="wp-block-paragraph">OWASP ZAP ve uygun yapılandırılmış güvenlik test araçları, yetkili test süreçlerinde yardımcı olabilir.</p>



<p class="wp-block-paragraph">Bir aracın herhangi bir bulgu üretmemesi uygulamanın kesinlikle güvenli olduğu anlamına gelmez.</p>



<h3 class="wp-block-heading">Java ile Basit Bir Regresyon Testi</h3>



<p class="wp-block-paragraph">Parametreli sorgunun beklendiği gibi davrandığını birim veya entegrasyon testinde de kontrol edebilirsiniz.</p>



<p class="wp-block-paragraph">Örneğin aşağıdaki JUnit 5 testi, depo katmanının şüpheli bir girdiyi geçerli müşteri kaydı gibi yorumlamamasını bekler.</p>



<pre class="wp-block-code language-java"><code>@Test
void shouldTreatSqlPayloadAsPlainData()
        throws SQLException {

    String input = "' OR '1'='1";

    Customer result =
        repository.findByEmail(
            connection,
            input
        );

    assertNull(result);
}</code></pre>



<p class="wp-block-paragraph">Bu testi çalıştırırken izole test veritabanınızda ilgili girdiyi e-posta değeri olarak taşıyan bir kayıt bulunmadığından emin olun.</p>



<p class="wp-block-paragraph">Testin gerçek değerini artırmak için veritabanını başlangıç verileriyle kurun, bilinen bir müşteri kaydı ekleyin ve sorgunun beklenmedik kayıt döndürmediğini doğrulayın.</p>



<p class="wp-block-paragraph">Tek bir test bütün SQL Injection türlerini kapsamaz. Ancak daha önce düzelttiğiniz bir açığın tekrar ortaya çıkmasını yakalayabilir.</p>



<h2 class="wp-block-heading">Bir E-Ticaret Uygulamasında SQL Injection Güvenliği</h2>



<p class="wp-block-paragraph">Bir e-ticaret sisteminin yalnızca giriş ekranını koruduğunu varsayalım.</p>



<p class="wp-block-paragraph">Bu yeterli değildir.</p>



<p class="wp-block-paragraph">Ürün arama, fiyat filtreleme, müşteri hesapları, sipariş geçmişi, kampanya sorguları ve yönetim paneli aynı güvenlik prensiplerine ihtiyaç duyar.</p>



<p class="wp-block-paragraph">Örneğin ürün arama sorgusunu şöyle oluşturabiliriz:</p>



<pre class="wp-block-code language-java"><code>public List&lt;Product&gt; searchProducts(
        Connection connection,
        String keyword,
        BigDecimal maxPrice
) throws SQLException {

    String sql = """
        SELECT id, name, price
        FROM products
        WHERE LOWER(name) LIKE LOWER(?)
          AND price &lt;= ?
        """;

    List&lt;Product&gt; products =
        new ArrayList&lt;&gt;();

    try (PreparedStatement statement =
             connection.prepareStatement(sql)) {

        statement.setString(
            1,
            "%" + keyword + "%"
        );

        statement.setBigDecimal(
            2,
            maxPrice
        );

        try (ResultSet rs =
                 statement.executeQuery()) {

            while (rs.next()) {
                products.add(
                    new Product(
                        rs.getLong("id"),
                        rs.getString("name"),
                        rs.getBigDecimal("price")
                    )
                );
            }
        }
    }

    return products;
}</code></pre>



<p class="wp-block-paragraph">Bu örnekte iki farklı parametre bağladık.</p>



<p class="wp-block-paragraph">İlk parametre ürün isminde arama yapıyor. İkinci parametre maksimum fiyatı belirliyor.</p>



<p class="wp-block-paragraph"><code>keyword</code> değerini Java tarafında <code>%</code> karakterleriyle birleştiriyoruz. Ancak bu birleşim SQL sorgusunun yapısını değil, parametre değerini değiştiriyor.</p>



<p class="wp-block-paragraph">Bu nedenle sorgu SQL Injection&#8217;a karşı güvenli bir sınır koruyor.</p>



<p class="wp-block-paragraph">Burada küçük bir ayrıntı daha var: Kullanıcı <code>%</code> veya <code>_</code> karakterlerini arama metnine eklerse <code>LIKE</code> operatörü bu karakterlere özel anlam yükleyebilir.</p>



<p class="wp-block-paragraph">Bu davranış SQL Injection değildir. Ancak uygulama düz metin araması bekliyorsa geliştirici joker karakterleri ayrıca ele almalıdır.</p>



<p class="wp-block-paragraph">Güvenli yazılım geliştirme yalnızca SQL Injection&#8217;ı durdurmakla bitmez. Uygulamanın iş kurallarını doğru uygulamasını da gerektirir.</p>



<h2 class="wp-block-heading">SQL Injection Hakkında Sık Sorulan Sorular</h2>



<h3 class="wp-block-heading">SQL Injection sadece giriş ekranlarında mı ortaya çıkar?</h3>



<p class="wp-block-paragraph">Hayır. Arama alanları, filtreler, raporlama ekranları, API parametreleri ve veritabanına sorgu gönderen diğer işlemler SQL Injection riski taşıyabilir.</p>



<h3 class="wp-block-heading">PreparedStatement SQL Injection&#8217;ı tamamen engeller mi?</h3>



<p class="wp-block-paragraph">PreparedStatement, doğru parametre bağlama kullanımıyla sorgunun veri değerlerinden kaynaklanan SQL Injection riskini önler. Ancak kullanıcı girdisini SQL yapısına başka bir noktada eklemek yeni bir açık oluşturabilir.</p>



<h3 class="wp-block-heading">Hibernate veya JPA kullanırsam güvende olur muyum?</h3>



<p class="wp-block-paragraph">ORM araçları parametreli sorgu kullanımını kolaylaştırır. Ancak dinamik sorguları güvensiz biçimde oluşturursanız SQL Injection açığı yine ortaya çıkabilir.</p>



<h3 class="wp-block-heading">SQL Injection ve XSS aynı şey mi?</h3>



<p class="wp-block-paragraph">Hayır. SQL Injection veritabanı sorgularının yapısını hedefler. Cross-Site Scripting (XSS) ise tarayıcının işlediği içerik üzerinden istemci tarafındaki güven sınırlarını hedefler.</p>



<h3 class="wp-block-heading">SQL Injection önlemek için kullanıcı girdisindeki özel karakterleri engellemeli miyim?</h3>



<p class="wp-block-paragraph">Hayır. Genel bir özel karakter yasağı güvenilir çözüm sunmaz ve geçerli kullanıcı verilerini de reddedebilir. Parametreli sorgular, iş kurallarına uygun doğrulama ve allowlist yaklaşımı daha doğru bir savunma oluşturur.</p>



<h3 class="wp-block-heading">Web Application Firewall (WAF) yeterli mi?</h3>



<p class="wp-block-paragraph">WAF bazı şüpheli istekleri engelleyebilir. Ancak uygulamanın güvensiz sorgu oluşturma hatasını düzeltmez. WAF&#8217;ı ek koruma katmanı olarak değerlendirin.</p>



<h2 class="wp-block-heading">Sonuç: SQL Injection&#8217;a Karşı Güvenli Kodlama Alışkanlığı</h2>



<p class="wp-block-paragraph">SQL Injection, uygulamanın veri ile komut arasındaki sınırı kaybetmesiyle ortaya çıkar.</p>



<p class="wp-block-paragraph">Geliştirici kullanıcı girdisini SQL sorgusuna doğrudan eklediğinde saldırgan sorgunun davranışını etkileyebilir.</p>



<p class="wp-block-paragraph">Bu riski azaltmak için karmaşık güvenlik algoritmalarına ihtiyacımız yok. Öncelikle doğru sorgu oluşturma alışkanlığını benimsememiz gerekiyor.</p>



<p class="wp-block-paragraph">Java uygulamalarında <code>PreparedStatement</code> kullanmak, dinamik SQL parçalarını allowlist ile sınırlandırmak, veritabanı hesaplarının yetkilerini azaltmak ve sorguları düzenli test etmek güçlü bir başlangıç sağlar.</p>



<p class="wp-block-paragraph">En önemli kural oldukça basit:</p>



<p class="wp-block-paragraph"><strong>SQL komutlarını uygulama belirlesin. Kullanıcılar yalnızca veri sağlasın.</strong></p>



<p class="wp-block-paragraph">Bu ayrımı koruduğunuzda SQL Injection saldırılarının temel giriş noktasını kapatmış olursunuz.</p>



<h2 class="wp-block-heading">Kaynaklar</h2>



<ol start="1" class="wp-block-list">
<li>OWASP — SQL Injection Prevention Cheat Sheet<br><a href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html">https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html</a></li>



<li>OWASP — Top 10:2025, A05 Injection<br><a href="https://top10.owasp.org/2025/A05_2025-Injection/">https://top10.owasp.org/2025/A05_2025-Injection/</a></li>



<li>OWASP — Web Security Testing Guide<br><a href="https://owasp.org/www-project-web-security-testing-guide/">https://owasp.org/www-project-web-security-testing-guide/</a></li>



<li>Oracle — JDBC PreparedStatement<br><a href="https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html">https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html</a></li>
</ol>
<p><a href="https://alikarahisar.com/blog/sql-injection-nedir/">SQL Injection Nedir? Saldırıyı Anlamak ve Önlemek İçin Pratik Rehber</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/sql-injection-nedir/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
