WordPress, yazılım ve IT üzerine notlar
Anasayfa » Güvenlik » SQL injection ve XSS: Her geliştiricinin bilmesi gereken iki açık ve kesin çözümleri

SQL injection ve XSS: Her geliştiricinin bilmesi gereken iki açık ve kesin çözümleri

İçindekiler
  1. SQL injection
  2. XSS (Cross-Site Scripting)
  3. Akılda kalacak iki cümle

OWASP'ın en kritik web güvenlik riskleri listesi yıllar içinde değişse de iki açık hep oradadır: SQL injection ve Cross-Site Scripting (XSS). İkisinin kökü de aynı hatadır: Kullanıcıdan gelen veriyi, kodun bir parçasıymış gibi kullanmak.

SQL injection

Nasıl oluşur?

// YANLIŞ — asla böyle yazmayın
$id = $_GET['id'];
$sonuc = $db->query("SELECT * FROM urunler WHERE id = $id");

Kullanıcı adres çubuğuna ?id=5 yazdığında sorgu beklendiği gibi çalışır. Ama ?id=5 OR 1=1 yazdığında bütün ürünler döner. ?id=5 UNION SELECT kullanici, sifre FROM yoneticiler gibi bir girdi ise başka tablolardaki verileri sızdırır.

Giriş formlarındaki klasik örnek daha da tehlikelidir:

// YANLIŞ
"SELECT * FROM kullanicilar WHERE eposta = '$eposta' AND sifre = '$sifre'"

E-posta alanına ' OR '1'='1' -- yazmak, şifre kontrolünü tamamen devre dışı bırakır.

Kesin çözüm: Hazırlanmış sorgular

$s = $pdo->prepare('SELECT * FROM urunler WHERE id = ?');
$s->execute([$_GET['id']]);
$urun = $s->fetch();

Hazırlanmış sorguda (prepared statement) SQL komutu ve veri veritabanına ayrı ayrı gönderilir. Veritabanı ? yerine gelen değeri ne olursa olsun yalnızca veri olarak görür, asla komut olarak çalıştırmaz. Bu yüzden içinde tırnak, OR ya da UNION olması bir şey değiştirmez.

Dikkat edilmesi gereken birkaç nokta:

  • addslashes() ya da elle tırnak kaçırma bir çözüm değildir. Karakter kodlaması gibi kenar durumlarda delinir.
  • Tablo adı, sütun adı ve ORDER BY yönü gibi şeyler parametre olarak verilemez. Bunları kullanıcıdan alıyorsanız bir izin listesiyle (whitelist) kontrol edin:
$izinli = ['fiyat', 'ad', 'tarih'];
$sirala = in_array($_GET['sirala'] ?? '', $izinli, true) ? $_GET['sirala'] : 'tarih';
  • PDO kullanıyorsanız bağlantıda PDO::ATTR_EMULATE_PREPARES değerini false yapmak, gerçek hazırlanmış sorgular kullanılmasını sağlar.

XSS (Cross-Site Scripting)

Nasıl oluşur?

// YANLIŞ
echo "<p>Arama sonuçları: " . $_GET['q'] . "</p>";

Kullanıcı ?q=<script>...</script> gönderdiğinde, o script sizin sitenizin içinde, sizin alan adınızın yetkileriyle çalışır. Oturum çerezlerini çalabilir, kullanıcı adına form gönderebilir, sayfayı sahte bir giriş formuyla değiştirebilir.

XSS'in üç türü vardır:

  • Yansıtılan (reflected): Zararlı kod adreste gelir ve sayfada hemen geri basılır. Kurbanın bir bağlantıya tıklaması gerekir.
  • Kalıcı (stored): Zararlı kod bir yorum, profil adı ya da ürün açıklaması olarak veritabanına kaydedilir ve o sayfayı açan herkeste çalışır. En tehlikelisi budur.
  • DOM tabanlı: Sunucu hiç dahil olmaz. Sayfadaki JavaScript, adresteki bir değeri innerHTML ile sayfaya yazar.

Kesin çözüm: Çıktıda kaçırma

Kural basittir: Veriyi HTML'e yazdığınız anda, bağlama uygun şekilde kaçırın.

function e(string $s): string {
    return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

echo '<p>Arama sonuçları: ' . e($_GET['q']) . '</p>';
echo '<input value="' . e($ad) . '">';

htmlspecialchars, <, >, " ve ' karakterlerini zararsız HTML varlıklarına çevirir. ENT_QUOTES, öznitelik içinde tek tırnakla kaçışı da engeller.

Bağlam önemlidir:

  • HTML içeriği ve öznitelikler: htmlspecialchars.
  • JavaScript içine veri: json_encode($veri, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT).
  • URL parametresi: rawurlencode.
  • Kullanıcının verdiği bağlantılar: javascript: ile başlayan adresleri reddedin; yalnızca http ve https adreslerine izin verin.

JavaScript tarafında da aynı kural geçerlidir: Kullanıcı verisini innerHTML yerine textContent ile yazın.

İkinci savunma hattı

  • Şablon motorları (Twig, Blade) çıktıyı varsayılan olarak kaçırır. Bu, unutma riskini büyük ölçüde azaltır.
  • Content-Security-Policy başlığı, sayfada hangi kaynaklardan script çalışabileceğini sınırlar. Bir yerde kaçırmayı unutsanız bile zararlı script'in çalışmasını engelleyebilir.
  • Çerezlerde HttpOnly bayrağı, JavaScript'in oturum çerezini okumasını engeller. XSS olsa bile oturum çalınması zorlaşır.

Akılda kalacak iki cümle

  • Veritabanına giden veri: Her zaman parametre olarak ver, asla sorgu metnine yapıştırma.
  • Sayfaya giden veri: Her zaman yazdığın yerde kaçır, asla olduğu gibi basma.

Bu iki kurala istisnasız uyan bir uygulamada, bu iki açık sınıfı neredeyse tamamen ortadan kalkar.

Tezcan Kabakçı
Hosting teknik destek uzmanı ve WordPress uzmanı. 2024'ten beri bir hosting şirketinde teknik destek bölümünde çalışıyor; WordPress, DNS ve e-posta sorunlarını her gün yakından görüyor. YouTube kanalı.
Bu yazı 30 Ağustos 2026 tarihinde Güvenlik kategorisinde yayınlandı.
Yeni yazılar e-postanıza gelsin

Ayda birkaç e-posta; istediğiniz an tek tıkla ayrılabilirsiniz.

Videolu anlatımlar YouTube'da
Sunucu ve WordPress sorunlarını kısa videolarla anlattığım kanala abone olabilirsiniz. ▶ Kanala abone ol

Yorumlar

Henüz yorum yok. İlk yorumu siz yazın.

Yorum yazın

Yorumlar onaylandıktan sonra yayımlanır. Bağlantı içeren yorumlar spam olarak değerlendirilebilir.