In breve: per la sicurezza delle API in PHP contano soprattutto cinque punti: solo HTTPS, autenticazione a ogni endpoint non pubblico, verifica dell'autorizzazione per ogni singolo oggetto, limiti rigorosi per metodo, tipo di contenuto e dimensione della richiesta, nonché messaggi di errore brevi senza dettagli interni. A ciò si aggiungono la limitazione della frequenza e un registro degli eventi rilevanti per la sicurezza. Contro le vulnerabilità nell'interprete PHP stesso come PHP2Shell non serve; per questo occorre l'aggiornamento di PHP.
L'interfaccia dietro il sito
Molte aziende nella settimana dopo il 21/10/2026 hanno verificato i propri siti. Le interfacce dietro di essi erano più di rado sull'elenco. Eppure è proprio lì che passano operazioni sensibili: l'app del portale clienti ritira le fatture, il gestionale scrive le scorte nello shop, un partner comunica gli ordini via webhook. Spesso queste API sono state costruite in fretta, per un unico scopo, e poi mai più riviste.
Un'API non ha un'interfaccia che renda visibili gli errori. Un controllo delle autorizzazioni mancante non dà nell'occhio a nessuno nella vita quotidiana, finché tutti i client richiedono solo ciò che devono. Un aggressore prova invece proprio ciò che non è previsto.
Il primo passo è quindi un inventario. Quali endpoint esistono, in fondo? Si trovano spesso vecchie versioni come /api/v1/, mai disattivate dopo il passaggio a /v2/, endpoint di test dello sviluppo o una descrizione dell'interfaccia richiamabile pubblicamente che elenca funzioni interne. Ogni endpoint non più necessario andrebbe disattivato. Ogni endpoint che resta richiede un responsabile.
Sicurezza delle API in PHP: le regole di base
Il REST Security Cheat Sheet di OWASP riassume regole collaudate. Per i progetti PHP si possono tradurre in un breve elenco:
- Solo HTTPS. Token e credenziali non devono mai essere trasmessi senza cifratura.
- Ogni endpoint verifica da sé. L'autenticazione può avvenire in modo centralizzato, la decisione sull'accesso si prende all'endpoint.
- Autorizzazione per oggetto. Non solo «è connesso», ma «può accedere proprio a questo record».
- Definire i metodi consentiti. Ciò che non è espressamente consentito viene rifiutato con 405.
- Verificare tipo di contenuto e dimensione. Tipo errato dà 415, richieste troppo grandi 413.
- Validare gli input. Tipo, lunghezza, formato e intervallo di valori, lato server.
- Nessun segreto negli URL. Token e chiavi appartengono agli header, non alla query string.
- Errori brevi. Nessuno stack trace, percorso o messaggio SQL al client.
- Limitazione della frequenza. Troppe richieste ricevono come risposta 429.
- Schermare gli endpoint di amministrazione. Non raggiungibili pubblicamente o solo con un accesso forte.
Autorizzazione per oggetto: l'errore più frequente
Un esempio rende tangibile il problema. Un endpoint fornisce fatture sotto /api/rechnungen/{id}. Si verifica se è presente un token valido. Non si verifica se la fattura appartiene al cliente del token. Chi aumenta di uno il numero della propria fattura vede quella del vicino.
La soluzione è semplice e deve comunque essere attuata in ogni punto: la query stessa limita all'ambito autorizzato.
<?php
// Consentire solo il metodo GET
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
http_response_code(405);
header('Allow: GET');
exit;
}
$kundeId = $auth->kundeId(); // dal token verificato
$id = filter_var($routeId, FILTER_VALIDATE_INT);
if ($id === false) {
http_response_code(400);
exit;
}
// L'autorizzazione è nella query: solo fatture proprie
$stmt = $pdo->prepare('SELECT nummer, betrag, datum FROM rechnungen WHERE id = ? AND kunde_id = ?');
$stmt->execute([$id, $kundeId]);
$rechnung = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$rechnung) {
http_response_code(404); // non rivelare se l'ID esiste
exit;
}
header('Content-Type: application/json');
echo json_encode($rechnung, JSON_THROW_ON_ERROR);
La query con segnaposto protegge al tempo stesso dalla SQL injection, vedi Prevenire la SQL injection in PHP. Come PHP metta a disposizione dati della richiesta come metodo, header e body lo spiega Superglobali: $_GET, $_POST, $_FILES e simili.
Verificare correttamente i token
Molte API lavorano con JSON Web Token. Il Cheat Sheet OWASP indica regole chiare: i token devono essere firmati, i token non firmati vengono rifiutati e l'algoritmo per la verifica viene stabilito sul server, non ripreso dal token. Anche emittente, destinatari e scadenza fanno parte della verifica. Usi a tal fine una libreria mantenuta anziché implementazioni proprie.
I token propri, ad esempio per webhook o link monouso, devono provenire da un generatore di numeri casuali crittograficamente sicuro ed essere confrontati in tempo costante. Come si fa in PHP lo mostra Numeri casuali e token sicuri in PHP.
Dia a ogni client e a ogni partner una chiave propria con esattamente i diritti che gli servono. Allora una chiave sottratta si può bloccare singolarmente, senza interrompere tutti gli altri collegamenti.
Porre limiti: dimensione, tipo e ritmo
Un'API dovrebbe accettare solo ciò che si aspetta. Si comincia dal tipo di contenuto: un endpoint che elabora JSON rifiuta tutto il resto. La dimensione massima di una richiesta la si limita su più livelli, nel server web e in PHP tramite post_max_size. Questo limite fa parte anche delle misure transitorie che php.net ha indicato per PHP2Shell. Come sceglierlo in modo sensato lo descrive Limitare in modo sensato post_max_size e upload_max_filesize.
La limitazione della frequenza protegge dal provare a tentativi gli identificativi e dal sovraccarico. Si può attuare nel server web, in un proxy a monte o nell'applicazione. È importante che i limiti valgano per chiave o utente e non solo per indirizzo IP.
Per le API richiamate direttamente dal browser si aggiunge CORS, cioè le regole su quali siti esterni possano inviare richieste. Imposti questi header solo se tali chiamate sono davvero previste e indichi le origini consentite singolarmente, anziché ammettere ogni origine. Un carattere jolly che consente tutti i siti è quasi sempre un errore negli endpoint con autenticazione.
Infine il registro: login falliti, token rifiutati e accessi negati appartengono al log, i token stessi no. Senza queste voci un attacco all'API resta invisibile.
Come aiuta concretamente PHP SOS?
- Rileviamo tutte le interfacce dei suoi sistemi PHP, anche dimenticate e interne, e verifichiamo quali siano raggiungibili pubblicamente.
- Leggiamo la logica delle autorizzazioni e testiamo in modo mirato se i record altrui siano richiamabili.
- Impostiamo limiti di dimensione, tipo e ritmo in server web e PHP e li concordiamo con i suoi client.
- Per una verifica completa dall'esterno lo combiniamo con audit di sicurezza e penetration test.
Conclusione: un'API è sicura quanto il suo endpoint più trascurato. Chi verifica le autorizzazioni per oggetto, pone limiti chiari e tiene brevi gli errori chiude le lacune più frequenti molto prima che qualcuno le cerchi in modo mirato.
Domande frequenti
Una chiave API basta come protezione?
Per dati sensibili no. Una chiave API identifica di solito un'applicazione, non un utente, e viene passata facilmente ad altri. Il REST Security Cheat Sheet di OWASP consiglia di non usare le chiavi API come unica protezione per risorse sensibili.
Che cos'è l'autorizzazione per oggetto e perché manca così spesso?
Si intende la verifica se l'utente connesso possa vedere o modificare proprio questo record. Manca spesso perché gli sviluppatori controllano solo se ci sia qualcuno connesso. Allora basta cambiare un identificativo nell'URL per richiamare dati altrui.
Un'API ben protetta protegge da PHP2Shell?
No. PHP2Shell si trova nell'interprete PHP e agisce nelle modalità di esecuzione interessate prima ancora che giri il suo codice. Contro di esso aiuta solo un aggiornamento di PHP con riavvio. I limiti di dimensione per le richieste restano comunque sensati e fanno parte anche delle misure transitorie di php.net.
Quali codici di stato dovrebbe fornire un'API in caso di errori di sicurezza?
Sono usuali 401 senza accesso valido, 403 per autorizzazione mancante, 405 per metodo non consentito, 413 per richiesta troppo grande, 415 per tipo di contenuto errato e 429 per troppe richieste. La risposta stessa resta breve e senza dettagli interni.