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 BYyö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_PREPARESdeğerinifalseyapmak, 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
innerHTMLile 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ızcahttpvehttpsadreslerine 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
HttpOnlybayrağı, 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.
Yorumlar
Henüz yorum yok. İlk yorumu siz yazın.