La SELECT proposta nella lezione precedente ci espone a potenziali rischi dal punto di vista dell'iniezione di codice malevolo, le sql injection infatti sono ancora al primo posto sia per la facilità di sfruttamento della vulnerabilità, sia per i rischi ad essa connessi; oltretutto, benché se ne parli da molto tempo, restano ancora diffuse.
Analizziamo il nostro codice:
$user = "pippo"; // Normalmente questi valori sarebbero ottenuti tramite POST
$password = hash('sha256', "paperino");
$query = $mysqli->query("SELECT * FROM login WHERE user = '$user' AND password = '$password'");
Il rischio è legato all'uso incauto della variabile $user
che verosimilmente sarà popolata da un $_POST['user']
e, usata senza controllo, potrebbe contenere qualsiasi codice malevolo come il classico ' or 1 â€"
.
La variabile $password
, venendo codificata dalla funzione hash()
, viene neutralizzata dal punto di vista della sicurezza.
A margine, bisogna ricordare che in alcuni casi è utile fare ricorso alle funzioni di sanificazione messe a disposizione a partire da PHP 5.2 in poi, nel caso di numeri o indirizzi email esse offrono già una buona garanzia.
La soluzione risiede nell'uso dei prepared statement che ci garantiscono un miglioramento della sicurezza e anche delle buone performance di esecuzione, in particolare nel caso di operazioni ripetute.
Generare la query con prepared statement
Il primo passo per utilizzare i prepared statement sarà quello di preparare la nostra query sostituendo le due variabili con dei segnaposto (placeholder) che verranno valorizzati in fase di binding:
$query = $mysqli->prepare("SELECT * FROM login WHERE user = ? AND password = ?");
La separazione fra la preparazione dell'SQL e i dati ci permette di avere una protezione rispetto alle SQL injection, infatti i dati provenienti dall'utente verranno gestiti al di fuori dell'istruzione SQL.
A livello di sintassi si deve registrare l'assenza dei delimitatori attorno al parametro: in questo caso pur trattandosi di stringhe non è necessario delimitarle, infatti l'associazione con il tipo di dato verrà fatta in fase di binding.
Associazione parametri-variabili
Il passaggio successivo sarà l'associazione tra il placeholder e la variabile:
$query->bind_param('ss',$user,$password);
Il metodo bind_param
accetta due valori: il primo è il tipo di dato e può avere i seguenti valori:
Valore | Descrizione |
---|---|
"s" | Corrisponde a variabili associate al tipo di dato stringa. |
"i" | Corrisponde a variabili associate al tipo di dato numeri interi. |
"d" | Corrisponde a variabili associate al tipo di dato numeri double. |
"b" | Corrisponde a variabili associate al tipo di dato BLOB, formato binario. |
Cosa succede se il valore della variabile non corrisponde con il tipo di dato dichiarato? Viene ripulita. Quindi se la mia variabile avesse come bind il tipo "i", numero intero, e il contenuto della variabile fosse "test", il valore inserito sarebbe 0, compatibile con quanto atteso in fase di dichiarazione.
Il secondo valore è invece la variabile contenente il dato da utilizzare nella query. Attenzione: non possono essere utilizzati valori statici. Le variabili possono essere valorizzate anche dopo il bind_param
, a patto ovviamente che siano valorizzate prima dell'esecuzione della query.
Un'altra cosa importante da osservare è che mysqli non supporta i nomi di binding ma solo il binding posizionale, quindi bisogna prestare particolare attenzione all'ordine dei parametri. Nel nostro caso la variabile $user
sarà associata al primo parametro della query. Questo limite deve essere considerato come una delle più gravi mancanze di MySQLi.
Eseguire la query
Il terzo ed ultimo passaggio è l'esecuzione vera e propria della query, quindi nel nostro caso:
$result = $query->execute();
A questo punto possiamo valutare il contenuto della variabile $result
per sapere se l'operazione di autenticazione è andata a buon fine o meno, potremo quindi procedere con le operazioni di update
e delete
dei record.