İçeriğe atla

Web Geliştirme

HTML Varlık Kodlaması ve Çözümleme: Geliştirici Rehberi

11 dk okuma

Bir kullanıcı yorum kutusuna bir kod parçası yapıştırır ve aniden tüm sayfa düzeni bozulur. Neden egzotik bir sorun değildir — yalnızca kodlanmamış tek bir < karakterinin HTML etiketi olarak yorumlanmasıdır. Tarayıcı yorum metnini göstermeyi bırakır ve etiket adı, öznitelikler ve kapatma köşeli parantez aramaya başlar. Çözüm de basit: o karakteri HTML varlığına dönüştürün ve sayfa doğru gösterilir. Bu rehber, HTML varlıklarının ne olduğunu, neden kullanıcı tarafından üretilen içerikle uğraşan her geliştiricinin bunlara ihtiyaç duyduğunu ve üretim ortamında gerçek sorunlara yol açan yaygın hataları nasıl önleyeceğinizi açıklar.

HTML varlıklarının gerçekte ne olduğu

HTML, kendi sözdizimi için küçük bir karakter kümesini ayırır: köşeli parantezler etiketleri tanımlar, ampersandlar varlıkları başlatır ve tırnak işaretleri öznitelik değerlerini sarar. Bu karakterlerden birini düz metin olarak görüntülemeniz gerektiğinde — markup olarak değil — onu bir varlık referansıyla değiştirirsiniz. Varlık referansı, ampersand ve noktalı virgül içine alınmış kısa bir isimdir (&amp;lt;) ya da sayısal bir koddur (&#60; veya &#x3C;). Tarayıcı ayrıştırma sırasında varlığı yorumlar ve karşılık gelen karakteri yapısal HTML olarak işlemeksizin görüntüler.

İki varlık ailesi vardır. Adlı varlıklar insan tarafından okunabilir isimler kullanır — ampersand için &amp;amp;, küçüktür için &amp;lt;, büyüktür için &amp;gt;. Sayısal (ondalık) varlıklar karakterin Unicode kod noktasını kullanır: küçüktür için &#60;, on altılık gösterimde &#x3C;. Her iki yaklaşım da tarayıcıda aynı sonucu üretir; adlı varlıklar insanlar için daha okunabilir, sayısal varlıklar ise her Unicode karakteri kapsar.

Her web geliştiricisinin ezberlemesi gereken beş varlık şunlardır: &amp;amp; (&), &amp;lt; (<), &amp;gt; (>), &amp;quot; ("), &amp;apos; ('). HTML5 spec'i ayrıca kesme yer boşluğu için &amp;nbsp; tanımlar; bu, sözdizimi karakterine karşılık gelmeyen ancak web içeriğinde sürekli görünen adlı varlıktır.

Beş zorunlu varlık ve sayısal alternatifleri

Adlı VarlıkKarakterOndalıkOn AltılıkNe Zaman Gerekli
&amp;amp;&&38;&#x26;Metin veya öznitelik değerlerindeki her ampersand — önce kodlanmalıdır
&amp;lt;<&#60;&#x3C;Etiket dışında her küçüktür işareti; kod parçacıkları, karşılaştırmalar gösterir
&amp;gt;>&#62;&#x3E;Metindeki büyüktür işareti; kod bloklarında &lt; ile birlikte gereklidir
&amp;quot;"&#34;&#x22;HTML öznitelik değerlerinin içindeki çift tırnak işaretleri
&amp;apos;'&#39;&#x27;Tek tırnaklı özniteliklerin içindeki tek tırnak işaretleri; XML uyumluluğu

Ampersandları önce kodlayın — her zaman

Ampersand, tüm varlıkların geçiş karakteridir. Önce < kodlayıp &amp;'ı kodlamazsanız, tarayıcı yeni oluşturduğunuz &amp;lt; literal bir ampersand ve ardından "lt;" olarak yorumlayabilir, &amp;lt; varlığı olarak değil. Her şeyi işlemeye başlamadan önce &amp;'ı &amp;amp; olarak kodlayın. Bu, üretim kodundaki en yaygın kodlama hatasıdır.

Kullanıcı girişini kodlamanın neden bir güvenlik gereksinimi olduğu

Bir uygulama bir kullanıcının yazdığı metni depolayıp HTML'e kodlamadan geri gösterdiğinde, o metindeki köşeli parantezler DOM'un parçası haline gelir. <script>alert('XSS')</script> içeren kötü niyetli bir yorum metin olarak görüntülenmez — JavaScript olarak çalıştırılır. Bu, siteler arası betik dağıtma (XSS) saldırısıdır ve OWASP Top 10 listesinde her baskıda yer alır çünkü geliştiriciler hâlâ kullanıcı girişini kodlamayı unutur.

Savunma basittir: kullanıcı tarafından üretilen bir dize HTML çıktısına girmeden önce, beş özel karakteri kodlayın. <script> dizesi doğru kodlamadan geçtiğinde &amp;lt;script&amp;gt; olur ve tarayıcı onu zararsız görünür metin olarak görüntüler. Aynı ilke öznitelikler için de geçerlidir: kullanıcının adı çift tırnak içeriyorsa ve siz bunu kodlamadan title özniteliğine eklerseniz, saldırgan öznitelikten çıkarak keyfi HTML enjekte edebilir.

<!-- TEHLİKELİ: kullanıcı girdisi ham olarak enjekte edildi -->
<div class="comment">${userComment}</div>

<!-- GÜVENLİ: kullanıcı girdisi enjeksiyondan önce kodlandı -->
<div class="comment">${encodeHtml(userComment)}</div>

<!-- TEHLİKELİ: ad özniteliğe kodlanmadan eklendi -->
<img src="photo.jpg" alt="${userName}">

<!-- GÜVENLİ: ad öznitelik içinde kodlandı -->
<img src="photo.jpg" alt="${encodeHtml(userName)}">

HTML kodlaması ile URL kodlaması: farklı sorunları çözerler

Geliştiriciler HTML kodlaması ile URL kodlamasını (yüzde kodlama olarak da bilinir) sıkça karıştırır. İkisi tamamen farklı amaçlara hizmet eder. HTML kodlaması, bir HTML belgesi içindeki metin içeriğini koruyarak özel karakterlerin görüntülenmesini sağlar, ayrıştırılmasını değil. URL kodlaması ise bir URL yolu, sorgu dizesi veya parçasındaki karakterleri koruyarak HTTP isteğinin doğru şekilde iletilmesini sağlar.

HTML'de boşluk &amp;nbsp; olur (ya da satır kırma ihtiyacınız yoksa düz boşluk). Aynı boşluk URL'de %20 olur. HTML'de ampersand &amp;amp; olur — ancak URL sorgu dizesinde bir ayırıcıdır, bu yüzden bir parametre değerinde literal ampersand gerekiyorsa %26 olur. Yanlış bağlamda yanlış kodlama kullanmak işleri bozar: &amp;nbsp; bir URL içinde çalışmaz ve %20 HTML içinde literal metin olarak görünür.

Kural

HTML metni veya öznitelikleri içindeyse: HTML varlıkları kullanın. URL'ler, dosya yolları veya sorgu parametreleri içindeyse: yüzde kodlama kullanın. Bir değer her ikisinden geçiyorsa (örneğin bir bağlantının href özniteliğindeki arama sorgusu), önce sorgu değerine URL kodlaması uygulayın, sonra tüm href'i markup içine eklemeden HTML kodlayın.

Kodlamanın önemli olduğu gerçek dünya senaryoları

HTML kodlaması akademik bir egzersiz değildir. Kullanıcı tarafından üretilen içeriği görüntüleyen veya dinamik veri işleyen her uygulama bu durumlarla günlük olarak karşılaşır:

  • Bloglarda veya belgelerde kod parçacıkları: Bir python karşılaştırmasını gösteren eğitim yazısı if x < 10 and y > 5:蜡rkeli parantezler kodlanmazsa çevreleyen HTML'i bozar. < bir etiket başlatır, tarayıcı hata kurtarma moduna girer ve görünür çıktı bozulur.
  • Kullanıcı yorumları ve forum iletileri: Serbest metin girdisi kabul eden her platform — yorumlar, incelemeler, sohbet iletileri — çıktıyı kodlamalıdır. a < b yazan yorum kötü niyetli değildir, ancak kodlanmamış < yine de渲染mayı bozacaktır.
  • Script etiketleri içindeki JSON: Sunucu tarafından oluşturulmuş ve <script> bloğuna gömülü JSON, dikkatli işlem gerektirir. </script> içeren dize değerleri script etiketini erken kapatır. Güvenli kalıp, JSON dize değerlerinde < ve > kodlamak veya type="application/json" ile JavaScript ayrıştırması olmayan bir veri bloğu kullanmaktır.
  • Özel karakterli mailto bağlantıları: Bir mailto bağlantısının konu satırındaki & HTML href içinde &amp;amp; olarak kodlanmalıdır, gerçek konu parametresi ise boşluk ve ASCII dışı karakterler için yüzde kodlama kullanır.
  • Özniteliklerdeki çift ampersand: HTML doğrudan href="search?q=a&&b" yazmak sorunludur. Tarayıcı ikinci &'ı bir varlık referansı başlatıcı olarak görür. Her iki ampersand da kodlanmalıdır: href="search?q=a&amp;amp;b".
  • Veri özniteliklerindeki küçüktür: data-config="{min: 0, max: 10}" gibi bir veri özniteliği sorunsuzdur, ancak data-template="<div>content</div>" tamamen kodlanmalıdır çünkü HTML ayrıştırıcısı JavaScript özniteliği görmeden önce < ve >'yi etiket olarak yorumlar.

Fazla ve eksik kodlama: iki hata modu

Kodlamayı her iki yönde de yanlış yapmak görünür hatalar yaratır. Eksik kodlama, yeterli karakteri kodlamamış olmanız demektir. Sonuçları bozuk düzenlerden (serseri bir < sayfanın bir kısmını çalar) tam XSS açıklarına kadar uzanır. Eksik kodlama daha tehlikeli hatadır çünkü güvenliği değil yalnızca görüntülemeyi etkiler.

Fazla kodlama ise kodlanması gerekmeyen karakterleri kodlamanız veya aynı karakterleri birden fazla kez kodlamanızdır. Kullanıcının adı Tom & Jerry gibi görünürken ampersand iki kez kodlandığında Tom &amp;amp; Jerry olur. Bu, bir boru hattının aynı dizeyi iki farklı aşamada kodlamasıyla gerçekleşir — uygulama bir kez kodlar, ardından bir şablon motoru veya içerik güvenlik katmanı tekrar kodlar. Sonuç okunamayan metin ve bozuk görüntülemedir.

Her iki sorunu da önlemenin yolu net bir kuraldır: tek seferde, çıkış noktasında kodlayın ve asla öncesinde kodlamayın. Ham değeri veritabanınızda saklayın. HTML markup'a eklediğiniz anda HTML kodlaması uygulayın. Kaydederken kodlamayın, arka uç işlevleri arasında geçirirken kodlamayın, şablon değişkenine saklarken kodlamayın. Tek noktadan kodlama çift kodlamayı önler; çıkışta tutarlı kodlama eksik kodlamayı önler.

// YANLIŞ: depolama sırasında kodlama
db.save({ comment: encodeHtml(userInput) }); // çift kodlama riski

// DOĞRU: çıkış noktasında kodlama
const comment = db.getComment(id); // ham metin
element.innerHTML = encodeHtml(comment); // burada tek seferde kodlayın

Her bağlamda hangi karakterlerin kodlanması gerektiği

Her karakter her bağlamda kodlanmayı gerektirmez. Gerekli minimum küme, metnin nerede göründüğüne bağlıdır:

  • Öğe içeriğinde (açma ve kapama etiketleri arasında): & ve < kodlanmalıdır. > teknik olarak isteğe bağlıdır ancak kesinlikle önerilir — tarayıcılar içerikte kodlanmamış >'yi tolere eder, ancak CDATA bölümleri ve komşu metin düğümleriyle kenar durumlarında sorunlara neden olabilir.
  • Çift tırnaklı özniteliklerde: &, < ve " kodlanmalıdır. <, öznitelik değerlerinde nadiren görülür ancak mevcut olduğunda kodlanmalıdır.
  • Tek tırnaklı özniteliklerde: Çift tırnak yerine &, < ve ' kodlanmalıdır.
  • HTML'e eklenen JavaScript dize değerlerinde: Beş zorunlu varlığın tamamı. Bir JS değişkeni innerHTML, template literal veya document.write içine akıyorsa, her özel karakter kodlanmalıdır.
  • CDATA bölümleri içinde (XML/XHTML): HTML5 belgelerinde gömülü <![CDATA[...]]> bloklarının içinde varlık kodlamasını tamamen atlayabilirsiniz — ]]> dizgesi hariç, bu asla kodlanmamış olarak görünmemelidir. CDATA, SVG ve MathML için geçerlidir.

Tarayıcı tuhaf davranışları ve kenar durumlar

Modern tarayıcılar birçok HTML hatasını affeder, ama bu affedicilik yanlış bir güvenlik hissi yaratır. Farkında olmanız gereken belirli kenar durumlar:

  • Özel karakterli tırnaksız öznitelikler: class=foo&bar gibi tırnaksız yazmak bozulur çünkü tarayıcı &'ı varlık başlatıcı olarak görür. Öznitelik değerlerini her zaman tırnak içine alın ve o tırnakların içeriğini kodlayın.
  • Kapama etiketi olmayan öğeler: <script>, <style> ve <textarea> gibi öğeler özel bir ayrıştırma modu kullanır. Bu öğelerin içinde HTML5'te neredeyse hiç varlık çözümlemesi gerçekleşmez — erken etiket kapanmasını önlemek için < ve & kodlamanız gerekir, ancak &amp;nbsp; gibi adlı varlıklar literal metin olarak işlenir.
  • Geçersiz varlık referansları: Tarayıcı tanınmayan varlıklardan noktalı virgülleri sessizce düşürür. &foo; yazmak çoğu tarayıcıda &foo; literal metni olarak görüntülenir, ancak &foo (noktalı virgül olmadan) bir sonraki karakteri yutabilir — izlenmesi son derece zor bir hatadır.
  • Sayısal karakter referansları ve kontrol karakterleri: HTML5'te &#x0; (null) gibi kontrol karakterleri için sayısal varlıklar kullanmak yasaktır. Ayrıştırıcı bunları sessizce U+FFFD (yerine karakter) ile değiştirir veya tamamen düşürür.

Noktalı virgül tuzağı

Noktalı virgül içermeyen bir varlık referansı belirsizdir. &amp;lt without the semicolon is parsed as the entity &amp;lt followed by whatever character comes next. Kullanıcı &amp;cat yazarak istemeden &amp;ca varlığı ve ardından yalnız bir t oluşturur. Kodlama işlevinizin ürettiği her varlık için noktalı virgül içerdiğinden her zaman emin olun.

Pratikte kodlama stratejileri

Çoğu web çerçevesi yerleşik kodlama işlevleri içerir. React'in JSX'i, öğe içeriğindeki ifade değerlerini otomatik kodlar (ancak dangerouslySetInnerHTML bunu tamamen atlar — yalnızca güvenilir, önceden temizlenmiş içerik için kullanın). Jinja2, Handlebars ve Blade gibi sunucu taraflı şablon motorları filtre sağlar. Temel nokta: çerçevenizin nerede otomatik kodladığını ve nerede kodlamadığını bilmektir.

Beş zorunlu varlık için minimal bir kodlama işlevi basittir. İşlev, ampersandları önce olmak üzere her özel karakteri adlı varlığıyla değiştirir:

function encodeHtml(str) {
  return str
    .replace(/&/g, "&amp;")   // önce bu olmalı
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

// Kullanım:
const safe = encodeHtml('He said "a < b & c > d"');
// → He said &quot;a &lt; b &amp; c &gt; d&quot;

Standart kütüphaneyi tercih edin

Yukarıdaki manuel işlev anlama içindir, üretim için değil. Node.js'te yerleşik text Encoding API'sini veya iyi test edilmiş bir kütüphane kullanın. Tarayıcıda textContent ataması çoğu durumu otomatik olarak halleder. Kullanıcı tarafından sağlanan HTML'e bazı etiketleri izin vermeniz ancak diğerlerini engellemeniz gerektiğinde DOMParser API'si en güvenli yoldur — betikleri çalıştırmadan ayrıştırır.

Convrs'teki HTML kodlama araçları

HTML varlıklarını hızlıca kodlamanız veya çözümlemeniz gerektiğinde —ister bir görüntüleme sorununu ayıklıyor olun, ister bir CMS için içerik hazırlıyor olun, ister kodlanmış metnin nasıl görüneceğini test ediyor olun — Convrs'teki HTML Encode/Decode aracı her iki yönü de tarayıcıda işler. Metninizi yapıştırın, kodlama veya çözümlemeyi seçin ve anında sonucu alın. Dosya yükleme yok, sunucu gidiş dönüşü yok, cihazınızdan dışarı çıkmayan veri. Beş zorunlu varlığın yanı sıra sayısal referansları da kapsar, böylece çıktınızın tarayıcının beklediğiyle eşleştiğini doğrulayabilirsiniz.

Buy me a coffee