modulo 18 di 24 / Ore 41-60

Moduli 17-24 / Checkpoint 60 ore

Input non fidati: cosa può arrivare dall'utente

Riconoscere cosa può arrivare di sbagliato dall'utente nel Registro del laboratorio, dove va validato e come distinguere sanitizzazione, validazione e normalizzazione.

25 minArchitettura del software

Input non fidati: cosa può arrivare dall’utente

Questa lezione appartiene al Modulo 18 — Sicurezza di base.

Perché l’input non è mai affidabile

Ogni volta che un utente compila un form, può inviare dati che il sistema non si aspetta. Non per malice necessariamente — per errore, per copia-incolla, per browser che si comportano in modo diverso.

Nel Registro del laboratorio, i campi che accettano input dell’utente sono:

  • Nome strumento
  • Categoria
  • Motivo del guasto (testo libero)
  • ID strumento (da form di ricerca o prestito)

Ognuno di questi può contenere qualcosa di inaspettato.

Cosa può arrivare — esempi concreti

Campo Input atteso Cosa può arrivare
Nome strumento "Trapano elettrico" "", " ", "<script>alert(1)</script>", stringa da 5000 caratteri
Categoria "Elettrico" "", valore non nella lista, "undefined" (se JS ha un bug)
Motivo guasto Frase breve Stringa vuota, testo di 10.000 caratteri, testo con nomi di persone
ID strumento "str-042" "", null, ID inesistente, caratteri speciali

Tre operazioni distinte sull’input

Spesso si confondono tre operazioni diverse. È utile distinguerle:

1. Normalizzazione — porta l’input in una forma standard, senza rifiutarlo:

function normalizzaTesto(testo) {
  if (typeof testo !== "string") return "";
  return testo.trim(); // rimuove spazi iniziali e finali
}

2. Validazione — verifica che l’input rispetti i vincoli, altrimenti lancia un errore:

function validaNome(nome) {
  if (nome.length < 2) throw new Error("NOME_TROPPO_CORTO");
  if (nome.length > 80) throw new Error("NOME_TROPPO_LUNGO");
}

3. Sanitizzazione — rimuove o neutralizza caratteri potenzialmente pericolosi:

// Nel contesto del Registro (solo localStorage, nessun server)
// XSS non è un rischio reale — ma è buona abitudine non salvare HTML grezzo
function sanitizzaTesto(testo) {
  return testo.replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

Nel Registro scolastico, la sanitizzazione contro XSS è a bassa priorità (localStorage non esegue script). La normalizzazione e la validazione sono invece indispensabili.

Dove va fatto ogni controllo

Presentation  →  normalizzazione (trim), feedback immediato al form
Application   →  validazione definitiva prima di passare al dominio
Domain        →  presuppone input già valido (non valida, usa)
Infrastructure →  nessun input raw — riceve solo dati già validati

La regola pratica: normalizza il prima possibile, valida prima di creare il comando, non lasciare che dati grezzi raggiungano il dominio.

Esempio completo — registrazione strumento

// presentation/formRegistraStrumento.js
document.getElementById("form-strumento").addEventListener("submit", async (e) => {
  e.preventDefault();

  // 1. Normalizzazione — prima di tutto
  const nome = normalizzaTesto(document.getElementById("nome").value);
  const categoria = normalizzaTesto(document.getElementById("categoria").value);

  // 2. Validazione rapida nella UI — feedback immediato
  if (nome.length < 2) {
    mostraErroreCampo("nome", "Il nome deve avere almeno 2 caratteri.");
    return;
  }
  if (!categoria) {
    mostraErroreCampo("categoria", "Seleziona una categoria.");
    return;
  }

  const comando = {
    id: crypto.randomUUID(),
    nome,      // già normalizzato
    categoria  // già normalizzato
  };

  try {
    // 3. Validazione definitiva nel caso d'uso
    await registraStrumento(comando);
    mostraSuccesso("Strumento registrato.");
  } catch (errore) {
    mostraMessaggio(traduciErrore(errore.message));
  }
});
// application/registraStrumento.js
function creaRegistraStrumento({ archivioStrumenti }) {
  return async function registraStrumento(comando) {
    // Validazione difensiva — anche se la UI ha già validato
    if (!comando.nome || comando.nome.length < 2) throw new Error("NOME_NON_VALIDO");
    if (!comando.categoria) throw new Error("CATEGORIA_MANCANTE");
    if (!comando.id) throw new Error("ID_MANCANTE");

    const strumento = creaStrumento(comando); // il dominio presuppone dati validi
    await archivioStrumenti.salva(strumento);
  };
}

Input da campo di ricerca

Il campo di ricerca è un caso particolare: non è obbligatorio e può contenere qualsiasi testo. La normalizzazione è sufficiente — la validazione sarebbe eccessiva.

// Ricerca: normalizza ma non rifiutare l'input vuoto
function normalizzaQuery(query) {
  if (typeof query !== "string") return "";
  return query.trim().toLowerCase();
}

// Se la query è vuota, restituisce tutti gli strumenti
function cercaStrumenti(archivio, query) {
  const q = normalizzaQuery(query);
  if (q.length === 0) return archivio.leggiTutti();
  return archivio.leggiTutti().filter(s =>
    s.nome.toLowerCase().includes(q) ||
    s.categoria.toLowerCase().includes(q)
  );
}

Procedura guidata

  1. Elenca tutti i form del Registro che accettano input dell’utente.
  2. Per ogni campo: quale normalizzazione serve? Quale validazione?
  3. Aggiungi normalizzaTesto ovunque si legge un input.value.
  4. Aggiungi validazione nel caso d’uso prima di usare il dato.
  5. Verifica che il dominio non riceva mai una stringa vuota o undefined nei campi obbligatori.

Attività in classe

## Input non fidati nel mio Registro

Form analizzato:

Campi con input utente:

Per ogni campo:
  - Normalizzazione applicata:
  - Validazione applicata:
  - Cosa succede se il campo è vuoto:

Input che raggiungono il dominio senza validazione (da correggere):

Spiegazione guidata per studiare

Ogni campo form è un punto di ingresso nel sistema. L’utente può inserire qualsiasi cosa — non perché sia malevolo, ma perché i browser, i copia-incolla e gli errori di digitazione producono input inaspettati.

Il punto centrale è: normalizza all’ingresso, valida prima di usare, non fidarti di nessun valore raw. Questa catena — normalizzazione → validazione → uso — protegge il dominio da dati malformati.

La differenza tra normalizzazione e validazione è importante: normalizzare trasforma (.trim()), validare rifiuta (lancia un errore). Un campo vuoto dopo .trim() viene rifiutato dalla validazione — ma la normalizzazione non lo rifiuta, solo lo pulisce.

Passo per passo nel progetto

  1. Apri tutti i form del Registro.
  2. Ogni input.value viene letto direttamente nel comando? Aggiungi .trim().
  3. Il caso d’uso ha almeno un controllo if (!campo || campo.length < N) per i campi obbligatori?
  4. Prova a inviare il form con tutti i campi vuoti — cosa succede?
  5. Aggiungi la validazione mancante e verifica di nuovo.

Esempio da leggere lentamente

// Senza protezione — tutto può arrivare al dominio
const comando = {
  nome: document.getElementById("nome").value,      // può essere ""
  categoria: document.getElementById("cat").value,  // può essere ""
};
creaStrumento(comando); // il dominio riceve "" come nome — produce dati corrotti

// Con protezione — tre passi in sequenza
const nomeCrudo = document.getElementById("nome").value;
const nome = normalizzaTesto(nomeCrudo);      // 1. normalizza
validaNome(nome);                              // 2. valida (lancia se non valido)
const comando = { id: crypto.randomUUID(), nome, categoria }; // 3. usa
creaStrumento(comando); // il dominio riceve dati già verificati

Prova di comprensione

  • Qual è la differenza tra normalizzazione e validazione? Fai un esempio per ciascuna.
  • Perché la validazione nella UI non è sufficiente da sola?
  • Cosa succede nel tuo progetto se l’utente invia il form con il nome vuoto?

Errori da evitare

  • usare input.value direttamente nel comando senza .trim();
  • fare la validazione solo nella UI — il caso d’uso deve difendersi da solo;
  • confondere normalizzazione e validazione (non rifiutare in normalizzazione);
  • validare nel dominio — il dominio deve presupporre dati già validi.

Prodotto da consegnare

Può essere:

  • una funzione normalizzaTesto usata in tutti i form;
  • una funzione validaComandoRegistraStrumento con almeno tre controlli;
  • un test che verifica che il caso d’uso lanci NOME_NON_VALIDO con input vuoto.

Checklist di chiusura

  • Ogni input.value viene normalizzato con .trim() prima dell’uso
  • Il caso d’uso valida i campi obbligatori prima di passarli al dominio
  • Il dominio non riceve mai stringhe vuote nei campi obbligatori
  • Il form mostra errori comprensibili per ogni campo non valido
  • Ho verificato cosa succede inviando il form con tutti i campi vuoti

Domande per la revisione

  • Hai trovato campi letti senza .trim()? Quanti?
  • Il dominio ha mai ricevuto una stringa vuota durante i tuoi test?
  • Qual è il campo più “pericoloso” del tuo Registro (quello più facile da lasciare vuoto per errore)?

Risultato atteso

Alla fine lo studente deve saper dire:

Nel mio Registro, i form che accettano input sono: [lista].
Per il campo [X] applico questa normalizzazione: [trim/lowercase/altro].
La validazione avviene in [UI / caso d'uso / entrambi].
Ho verificato il comportamento con input vuoto: [risultato].

Se questa spiegazione non è possibile, la lezione non è ancora davvero conclusa.