Validazione e sanitizzazione degli input in PHP

by theArchitect
SHARE
Validazione e sanitizzazione degli input in PHP
© Guida-HTML5.it

Introduzione

Quando si parla di sicurezza in PHP, molti sviluppatori pensano subito a SQL injection, XSS o gestione delle sessioni. In realtà, uno dei punti più delicati di tutta un’applicazione è spesso il più semplice: l’input dell’utente. Ogni dato che arriva da un form, da una query string, da un header HTTP o da un JSON body non deve mai essere considerato affidabile a priori.

Validare e sanitizzare correttamente gli input significa ridurre drasticamente il rischio di errori logici, vulnerabilità e comportamenti inattesi. È un tema pratico, trasversale e fondamentale, perché tocca quasi ogni pagina e ogni endpoint di un progetto PHP.

In questo tutorial vedremo come costruire un flusso robusto per gestire i dati in ingresso: controllare il formato, rifiutare valori non ammessi, normalizzare i dati e preparare informazioni sicure da usare nel resto dell’applicazione.

Codice completo

<?php
declare(strict_types=1);

/**
 * Esempio pratico:
 * - riceviamo dati da un form
 * - li validiamo
 * - li sanitizziamo dove serve
 * - restituiamo errori chiari
 */

function getPostValue(string $key): ?string
{
    $value = filter_input(INPUT_POST, $key, FILTER_UNSAFE_RAW);
    if ($value === null || $value === false) {
        return null;
    }

    return trim((string) $value);
}

function validateName(?string $name): array
{
    $errors = [];

    if ($name === null || $name === ´´) {
        $errors[] = ´Il nome è obbligatorio.´;
        return $errors;
    }

    if (mb_strlen($name) < 2) {
        $errors[] = ´Il nome deve contenere almeno 2 caratteri.´;
    }

    if (mb_strlen($name) > 50) {
        $errors[] = ´Il nome non può superare 50 caratteri.´;
    }

    if (!preg_match(´/^[pLs´-]+$/u´, $name)) {
        $errors[] = ´Il nome contiene caratteri non ammessi.´;
    }

    return $errors;
}

function validateEmail(?string $email): array
{
    $errors = [];

    if ($email === null || $email === ´´) {
        $errors[] = ´L’email è obbligatoria.´;
        return $errors;
    }

    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = ´L’email non è valida.´;
    }

    return $errors;
}

function validateAge(?string $age): array
{
    $errors = [];

    if ($age === null || $age === ´´) {
        $errors[] = ´L’età è obbligatoria.´;
        return $errors;
    }

    if (filter_var($age, FILTER_VALIDATE_INT, [
        ´options´ => [
            ´min_range´ => 1,
            ´max_range´ => 120,
        ]
    ]) === false) {
        $errors[] = ´L’età deve essere un intero compreso tra 1 e 120.´;
    }

    return $errors;
}

function sanitizeComment(?string $comment): string
{
    $comment = $comment ?? ´´;
    $comment = trim($comment);

    // Rimuove tag HTML per evitare contenuti non desiderati.
    // Non sostituisce l´escape in output, ma aiuta a mantenere il dato pulito.
    return strip_tags($comment);
}

$errors = [];

$name = getPostValue(´name´);
$email = getPostValue(´email´);
$age = getPostValue(´age´);
$comment = getPostValue(´comment´);

$errors = array_merge($errors, validateName($name));
$errors = array_merge($errors, validateEmail($email));
$errors = array_merge($errors, validateAge($age));

$cleanComment = sanitizeComment($comment);

if (!empty($errors)) {
    echo "<h1>Errore di validazione</h1>";
    echo "<ul>";
    foreach ($errors as $error) {
        echo ´<li>´ . htmlspecialchars($error, ENT_QUOTES | ENT_SUBSTITUTE, ´UTF-8´) . ´</li>´;
    }
    echo "</ul>";
    exit;
}

// Dati validi: possiamo usarli in modo più sicuro.
$cleanData = [
    ´name´ => $name,
    ´email´ => $email,
    ´age´ => (int) $age,
    ´comment´ => $cleanComment,
];

echo "<h1>Dati validati con successo</h1>";
echo ´<p>Nome: ´ . htmlspecialchars($cleanData[´name´], ENT_QUOTES | ENT_SUBSTITUTE, ´UTF-8´) . ´</p>´;
echo ´<p>Email: ´ . htmlspecialchars($cleanData[´email´], ENT_QUOTES | ENT_SUBSTITUTE, ´UTF-8´) . ´</p>´;
echo ´<p>Età: ´ . $cleanData[´age´] . ´</p>´;
echo ´<p>Commento: ´ . htmlspecialchars($cleanData[´comment´], ENT_QUOTES | ENT_SUBSTITUTE, ´UTF-8´) . ´</p>´;
?>

Spiegazione

Il punto centrale di questo esempio è distinguere bene tra validazione e sanitizzazione.

1. Validazione

La validazione controlla se un valore rispetta le regole attese. Per esempio:

  • un nome deve avere una lunghezza minima e massima;
  • un’email deve rispettare un formato plausibile;
  • un’età deve essere un numero intero in un intervallo sensato.

Se il dato non rispetta le regole, viene rifiutato. Questo è importante: non correggere automaticamente valori sbagliati quando la logica di business richiede precisione.

2. Sanitizzazione

La sanitizzazione serve a ripulire il dato da elementi non desiderati. Nell’esempio, il commento viene trattato con strip_tags() per rimuovere eventuali tag HTML. Questo può essere utile in alcuni contesti, ma non deve essere confuso con la protezione finale in output.

Un errore comune è pensare che sanitizzare basti sempre. In realtà, se mostri un dato in HTML, devi comunque usare htmlspecialchars() al momento dell’output. La regola è semplice:

  • validare per accettare solo valori corretti;
  • sanitizzare per ripulire quando ha senso;
  • escapare in output per evitare che i dati diventino codice HTML o JavaScript.

3. Uso di filter_input e filter_var

filter_input() è utile per leggere direttamente i dati da $_POST, $_GET e altre superglobali, con un approccio più ordinato rispetto all’accesso diretto. Nel nostro esempio usiamo FILTER_UNSAFE_RAW perché vogliamo leggere il valore senza alterarlo, per poi applicare le nostre regole.

filter_var(), invece, è molto comodo per validazioni standard come email, interi, URL e booleani. È una scelta pratica e leggibile.

4. Perché usare regex con attenzione

Nel controllo del nome abbiamo usato una regex che accetta lettere Unicode, spazi, apostrofi e trattini. Questo è più realistico rispetto a controlli troppo rigidi. Tuttavia, le regex vanno usate con criterio: se diventano troppo complesse, il codice diventa difficile da mantenere. Quando possibile, preferisci regole semplici e comprensibili.

5. Normalizzazione dei dati

La funzione trim() rimuove spazi inutili all’inizio e alla fine. È una forma di normalizzazione molto comune. Anche convertire un valore in intero con (int) dopo la validazione è una buona pratica, perché garantisce un tipo coerente nel resto del codice.

Best practice

  • Non fidarti mai dell’input utente, nemmeno se arriva da un form interno.
  • Valida il prima possibile, appena ricevi il dato.
  • Separa validazione e output: anche un dato valido va escapato quando viene stampato in HTML.
  • Usa regole chiare e specifiche per ogni campo, evitando controlli generici.
  • Preferisci whitelist invece di blacklist: definisci cosa è ammesso, non solo cosa è vietato.
  • Non usare sanitizzazioni “magiche” come sostituto della logica applicativa.
  • Centralizza le regole in funzioni o classi dedicate, così il progetto resta manutenibile.
  • Gestisci gli errori in modo esplicito, con messaggi chiari per l’utente e log utili per il debug.

Un’altra buona pratica è applicare la validazione anche lato backend quando usi form con validazione JavaScript. Il controllo client-side migliora l’esperienza utente, ma non protegge davvero l’applicazione, perché può essere aggirato facilmente.

Riepilogo

La sicurezza degli input in PHP è una delle basi più importanti per costruire applicazioni affidabili. In questo tutorial abbiamo visto come:

  • leggere i dati in modo ordinato;
  • validare nome, email ed età con regole concrete;
  • ripulire un commento da contenuti indesiderati;
  • mostrare gli errori in modo sicuro;
  • preparare dati coerenti da usare nel resto dell’applicazione.

Il messaggio chiave è semplice: i dati non diventano sicuri perché arrivano da un form ben fatto. Diventano gestibili solo quando li controlli, li normalizzi e li stampi nel modo corretto. Questo approccio riduce bug, migliora la qualità del codice e rende l’applicazione molto più robusta.

Approfondisci con risorse ufficiali

  • PHP Manual - filter_var(): documentazione ufficiale per la validazione dei dati.
  • PHP Manual - filter_input(): lettura sicura e strutturata degli input.
  • PHP Manual - htmlspecialchars(): escape corretto dei contenuti in output HTML.
  • PHP Manual - mb_strlen(): gestione corretta delle stringhe multibyte.
  • PHP Manual - strip_tags(): rimozione dei tag HTML da una stringa.

Se vuoi fare un passo in più, il prossimo argomento da studiare è la costruzione di un validator centralizzato, magari con classi dedicate o con un piccolo layer di form request, per rendere la validazione più riutilizzabile e facile da testare.

SHARE