- Konu sahibi
- #1
HTML çıktı kodlaması, verinin sayfada kullanılacağı bağlama göre yapılmalıdır. HTML metin düğümüne yazılan değer HTML bağlamına, etiket niteliğine yazılan değer nitelik bağlamına, JavaScript içine aktarılan değer JavaScript bağlamına göre kodlanır. Her yerde aynı kaçış yöntemini kullanmak güvenli ve doğru değildir; HTML için kullanılan kodlama JavaScript veya URL içinde yeterli olmayabilir.
Kullanıcıdan, veritabanından veya başka bir servisten gelen değer güvenilir kabul edilmemelidir. Değer, sayfaya eklenmeden hemen önce ve hedef bağlam bilinerek kodlanır. Veriyi girişte HTML entity'lerine çevirmek yerine ham biçimde saklamak, gerektiğinde farklı ortamlarda yeniden kullanmayı kolaylaştırır.
Örneğin kullanıcı adı şu değer olsun:
Bu değer HTML metni olarak gösterilecekse özel karakterler entity biçimine çevrilmelidir. PHP ile küçük bir örnek:
Beklenen sonuç, betiğin çalışması değil, karakterlerin metin olarak görünmesidir. Değer sayfada çalışabilir kod hâline geliyorsa ya yanlış bağlam kodlaması kullanılmıştır ya da kodlanmamış başka bir çıktı noktası kalmıştır.
Aşağıdaki ayrım, hangi yöntemin seçileceğini belirler:
Tablodaki yöntemler birbirinin yerine geçmez. Örneğin URL parametresi için HTML entity kodlamak, URL'nin doğru oluşturulduğu anlamına gelmez. URL daha sonra HTML niteliğine yerleştiriliyorsa önce URL bileşeni, ardından dış HTML niteliği için gereken işlem uygulanmalıdır; daha basit ve güvenli seçenek, URL'yi elle birleştirmek yerine platformun URL oluşturma API'sini kullanmaktır.
Girdiyi kaydederken encode etmek: Aynı veri e-posta, JSON veya rapor içinde kullanılacaksa önceden HTML'e çevrilmiş entity'ler sorun çıkarabilir. Ham veriyi uygun doğrulamayla saklayıp çıktı bağlamında kodlamak genellikle daha doğru yaklaşımdır.
HTML encode'u her yerde kullanmak: HTML metni için doğru olan yöntem JavaScript, CSS veya URL bağlamında aynı korumayı sağlamaz. Özellikle script içine string birleştirmek, HTML karakterlerini kaçırmakla güvenli hâle gelmez.
Güvenilir görünen veriyi atlamak: Veritabanı yöneticisi, yönetici hesabı veya dış servis yanıtı da değiştirilmiş olabilir. Güven kararı kaynağa göre değil, verinin kullanılacağı bağlama göre verilmelidir.
Çift kodlama yapmak: Bir değer daha önce encode edilmişse tekrar encode etmek kullanıcıya
Bağlama göre kodlama nasıl çalışır?
Kullanıcıdan, veritabanından veya başka bir servisten gelen değer güvenilir kabul edilmemelidir. Değer, sayfaya eklenmeden hemen önce ve hedef bağlam bilinerek kodlanır. Veriyi girişte HTML entity'lerine çevirmek yerine ham biçimde saklamak, gerektiğinde farklı ortamlarda yeniden kullanmayı kolaylaştırır.
Örneğin kullanıcı adı şu değer olsun:
"><script>alert(1)</script>Bu değer HTML metni olarak gösterilecekse özel karakterler entity biçimine çevrilmelidir. PHP ile küçük bir örnek:
Kod:
<?php
$kullaniciAdi = '"><script>alert(1)</script>';
?>
<!doctype html>
<html lang="tr">
<head><meta charset="utf-8"><title>Profil</title></head>
<body>
<p>Kullanıcı: <?= htmlspecialchars($kullaniciAdi, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></p>
</body>
</html>
Beklenen sonuç, betiğin çalışması değil, karakterlerin metin olarak görünmesidir. Değer sayfada çalışabilir kod hâline geliyorsa ya yanlış bağlam kodlaması kullanılmıştır ya da kodlanmamış başka bir çıktı noktası kalmıştır.
ENT_SUBSTITUTE geçersiz UTF-8 dizilerinin çıktıyı bozmak yerine değiştirilmesini sağlar; uygulamanın kullandığı karakter kümesiyle kodlama parametresi aynı olmalıdır.Yaygın çıktı bağlamları
Aşağıdaki ayrım, hangi yöntemin seçileceğini belirler:
| Bağlam | Doğru yaklaşım | Kaçınılması gereken |
|---|---|---|
| HTML metni | HTML özel karakterlerini encode etmek | Ham kullanıcı verisini doğrudan yazmak |
| HTML niteliği | Tırnakları da kapsayan HTML attribute encoding | Niteliği tırnaksız bırakmak |
| JavaScript verisi | Çevre bağlamı güvenli serileştirme; mümkünse ayrı JSON yanıtı | Değeri JavaScript metniyle birleştirmek |
| CSS değeri | İzin verilen değerleri listeyle sınırlamak | Kullanıcı metnini CSS kodu gibi eklemek |
| URL parametresi | URL bileşenine uygun percent-encoding | HTML encode etmeyi URL encode sanmak |
Tablodaki yöntemler birbirinin yerine geçmez. Örneğin URL parametresi için HTML entity kodlamak, URL'nin doğru oluşturulduğu anlamına gelmez. URL daha sonra HTML niteliğine yerleştiriliyorsa önce URL bileşeni, ardından dış HTML niteliği için gereken işlem uygulanmalıdır; daha basit ve güvenli seçenek, URL'yi elle birleştirmek yerine platformun URL oluşturma API'sini kullanmaktır.
Uygulama adımları
- Değerin kaynağını ve hedefini belirleyin: HTML metni, attribute, inline JavaScript, CSS veya URL. Aynı değişkenin farklı hedeflerde kullanıldığı yerleri ayrı ayrı inceleyin.
- Metni mümkünse textContent ile yerleştirin. setAttribute yalnız sabit ve güvenli nitelik adlarıyla kullanılmalıdır; onclick gibi olay niteliği veya href/src gibi URL alanı her değer için güvenli değildir. URL şemasını ve hedef politikasını ayrıca doğrulayın; javascript: değeri HTML escaping ile güvenli URL olmaz.
- Sunucu tarafı şablonunda HTML metni ve attribute değerleri için bağlama uygun HTML escaping uygulayın. Attribute değerlerini daima tırnak içinde üretin.
- JavaScript'e veri geçirmek zorundaysanız değeri JavaScript kaynak kodu olarak birleştirmeyin; ayrı application/json yanıtı gibi veri taşıma yöntemini tercih edin. Inline script içine JSON gömülüyorsa düz JSON.stringify/json_encode çıktısını yeterli sanmayın; HTML script kapanışı gibi çevre bağlamı da güvenli serileştirilmelidir. PHP’de uygun JSON_HEX_TAG gibi bayraklar bu bağlam için değerlendirilebilir.
- İzin verilen değerleri sınırlayın. CSS sınıfı, sıralama yönü veya HTML etiketi gibi alanlarda yalnızca beklenen sabit değerleri kabul etmek, sonradan kaçış yapmaya göre daha sağlamdır.
- Her çıktı noktasını saldırı karakterleri içeren test değeriyle kontrol edin. Sonuç metin olarak görünmeli, yeni etiket veya komut olarak yorumlanmamalıdır.
Sık yapılan hatalar
Girdiyi kaydederken encode etmek: Aynı veri e-posta, JSON veya rapor içinde kullanılacaksa önceden HTML'e çevrilmiş entity'ler sorun çıkarabilir. Ham veriyi uygun doğrulamayla saklayıp çıktı bağlamında kodlamak genellikle daha doğru yaklaşımdır.
HTML encode'u her yerde kullanmak: HTML metni için doğru olan yöntem JavaScript, CSS veya URL bağlamında aynı korumayı sağlamaz. Özellikle script içine string birleştirmek, HTML karakterlerini kaçırmakla güvenli hâle gelmez.
Güvenilir görünen veriyi atlamak: Veritabanı yöneticisi, yönetici hesabı veya dış servis yanıtı da değiştirilmiş olabilir. Güven kararı kaynağa göre değil, verinin kullanılacağı bağlama göre verilmelidir.
Çift kodlama yapmak: Bir değer daha önce encode edilmişse tekrar encode etmek kullanıcıya
& gibi metinler gösterebilir. Veri akışını izleyin; hangi katmanda dönüşüm yapıldığını netleştirin ve tek bir sorumlu çıktı katmanı belirleyin.