HTL Tutorial #4: Context-Aware XSS Protection

HTL Tutorial #4: Context-Aware XSS Protection

Cos'è XSS?

Cross-Site Scripting (XSS) è una vulnerabilità che permette agli attaccanti di iniettare codice JavaScript malevolo nelle pagine web.

Esempio di attacco XSS:

// Un utente inserisce questo in un campo "nome":
<script>alert('Hacked!');</script>

// Senza protezione, viene eseguito:
<p>Ciao <script>alert('Hacked!');</script></p>PERICOLOSO!

La Soluzione di HTL

HTL protegge automaticamente da XSS analizzando il contesto dove l'output viene inserito e applicando l'escaping appropriato.

Escaping Automatico

<!-- Input utente malevolo -->
${properties.userInput}  <!-- contiene: <script>alert('XSS')</script> -->

<!-- HTL output sicuro: -->
&lt;script&gt;alert('XSS')&lt;/script&gt;

Il codice viene visualizzato come testo, non eseguito!

I 17 Context di HTL

HTL riconosce automaticamente 17 diversi contesti e applica l'escaping corretto per ognuno.

1. context='html' (DEFAULT)

Contesto: Contenuto HTML normale Comportamento: Rimuove markup potenzialmente pericoloso

<!-- Automatico nel contenuto HTML -->
<div>${properties.content}</div>

<!-- Esplicito -->
<div>${properties.content @ context='html'}</div>

Esempio:

<!-- Input -->
${properties.text}  <!-- "Hello <b>World</b> <script>alert('xss')</script>" -->

<!-- Output -->
Hello <b>World</b>  <!-- <script> rimosso! -->

2. context='text'

Contesto: Testo puro (codifica TUTTO l'HTML) Uso: Quando vuoi mostrare HTML come testo

<pre>${properties.codeSnippet @ context='text'}</pre>

Esempio:

<!-- Input -->
${'<div>HTML Code</div>' @ context='text'}

<!-- Output (visibile come testo) -->
&lt;div&gt;HTML Code&lt;/div&gt;

3. context='attribute'

Contesto: Valori di attributi HTML generici

<div title="${properties.tooltip @ context='attribute'}">
  Hover me
</div>

4. context='uri'

Contesto: URL/URI (href, src, action) Comportamento: Blocca javascript:, data:, vbscript: pericolosi

<!-- Sicuro -->
<a href="${properties.link @ context='uri'}">Link</a>

<!-- Se properties.link = "javascript:alert('XSS')" -->
<!-- HTL blocca e output: "#" -->

Esempio pratico:

<!-- Link esterni -->
<a href="${properties.externalLink @ context='uri'}" target="_blank">
  Visita il sito
</a>

<!-- Download -->
<a href="${properties.downloadUrl @ context='uri'}" download>
  Scarica PDF
</a>

<!-- Immagini -->
<img src="${properties.imageUrl @ context='uri'}" alt="${properties.alt}">

5. context='elementName'

Contesto: Nome tag HTML dinamico Comportamento: Whitelist di tag permessi

<${properties.tagName @ context='elementName'}>
  Contenuto
</${properties.tagName @ context='elementName'}>

Esempio:

<!-- properties.tagName = "article" -->
<article>Contenuto</article><!-- properties.tagName = "script" -->
<!-- Bloccato! Output: <div> come fallback -->

6. context='attributeName'

Contesto: Nome attributo dinamico

<div ${properties.attrName @ context='attributeName'}="value">
  Content
</div>

7-10. JavaScript Contexts

context='scriptToken'

JavaScript token (variabili, funzioni):

<script>
  var ${properties.varName @ context='scriptToken'} = 'value';
</script>

context='scriptString'

Stringhe JavaScript:

<script>
  var message = '${properties.message @ context='scriptString'}';
</script>

context='scriptComment'

Commenti JavaScript:

<script>
  /* ${properties.comment @ context='scriptComment'} */
</script>

context='scriptRegExp'

Regular expressions:

<script>
  var pattern = /${properties.pattern @ context='scriptRegExp'}/g;
</script>

11-13. CSS Contexts

context='styleToken'

Token CSS (proprietà, valori):

<div style="color: ${properties.color @ context='styleToken'}">
  Text
</div>

context='styleString'

Stringhe CSS:

<style>
  .class::before {
    content: '${properties.content @ context='styleString'}';
  }
</style>

context='styleComment'

Commenti CSS:

<style>
  /* ${properties.comment @ context='styleComment'} */
</style>

14. context='comment'

Contesto: Commenti HTML

<!-- ${properties.comment @ context='comment'} -->

15. context='number'

Contesto: Solo valori numerici

<input type="number" value="${properties.age @ context='number'}">

Esempio:

<!-- Input: "25abc" -->
${properties.value @ context='number'}
<!-- Output: "25" (solo numero) -->

<!-- Input: "not a number" -->
${properties.value @ context='number'}
<!-- Output: "0" (fallback) -->

16. context='unsafe' ⚠️

ATTENZIONE: Disabilita TUTTA la protezione XSS!

Usa SOLO se:

  • Il contenuto proviene da fonte assolutamente fidata
  • Il contenuto è già stato sanitizzato
  • Sai esattamente cosa stai facendo
<!-- ⚠️ PERICOLOSO se source non è sicura! -->
<div>${properties.richText @ context='unsafe'}</div>

Alternativa sicura: Usa context='html' che rimuove script malevoli ma mantiene markup sicuro.

Rilevamento Automatico del Context

HTL rileva automaticamente il context basandosi sulla posizione:

<!-- Context HTML (auto) -->
<p>${properties.text}</p>

<!-- Context ATTRIBUTE (auto) -->
<div class="${properties.cssClass}"></div>

<!-- Context URI (auto) -->
<a href="${properties.url}">Link</a>
<img src="${properties.image}">

<!-- Context TEXT nei text node -->
${properties.plainText}

Quando Specificare il Context Manualmente

1. JavaScript Inline

<script>
  // DEVI specificare il context!
  var data = '${properties.data @ context='scriptString'}';
</script>

2. CSS Inline

<style>
  .custom {
    color: ${properties.color @ context='styleToken'};
  }
</style>

3. Attributi Dinamici

<div ${properties.attrName @ context='attributeName'}="${properties.attrValue @ context='attribute'}">
</div>

Esempio Completo: User Profile Card

<div class="profile-card" data-sly-use.user="com.example.UserModel">
  <!-- Text content - auto HTML context -->
  <h2>${user.name}</h2>

  <!-- Bio (può contenere <b>, <i> etc) - HTML context permette markup sicuro -->
  <p>${user.bio @ context='html'}</p>

  <!-- Avatar image - auto URI context -->
  <img src="${user.avatarUrl}" alt="${user.name}">

  <!-- Social link - URI context esplicito per sicurezza -->
  <a href="${user.website @ context='uri'}" target="_blank">
    Website
  </a>

  <!-- Attributo data dinamico -->
  <div data-user-id="${user.id @ context='attribute'}">
    <!-- JavaScript context -->
    <script>
      var userName = '${user.name @ context='scriptString'}';
      var userId = ${user.id @ context='scriptToken'};
      console.log('User: ' + userName);
    </script>
  </div>

  <!-- Badge con HTML sicuro ma limitato -->
  <span class="badge">${user.badgeHtml @ context='html'}</span>
</div>

Best Practice

  1. Lascia che HTL scelga automaticamente quando possibile
  2. Specifica sempre il context in <script> e <style>
  3. Mai usare context='unsafe' su input utente
  4. Usa context='html' invece di unsafe per rich text
  5. URI context blocca automaticamente link pericolosi
  6. Number context per input numerici - previene injection
  7. Test i tuoi context con input malevoli durante lo sviluppo

Tabella Riepilogativa

Context Uso Esempi
html Contenuto HTML (default) <div>${text}</div>
text Testo puro encoded <pre>${code}</pre>
attribute Attributi generici <div title="${tip}">
uri URL/Link <a href="${link}">
elementName Tag dinamici <${tag}>content</${tag}>
attributeName Attributi dinamici <div ${attr}="val">
scriptToken JS tokens var ${name} = value;
scriptString Stringhe JS var s = '${text}';
styleToken CSS token color: ${color}
number Solo numeri value="${age}"
unsafe NO escaping ⚠️ Evita se possibile!

Test di Sicurezza

Prova questi input malevoli nei tuoi componenti:

<script>alert('XSS')</script>
javascript:alert('XSS')
<img src=x onerror="alert('XSS')">
" onload="alert('XSS')
');alert('XSS');//

HTL dovrebbe bloccarli automaticamente!

Prossima Lezione

Nella prossima lezione inizieremo con i block statements, partendo da data-sly-text per l'output di contenuto.


Lezione #4 della serie HTL Tutorial. ← Lezione precedente | Lezione successiva →