XSS nedir, nasıl önlenir?
XSS (Cross-Site Scripting, siteler arası betik çalıştırma), bir saldırganın sitenizin sayfasına kendi betiğini karıştırabilmesidir. Betik sitenizin adresinden geldiği için ziyaretçinin tarayıcısı ona sitenizin kendi kodu gibi güvenir: sayfadaki içeriği değiştirebilir, kullanıcı adına istek gönderebilir, sahte bir giriş formu gösterebilir ya da korunmayan oturum çerezini okuyabilir.
Açığın nedeni, kullanıcıdan gelen verinin sayfaya kaçışlanmadan yazılmasıdır. Çözümün özü de buradadır: her veri, sayfaya yazıldığı bağlama uygun biçimde kaçışlanır. Bu rehber kavramı, türlerini ve PHP ile doğru uygulamayı anlatır.
XSS nasıl ortaya çıkar?
-
Üç tür, tek neden
- Kalıcı (stored) XSS: Zararlı içerik veritabanına kaydedilir (yorum, profil adı, destek talebi) ve o sayfayı açan herkese gösterilir. Etkisi en geniş türdür; yönetim panelinde görüntülenen kayıtlar yöneticiyi de etkiler.
- Yansıyan (reflected) XSS: Zararlı içerik istekteki bir parametreden gelir ve yanıtta aynen geri yazılır (arama terimi, hata mesajı). Kullanıcının özel hazırlanmış bir bağlantıya tıklaması gerekir.
- DOM tabanlı XSS: Sorun sunucuda değil tarayıcıdaki JavaScript kodundadır; betik, adresten ya da başka bir kaynaktan aldığı veriyi sayfaya HTML olarak yazar.
Üçünde de neden aynıdır: güvenilmeyen veri, sayfada kod olarak yorumlanabilecek bir yere kaçışlanmadan yerleştirilmiştir.
-
Koruma çıktıda yapılır
Veriyi kaydederken "temizlemek" yerine, sayfaya yazarken kaçışlayın. Aynı veri bir gün HTML içinde, başka bir gün bir e-postada ya da bir JSON yanıtında kullanılabilir; her birinin kaçışlama kuralı farklıdır. Veritabanında ham veriyi saklayıp çıktıda bağlama göre kaçışlamak hem güvenli hem tutarlıdır.
-
Bağlam kaçışlamayı belirler
HTML gövdesi, HTML özniteliği, JavaScript içi ve URL parametresi ayrı bağlamlardır ve her birinin kendi kuralı vardır. HTML için doğru olan kaçışlama, JavaScript içinde yeterli değildir. Aşağıdaki bölümde her bağlam için doğru yöntemi bulacaksınız.
-
İkinci katmanlar: CSP ve çerez bayrakları
Kaçışlamada bir yer gözden kaçarsa zararı sınırlayacak katmanlar gerekir: Content-Security-Policy tarayıcıya hangi kaynaklardan betik çalıştırabileceğini söyler; HttpOnly bayrağı oturum çerezinin JavaScript tarafından okunmasını engeller.
Belirtiler: nasıl fark edilir?
- Kod incelemesinde:
echo $_GET[...],<?= $degisken ?>gibi kaçışlanmadan yapılan çıktılar; JavaScript tarafındainnerHTML,document.write, jQuery.html()ve.append()ile kullanıcı verisinin sayfaya yazılması. - Sitede: Bir alana HTML etiketi içeren zararsız bir metin (ör. kalın yazı etiketi) yazdığınızda metin olduğu gibi görünmek yerine biçimlenmiş çıkıyorsa, o alan kaçışlanmıyordur. Bunu yalnızca kendi sitenizde deneyin.
- Ziyaretçi şikâyetleri: Beklenmedik yönlendirmeler, açılır pencereler, sayfada size ait olmayan içerik.
- CSP raporları: Raporlama açıksa, izin verilmeyen kaynaklardan betik yükleme girişimleri raporlanır.
Bağlama göre çıktı kaçışlama
HTML gövdesi ve öznitelikler
PHP'de htmlspecialchars işlevini ENT_QUOTES ve karakter kümesiyle kullanın. Kısa bir yardımcı işlev, her yerde aynı ayarın kullanılmasını sağlar. Öznitelik değerlerini her zaman tırnak içine alın; tırnaksız öznitelikte kaçışlama yeterli olmaz.
<?php
// Tek bir yardımcı: her yerde aynı ayar
function e($deger): string
{
return htmlspecialchars((string) $deger, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
?>
<p>Merhaba, <?= e($kullaniciAdi) ?></p>
<input type="text" name="q" value="<?= e($_GET['q'] ?? '') ?>">
JavaScript içine veri aktarma
PHP'den JavaScript'e veri aktarırken değeri tırnak içine elle yazmayın; json_encode ile ve HTML'e duyarlı karakterleri dönüştüren bayraklarla aktarın. Daha da güvenli yol, veriyi bir data- özniteliğine koyup JavaScript'ten okumaktır.
<?php
$veri = ['ad' => $kullaniciAdi, 'sepet' => $sepetAdedi];
$bayraklar = JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE;
?>
<script>
var sayfaVerisi = <?= json_encode($veri, $bayraklar) ?>;
</script>
<!-- Daha güvenli seçenek: veriyi data- özniteliğiyle taşıyın -->
<div id="kullanici" data-ad="<?= htmlspecialchars($kullaniciAdi, ENT_QUOTES, 'UTF-8') ?>"></div>
URL ve bağlantılar
Bir URL'nin parametre değerine yazılan veri rawurlencode ile kodlanır. Kullanıcının verdiği bir adres href ya da src içine yazılacaksa, kaçışlamaya ek olarak şemasını denetleyin: yalnızca http ve https ile başlayan adreslere izin verin.
<?php
// Parametre değeri: rawurlencode
$adres = 'arama?q=' . rawurlencode($terim);
// Kullanıcının verdiği adres: yalnız http/https şemasına izin ver
function guvenliAdres(string $adres): string
{
$sema = strtolower((string) parse_url($adres, PHP_URL_SCHEME));
return in_array($sema, ['http', 'https'], true) ? $adres : '#';
}
?>
<a href="<?= htmlspecialchars($adres, ENT_QUOTES, 'UTF-8') ?>">Sonuçlar</a>
<a href="<?= htmlspecialchars(guvenliAdres($profilSitesi), ENT_QUOTES, 'UTF-8') ?>" rel="noopener nofollow">Web sitesi</a>
Kaçınılacak yerler
Güvenilmeyen veriyi <script> bloğunun içine doğrudan, olay özniteliklerine (onclick gibi), <style> içine ya da etiket ve öznitelik adı olarak yazmayın. Bu bağlamlarda güvenli kaçışlama zordur; veriyi data- özniteliğiyle taşıyın.
Tarayıcı tarafı (DOM) için güvenli kullanım
JavaScript'te veriyi sayfaya yazarken HTML olarak yorumlayan yöntemler yerine metin olarak yazan yöntemleri kullanın. textContent veriyi her zaman düz metin olarak ekler.
// YANLIŞ: veri HTML olarak yorumlanır
// kutu.innerHTML = 'Merhaba ' + ad;
// DOĞRU: veri düz metin olarak eklenir
kutu.textContent = 'Merhaba ' + ad;
// Yeni öğe oluştururken
var satir = document.createElement('li');
satir.textContent = yorum.metin;
liste.appendChild(satir);
React, Vue ve benzeri çerçeveler şablonlarda değerleri varsayılan olarak kaçışlar. Risk, bu korumayı bilerek devre dışı bırakan özelliklerde geri gelir (dangerouslySetInnerHTML, v-html). Bu özelliklere yalnızca temizlenmiş HTML verin.
Zengin metin: izin listesiyle temizleme
Blog editörü ya da ürün açıklaması gibi alanlarda kullanıcının HTML girmesi gerekiyorsa kaçışlama kullanılamaz; çünkü biçimlendirmenin görünmesi istenir. Bu durumda HTML, bir izin listesine göre temizlenir: yalnızca belirli etiketlere (paragraf, kalın, liste, bağlantı gibi) ve belirli özniteliklere izin verilir, geri kalan her şey atılır.
- Temizleme için kendi düzenli ifadelerinizi yazmayın; HTML'yi düzenli ifadeyle güvenle süzmek pratikte mümkün değildir. Bu iş için geliştirilmiş, bakımı süren bir kütüphane kullanın (PHP'de HTML Purifier, tarayıcıda DOMPurify gibi).
strip_tagstek başına yeterli değildir: izin verdiğiniz etiketlerin özniteliklerini temizlemez.- Temizlemeyi sunucuda yapın; tarayıcıdaki editörün ürettiği HTML'ye güvenmeyin.
- Bağlantılarda yalnızca
http,httpsvemailtoşemalarına izin verin.
Content-Security-Policy (CSP)
CSP, tarayıcıya sayfanın hangi kaynaklardan betik, stil, görsel yükleyebileceğini bildiren bir yanıt başlığıdır. Satır içi betiklere izin vermeyen bir politika, sayfaya bir şekilde karışan betiğin çalışmasını engeller. CSP kaçışlamanın yerine geçmez; hata yapıldığında devreye giren emniyet kemeridir.
Mevcut bir sitede katı bir politika sayfaları bozabilir. Önce raporlama modunda başlayın; hiçbir şey engellenmez, ihlaller yalnızca tarayıcı konsolunda ve tanımladıysanız rapor adresinde görünür. İhlalleri giderdikten sonra uygulamaya geçin.
<IfModule mod_headers.c>
# 1. aşama: yalnız raporla, hiçbir şeyi engelleme
Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
# 2. aşama: ihlaller giderildikten sonra başlığın adını
# Content-Security-Policy olarak değiştirin
</IfModule>
Satır içi betikleri ayrı dosyalara taşımak CSP'yi sıkılaştırmanın en önemli adımıdır. Taşınamayan satır içi betikler için her istekte değişen bir nonce değeri kullanılabilir. Ayrıntılar için Güvenlik başlıkları ve HTTPS rehberine bakın.
Oturum çerezini koruyun
Oturum çerezi HttpOnly olarak işaretlendiğinde JavaScript tarafından okunamaz; bir XSS açığı olsa bile çerezin doğrudan çalınması engellenir. Secure bayrağı çerezin yalnızca HTTPS üzerinden gönderilmesini, SameSite ise başka sitelerden gelen isteklerle gönderilmesini sınırlar.
<?php
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'secure' => true, // yalnız HTTPS
'httponly' => true, // JavaScript okuyamaz
'samesite' => 'Lax',
]);
session_start();
HttpOnly, XSS'in diğer etkilerini (kullanıcı adına işlem yapma, sayfa içeriğini değiştirme) önlemez; asıl çözüm yine kaçışlamadır.
Kontrol listesi
- Şablonlardaki tüm değişken çıktıları kaçışlama yardımcısından geçiyor.
- Öznitelik değerleri tırnak içinde.
- JavaScript'e veri
json_encodeya dadata-özniteliğiyle aktarılıyor. - Kullanıcıdan gelen adreslerde şema denetimi var.
- JavaScript'te kullanıcı verisi
textContentile yazılıyor;innerHTMLkullanımları gözden geçirildi. - Zengin metin sunucuda izin listeli bir kütüphaneyle temizleniyor.
- CSP en azından raporlama modunda tanımlı.
- Oturum çerezi HttpOnly, Secure ve SameSite bayraklarıyla veriliyor.
- Yönetim panelinde listelenen kullanıcı girdileri (form başvuruları, yorumlar) de kaçışlanıyor.
Sık sorulan sorular
Girdiyi kaydederken temizlersem çıktıda kaçışlamam gerekir mi?
Evet. Koruma çıktıda yapılır; çünkü aynı veri farklı bağlamlarda kullanılabilir ve veritabanına başka yollardan da veri girebilir. Girdide yalnızca biçim doğrulaması yapın, kaçışlamayı çıktıya bırakın.
strip_tags kullanmak XSS'i önler mi?
Tek başına güvenilir değildir. Öznitelik bağlamında işe yaramaz, izin verilen etiketlerin özniteliklerini temizlemez. Düz metin alanlarında htmlspecialchars, zengin metinde izin listeli bir temizleme kütüphanesi kullanın.
CSP eklersem kaçışlamaya gerek kalır mı?
Hayır. CSP ikinci savunma hattıdır; eski tarayıcılar, yanlış yapılandırma ya da izin verilen kaynaklar üzerinden atlatılabilir. Asıl koruma doğru kaçışlamadır.
Yalnızca yöneticilerin gördüğü sayfalarda da kaçışlama gerekir mi?
Evet, özellikle orada. İletişim formu ya da yorum gibi herkesin doldurabildiği alanlar yönetim panelinde listelenir; kaçışlanmazsa betik yönetici oturumunda çalışır.
BYK Yazılım Destek Ekibi
Bu rehber BYK Yazılım destek ekibi tarafından hazırlanır ve düzenli olarak gözden geçirilir. Son güncelleme: 04.10.2026.
İlgili rehberler
Web sitenizi birlikte gözden geçirelim
BYK Yazılım kurumsal web siteleri geliştirir. Sitenizle ilgili sorularınız için bize yazabilirsiniz.
Bize ulaşın Kurumsal web sitesi hizmetimiz