Grazie alla funzione sleep() mysql mette involontariamente a disposizione dei malintenzionati una particolare tecnica di Sql Injection con la quale cercare di bloccare un intero sito.. Meglio esser previdenti.
La funzione sleep() di mysql è poco conosciuta perché si occupa di bloccare l'esecuzione di una query per alcuni secondi, quelli passati come parametro. Si tratta quindi di una funzione usata più che altro in ambienti avanzati, come nelle stored procedure. Non va confuso con lo stato "sleep" che troviamo quando estraiamo l'elenco dei processi attivi in un database.
Tuttavia è una funzione che faremmo bene a considerare a prescindere dal suo utilizzo, perché fa gola ai malintenzionati che cercano di attaccare i nostri siti. Questa funzione può essere micidiale quando associata ad una select:
select * from comuni where province_id=<id provincia>
Ipotizzando che il parametro "province_id" arrivi dalla url, la query diventerebbe questa:
$province_id = isset($_GET['province_id'])?$_GET['province_id']:0;
$sql = 'select * from comuni where province_id='.$province_id;
Fin qui tutto bene. Ad esempio la provincia Varese (nel mio db, id = 102) restituirebbe un elenco di 139 comuni.
Ma cosa accadrebbe in una query di questo tipo?
select * from comuni where province_id=102 && SLEEP(1)
La funzione sleep(1 secondo) verrebbe richiamata per ogni record restituito dalla query, che terminerebbe il proprio risultato dopo 139 secondi. Immaginate se al posto di 1 secondo il malintenzionato passasse valori piu elevati (20 secondi significherebbe 2780 secondi, oltre tre quarti d'ora!) e lo facesse richiamando la pagina più volte... ecco che un attacco DOS artigianale ma potenzialmente molto efficace è servito. Basterebbe aggiungere alla url questa stringa..
pagina.php?province_id=102%20%26%26%20SLEEP%281%29
La soluzione è quella di usare apposite librerie (come Pdo) che includono già una protezione contro questi attacchi; se invece abbiamo a che fare con un codice obsoleto possiamo applicare un paio di interventi rapidi:
- se il parametro è di tipo intero, basta inserire un casting del parametro:
$province_id = isset($_GET['province_id'])?$_GET['province_id']:0;
$province_id = (int)$province_id;
- usare quantomeno le funzioni native di php:
$province_id = isset($_GET['province_id'])?$_GET['province_id']:0;
$sql = sprintf("select * from comuni where province_id='%s'",mysql_real_escape_string($province_id));