Introduzione alla sicurezza in PHP

Introduzione alla sicurezza in PHP

Per poter usare al meglio PHP sul proprio sito web, occorre tenere a mente alcune semplici regole di sicurezza relative al modo con cui scriviamo il nostro codice e gestiamo il software sui nostri siti.

Diffidare dell'input utente

Quando creiamo un form o interpretiamo le variabili di una query string in un URL, dobbiamo sempre tenere presente che i dati inviati possono essere manipolati in modo arbitrario per cercare di sfruttare eventuali falle di sicurezza nel nostro codice.

Solitamente chi manipola questi dati lo fa inizialmente per verificare come queste eccezioni nell'input vengono gestite da noi.

Se ad esempio non abbiamo implementato correttamente la gestione degli errori e delle eccezioni, alla persona che sta effettuando questi test basterà semplicemente verificare che il web server restituisca un errore HTTP 500.

Questo errore è ciò che viene restituito per impostazione predefinita da PHP, in quanto in ambiente di produzione la visualizzazione degli errori viene disabilitata e tali errori vengono registrati nei file di log consultabili dal pannello di controllo.

Lo scenario appena descritto si può considerare puramente ideale, in quanto molto spesso queste verifiche malevole vengono effettuate da strumenti di analisi automatici. Tali strumenti attingono ad un vastissimo database di vulnerabilità note e richiedono una minima interazione da parte dell'utente. Una volta inserito l'indirizzo del nostro sito, questi tool procedono sistematicamente a scandagliare le nostre pagine testando la presenza delle già citate vulnerabilità.

Quando viene trovata una vulnerabilità, il tool fornisce anche all'utente un link dove può visualizzarne i dettagli e spesso scaricare il codice da usare per sfruttarla. Da qui in poi le azioni di un utente malintenzionato possono essere facilmente intuibili.

Fortunatamente per noi, esistono delle contromisure che possiamo adottare per bloccare questo tipo di attacchi al codice PHP. Vediamole insieme.

Filtrare l'input utente

Un filtro si basa su una serie di regole che solo se soddisfatte permettono all'input in entrata di passare al livello successivo, ossia alla sua elaborazione.

Supponiamo di avere un form di contatto. Dobbiamo essere certi che l'email fornita sia valida. Possiamo usare la funzione di PHP filter_var() in questo modo:

$email = trim($_POST['email']);
$errors = [];

if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
    $errors[] = 'Indirizzo email non valido.';
}

I filtri possono e devono essere implementati non solo sulle stringhe, ma più in generale su tutti i tipi di dati gestiti da PHP.

Supponiamo di voler permettere il download di documenti PDF. Dobbiamo essere sicuri che un malintenzionato non possa scaricare altri tipi di file. Dobbiamo quindi verificare che un file esista in una determinata directory e che sia effettivamente un file PDF.

$filename = trim($_GET['f']);
if(!ctype_alnum($filename)) {
    exit;
}
if(strlen($filename) > 21) {
    exit;
}
$download_dir = $_SERVER['DOCUMENT_ROOT'] . '/downloads/';
$file = $download_dir . $filename;
if(!file_exists($file)) {
    exit;
}
if(mime_content_type($file) !== 'application/pdf') {
    exit;
}
// Qui possiamo implementare la logica del download



Il nostro filtro consiste in una serie di regole implementate in sequenza. La prima verifica che il nome del file sia composto solo da caratteri alfanumerici, la seconda che il nome del file sia di una lunghezza determinata, la terza che il file esista e la quarta che il tipo MIME del file richiesto sia conforme al formato PDF. Non abbiamo usato l'estensione del file, in quanto questo tipo di verifica è assolutamente inaffidabile nonché causa di svariati problemi dovuti alla presenza di un eventuale doppia estensione.

Sanificare l'input utente

La sanificazione dell'input utente si ha quando accettiamo l'input utente ma vogliamo comunque eliminare qualsiasi tipo di carattere che potrebbee rivelarsi pericoloso.

Un esempio tipico in PHP è la prevenzione della SQL injection con MySQL. In questo caso possiamo sia usare i prepared statements che i metodi delle classi per eliminare dalle stringhe quei caratteri che hanno un significato speciale per MySQL.

// Sanificazione tramite metodo

$mysqli = new mysqli("localhost", "username", "password", "database");
$name = trim($_POST['name']);
$query = sprintf("SELECT code FROM regions WHERE name='%s'",
    $mysqli->real_escape_string($name));
$result = $mysqli->query($query);


// Sanificazione tramite prepared statements


$dsn = 'mysql:dbname=database;host=localhost';
$user = 'username';
$password = 'password';

$dbh = new PDO($dsn, $user, $password);
$name = trim($_POST['name']);
$query = 'SELECT code FROM regions WHERE name = :name';
$sth = $dbh->prepare($query, [PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY]);
$sth->execute([':name' => $name]);
$result = $sth->fetch();

Un altro esempio, molto più complesso, è quello relativo alla prevenzione degli attacchi XSS. Nonostante in molti credano che per prevenire questo tipo di attacchi sia necessario solo far ricorso ad alcune funzioni PHP come strip_tags() o htmlspecialchars() , in realtà questo è il caso in cui dobbiamo rivolgerci alla comunità PHP e adottare una soluzione che ci eviti non solo di reinventare la ruota ma anche di esporci a potenziali rischi.

HTML Purifier è una soluzione conforme agli standard per filtrare l'input utente ed evitare che venga generato codice HTML dannoso. Questa libreria prende in considerazione anche il codice JavaScript e CSS che potrebbe contenere stringhe pericolose e lo filtra prima che questo venga inviato sull'output di una pagina.

Aggiornare il software in uso

Nel caso in cui stiamo usando un CMS come WordPress o un framework come Laravel per gestire il nostro sito web, dovremmo sempre avere la premura di aggiornare il nostro software non appena viene rilasciato un nuovo aggiornamento. Se poi questo aggiornamento contiene delle correzioni di sicurezza, l'aggiornamento è obbligatorio.

Per farlo senza problemi, possiamo clonare il nostro dominio, il database e i file su un sottodominio che proteggeremo utilizzando un file .htpasswd.

Se l'aggiornamento ha avuto successo e non sono stati riscontrati problemi o malfunzionamenti, possiamo cancellare il nostro sottodominio copia e ripetere la procedura di aggiornamento sul sito principale.

Aggiornare la versione di PHP

Per aggiornare PHP nel modo migliore, dobbiamo verificare sul sito ufficiale di PHP quali novità e cambiamenti sono stati introdotti con la release che vogliamo attivare.

Se abbiamo sviluppato il nostro sito da zero, sarà sufficiente leggere le Release Notes e verificare che il nostro codice sia conforme.

Se invece stiamo utilizzando un CMS come WordPress, la parte più complessa non riguarda il core, ma i moduli, temi e plugin installati, che non sempre sono conformi con l'ultima release di PHP. Quello che possiamo fare se non siamo sicuri, è attendere il prossimo aggiornamento di questi componenti e verificare sempre sui siti degli sviluppatori se ci sono problemi noti con la versione di PHP che vogliamo attivare.

Conclusione

Usando alcune precauzioni e un minimo di prudenza, possiamo usare senza alcun problema PHP per sviluppare e gestire i nostri siti web.

Torna su