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.
