A böngésző ugyanabban a dokumentumban olvassa a markup szerkezetét és a felhasználónak szánt szöveget. Emiatt bizonyos karakterek kettős szerepet kapnak. A kisebb jel kezdhet taget, az ampersand karakterhivatkozást, az idézőjel pedig lezárhat egy attribútumértéket. Ha ezeket egyszerűen szövegként szeretnénk megjeleníteni, olyan jelölés kell, amely egyértelműen közli a parserrel: itt nem új HTML-szerkezet kezdődik. Ezt a feladatot látják el a karakterhivatkozások, közismert nevükön HTML entityk.

A markup és a tartalom ugyanazon a karakterfolyamon osztozik

Egy <p> szekvencia elemet nyit, de egy matematikai magyarázatban a „3 < 5” kisebb jele csupán tartalom. A parsernek kontextus alapján kell különbséget tennie. A &lt; hivatkozás feldolgozás után valódi kisebb jellé válik a text node-ban, anélkül hogy taget nyitna.

Ez nem a böngésző vizuális trükkje, hanem a HTML tokenizálás része. A forrásban több karakterből álló hivatkozás jelenik meg, a DOM-ban azonban rendszerint már a reprezentált Unicode karakter található. A fejlesztőeszköz ezért mást mutathat a „View Source” és az Elements panel nézetében.

Három elterjedt karakterhivatkozási forma van

A névvel ellátott entity például &amp; vagy &copy;. A decimális numerikus forma &#169;, a hexadecimális pedig &#xA9;. Mindhárom ugyanahhoz a karakterhez vezethet, de az olvashatóság és kompatibilitás eltér.

A név kényelmes az ismert jelekhez, a numerikus hivatkozás bármely megfelelő Unicode kódponthoz használható. Modern UTF-8 dokumentumban a legtöbb nem ASCII karakter közvetlenül is leírható, ezért nem szükséges minden ékezetes betűt vagy szimbólumot entityvé alakítani.

Az ampersand saját magát is escape-eli

Mivel az entity ampersanddal kezdődik, egy szó szerinti & bizonyos helyzetekben &amp; formában jelenik meg. URL query szövegében ez gyakori: a HTML forrásban az elválasztó escape-elt, miközben a DOM attribútumértéke és a böngésző által kért URL valódi ampersandot tartalmaz.

Ez könnyen dupla kódoláshoz vezet. Ha egy már HTML-escape-elt stringet újra escape-elünk, a &amp; forrásból &amp;amp; lesz, a felhasználó pedig szó szerint „&amp;” szöveget láthat. Minden adatfolyamban pontosan egy réteg legyen felelős a végső output encodingért.

Az idézőjel az attribútum kontextusában különleges

Idézőjellel határolt attribútumban ugyanaz a karakter lezárhatja az értéket. Ezért dinamikus tartalomnál nemcsak a kisebb jelet és ampersandot, hanem az adott határolót is kódolni kell. A biztonságos template engine ezt az attribútumkontextusnak megfelelően végzi.

Az idézőjel nélküli attribútum több karakterre érzékeny, és nehezebb helyesen kezelni, ezért dinamikus értéknél kerülendő. Az entityk ismerete nem indok a kézi string-összefűzésre; a strukturált DOM API és autoescaping sablon egyszerre olvashatóbb és biztonságosabb.

A karakterkódolás és az entity két külön réteg

UTF-8 meghatározza, hogyan lesz egy Unicode karakterből bájtsorozat. A HTML entity azt mondja meg, hogyan írjuk le a karaktert a markup nyelvtanán belül. Egy é közvetlen UTF-8 bájtokkal és numerikus hivatkozással is ugyanazt a DOM-karaktert eredményezheti.

Ha a szerver rossz charsetet deklarál, a közvetlen karakterek hibásan jelenhetnek meg. A numerikus hivatkozás bizonyos esetben túléli ezt, de nem jó megoldás a következetlen encodingra. A dokumentum, HTTP fejléc, fájl és adatbázis egységesen UTF-8-at használjon.

A névvel ellátott entityk történelmi örökséget hordoznak

A HTML sok, matematikából, tipográfiából és korábbi szabványokból származó nevet támogat. Egy részük kis- és nagybetűre érzékeny, némely régi környezetben pontosvessző nélkül is felismerhető. Ez a böngésző-kompatibilitás miatt létezik, nem követendő szerzői gyakorlat.

Új markupban a pontosvesszőt mindig ki kell írni. Enélkül a következő alfanumerikus karakter megváltoztathatja a tokenizálást, attribútumban pedig további kétértelműség keletkezik. A valid, explicit forma könnyebben olvasható és eszközökkel ellenőrizhető.

Nem minden láthatatlan karakter ártalmatlan

Numerikus hivatkozással vezérlő, irányváltó vagy nulla szélességű karakter is beilleszthető. Ezek legitim nyelvi és tipográfiai célra szolgálhatnak, de azonosítókban, logokban vagy biztonsági felületen megtévesztő szöveget készíthetnek.

Az entity dekódolása után a rendszernek továbbra is alkalmaznia kell a domén validációját. Felhasználónév, fájlnév és forráskód-megjelenítés eltérő karakterpolicyt igényel. A „valid HTML” nem jelenti azt, hogy az adat minden üzleti kontextusban elfogadható.

Az entity nem általános adattovábbítási formátum

HTML escapinget csak akkor érdemes alkalmazni, amikor adatot HTML dokumentumba helyezünk. Adatbázisban, JSON API-ban vagy keresési indexben a nyers logikai szöveg tárolandó. Ha előre escape-elt szöveg kerül ezekbe a rétegekbe, más kimeneti célok hibás karaktereket kapnak.

Egy e-mail tárgy, natív mobilfelület és CSV-export nem ugyanazt a kódolást használja. A tárolt adat legyen prezentációfüggetlen, majd minden renderer a saját kontextusának megfelelően escape-eljen a lehető legkésőbbi ponton.

A böngésző dekódolása nem sanitization

Az entityk feldolgozása karaktereket állít elő; nem dönti el, biztonságos-e az így kapott markup. A &lt;script&gt; text node-ban ártalmatlanul jelenik meg, de ha később dekódolják és innerHTML-ként újraparsolják, aktív taggé válhat.

A rendszernek nyomon kell követnie, hogy egy érték nyers adat, HTML-szöveg vagy megbízhatóan sanitizált markup. Ezeket nem szabad ugyanazzal a string típussal és véletlenszerű decode hívásokkal keverni. A dekódolás adattranszformáció, nem biztonsági jóváhagyás.

A copy-paste felfedi a DOM valódi tartalmát

A felhasználó rendszerint a renderelt karaktert másolja, nem a forrás entity szövegét. Ez a kívánt viselkedés dokumentációban és matematikai képletben. Kódrészlet bemutatásakor viszont lehet, hogy magát a &lt; karakterláncot szeretnénk láthatóvá tenni, ezért az ampersandot újabb szinten kell escape-elni.

A rétegek számát példákkal érdemes tesztelni. Forráskód, DOM textContent, renderelt képernyő és vágólap négy külön megfigyelési pont. Egy snapshot csak az egyiket bizonyíthatja, ezért a felhasználói célnak megfelelő szinten kell ellenőrizni.

A template engine kontextusa fontosabb az entitylista ismereténél

A fejlesztőnek nem kell több ezer névvel ellátott entityt megjegyeznie. Sokkal fontosabb, hogy dinamikus szöveget alapértelmezetten autoescaping sablonba adjon, URL-t URL builderrel készítsen, és ne illesszen adatot közvetlenül script- vagy style-kódba.

A kézi replace('<', ...) jellegű megoldás rendszerint hiányos, rossz sorrendű vagy dupla kódolást okoz. A framework beépített, kontextusérzékeny funkciója követi a parser szabályait és code review során is felismerhető biztonsági mintát ad.

Az entity a kétértelműséget oldja fel

A HTML karakterhivatkozásának alapvető feladata egyszerű: olyan karaktert ír le tartalomként, amelyet a markup parser egyébként szerkezetként vagy hivatkozás kezdeteként értelmezne. Emellett kényelmes neveket ad néhány nehezen beírható szimbólumhoz.

Modern UTF-8 weben nem kell minden karaktert entityvé alakítani. A különleges karaktereket a megfelelő HTML-kontextusban, egyszer, a renderelés határán kell escape-elni. Ezzel megmarad az olvasható adat, elkerülhető a dupla kódolás, és a böngésző pontosan azt a szöveget építi a DOM-ba, amelyet a szerző szánt.