il PHP è un potente linguaggio ed interprete,può essere incluso come un modulo del server web o come un CGI eseguibile separato. PHP può accedere ai files, eseguire comandi di shell e aprire connessioni di rete. Queste proprietà rendono qualsiasi cosa venga eseguito su un web server, non sicuro per default.
PHP, d'altro canto, è stato progettato specificatamente per essere un linguaggio per applicazioni web, più sicuro del PERL o del C e con la corretta configurazione di compilazione e di run-time, e la giusta maniera di scrivere il codice degli script, può fornire il giusto compromesso tra libertà e sicurezza, di cui necessitiamo.
Ci sono differenti modi di utilizzare il PHP e molte opzioni di configurazione per controllarne l'utilizzo. Un vasto numero di opzioni ne fanno un linguaggio versatile ma questo, in combinazione con le opzioni del server web, possono dare come risultato un configurazione poco sicura.
La flessibilità della configurazione rivaleggia con la flessibilità del codice.
Il PHP può essere usato per implementare una applicazione server completa che includa l'utilizzo e la potenza dei comandi di shell, così come puo essere usato solo per includere automaticamente pagine in un sito con poco o nessun rischio in un ambiente controllato. Come configurare questo ambiente, e quanto esso sia sicuro, dipende in gran parte dallo sviluppatore PHP.
Un sistema sicuro completamente è virtualmente impossibile da realizzare, così spesso l'approccio del professionista è quello di bilanciare il rischio con la fruibilità del sistema.
Il giusto compromesso nella sicurezza, dovrebbe consentire di soddisfare le esigenze dell'utente senza sovraccaricarlo di lavoro e allo stesso tempo non oberare lo sviluppatore con una eccessiva complessità del codice. Spesso gli attacchi che si subiscono tendono semplicemente ad indurre gli sviluppatori a sovraccaricare il sistema con sistemi di sicurezza e a far consumare risorse.
Occorre ricordare sempre una frase: "un sistema è sicuro quanto lo è il suo componente più debole".
Se noi registriamo,attentamente e pesantemente, tutte le transazioni in base ad ora, data, provenienza, attività, ma poi autentichiamo l'utente attraverso il semplice uso di un cookie, la validità dell'attribuire ad un utente una transazione è fortemente indebolita.
Quando testate un'applicazione, considerate che non sarete capaci di verificare tutte le possibilità anche in una pagina molto semplice. Gli input che voi vi aspettate sono completamente slegati da quelli che può inserire un impiegato annoiato o un cracker con mesi di tempo da perdere, o un gatto domestico che passeggia sulla tastiera del computer.
Questo è il motivo per cui è meglio guardare al codice da una prospettiva logica, comprendere da dove possono essere inseriti dati non previsti e quindi verificare come possono essere modificati, ridotti o ampliati.
Internet è piena di "personaggi" che per farsi un nome cercano le falle del vostro codice, fanno cadere il vostro sito, postano contenuti non appropriati e vi rendono la giornata interessante con simili piacevolezze.
Non gli interessa se voi avete un piccolo od un grande sito, voi siete un obiettivo per il semplice fatto di essere on-line, perché avete un server che può essere connesso.
Molti programmi di cracking non controllano le dimensioni, semplicemente dragano blocchi massivi di indirizzi IP cercando vittime. Cercate di non diventare una di loro.
Possibili attacchi
Usare il PHP come un CGI binario è un opzione per gli ambienti che per qualche motivo non vogliono integrare il PHP come un modulo nel software del server oppure vogliono usare il PHP con differenti specie di contenitori CGI per creare delle chroot sicure ed un ambiente controllato per gli script.
Questo tipo di configurazione comporta l'installazione di un binario eseguibile PHP nella directory cgi-bin del server web. Gli organismi preposti raccomandano di non porre nessun interprete nella directory cgi-bin.
Anche se il binario PHP può essere usato come un interprete stand-alone, PHP è progettato per prevenire gli attacchi che questa configurazione rende possibili:
La query nell' url dopo il punto interrogativo (?) è passata come l'argomento di un comando di shell per essere interpretato dalla interfaccia CGI.
Di solito gli interpreti aprono ed eseguono il file specificato come il primo argomento del comando di shell.
Quando il PHP è eseguito come un binario CGI si rifiuta di interpretare gli argomenti per i comandi di shell.
la parte informativa del path dopo il nome del binario php, /secret/doc.html convenzionalmente è usata per specificare il nome del file che deve essere aperto ed interpretato dal programma CGI. Di solito le direttive di configurazione di alcuni web server (Apache:Action) ridirigono le richieste di un documento come http://my.host/secret/script.php all'interprete PHP.
Con questa configurazione, il server web, prima controlla http://my.host/cgi-bin/php/secret/script.php i permessi di accesso alla directory /secret, dopo crea il redirect http://my.host/cgi-bin/php/secret/script.php.
Sfortunatamente, se la richiesta originale è data in questa forma, non viene effettuato nessun controllo di accesso da parte del web server sul file /secret/script.php, ma solo per il file/cgi-bin/php. In questo modo ogni utente che può accedere /cgi-bin/php accede anche ad ogni documento protetto sul sever web.
Per prevenire questo tipo di attacco occorre predisporre in fase di compilazione le direttive di configurazione --enable-force-cgi-redirect e nella configurazione runtime vanno usate le direttive doc_root e user_dir, se l'albero delle directory del server ha delle directory con restrizioni di accesso.
Vediamo ora un po' di esempi:
Caso 1 solo file pubblici
Se il vostro server non ha alcun contenuto il cui accesso sia controllato tramite password o verifica dell'indirizzo IP, non c'è necessità di questa opzione di configurazione.
Se il vostro server non consente il redirect, o il server non ha un modo di comunicare al binario PHP che la richiesta è una richiesta sicura ridiretta, voi potete specificare l'opzione --enable-force-cgi-redirect allo script di configurazione della compilazione del PHP.
Dovete però essere sicuri che i vostri script PHP non utilizzino ne l'uno ne l'altro modo di chiamare gli script, ne direttamente http://my.host/cgi-bin/php/dir/script.php ne per mezzo della redirezione http://my.host/dir/script.php.
La redirezione può essere configurata in Apache usando la direttiva AddHandler e Action
Caso 2 usare --enable-force-cgi-redirect
Questa opzione di compilazione previene che qualcuno possa chiamare il PHP direttamente con una url del tipo http://my.host/cgi-bin/php/secretdir/script.php
Invece il PHP interpreterà la url solo se è stato chiamato attraverso un redirect del server.
La configurazione in Apache per attivare la redirezione e realizzata attraverso le seguenti direttive:
Action php-script /cgi-bin/php
AddHandler php-script .php
Questa opzione è stata testata solo con il server web Apache, e conta su Apache per settare la variabile di ambiente non-standard CGI REDIRECT_STATUS sulla richiesta rediretta.
Se il vostro server non supporta nessun modo per dichiarare se una richiesta è diretta o rediretta, voi nonn potete usare questa opzione e dovete usare un altro modo per eseguire i CGI.
Caso 3 : impostare doc_root o user_dir
Includere contenuti attivi, come script ed eseguibili, nelle directory dei documenti di un web server, è considerata una pratica poco sicura.
Questo perchè, per qualche errore di configurazione, gli script possono non essere eseguiti e quindi mostrati come delle semplici pagine HTML mostrando informazioni riservate o password di accesso.
Perciò molti amministratori di sistema preferiscono prevedere un altra struttura di directory per gli script, accedibile solo attraverso PHP CGI e che verrà interpretata e non mostrata in ogni caso.
E' necessario predisporre una doc_root per gli script diversa da quella per le pagine html anche se non è possibile utilizzare il metodo redirect, come mostrato in precedenza, per gli script.
E' possibile settare la document_root per gli script PHP impostandola nel file di configurazione del PHP (php.ini) oppure impostando la variabile d'ambiente $PHP_DOCUMENT_ROOT. Se è impostata, la versione CGI del PHP costruirà sempre il nome del file della richiesta aprendo questa doc_root e le informazioni del percorso dato, così potete essere sicuri che il vostro script non sarà mai eseguito al di fuori di questa directory (con l'eccezione della user_dir che vedremo in seguito).
Un'altra opzione che è possibile usare è user_dir. Quando la user_dir non è impostata l'unico controllo sulla apertura degli script è quello della doc_root.
Aprire un url like http://my.host/~user/doc.php non apre un file nella directory home dello user, ma apre un file chiamato ~user/doc.php nella doc_root (si ha un nome di directory che inizia con la tilde [~]).
Se la user_dir è impostata per esempio a public_php, una richiesta come http://my.host/~user/doc.php aprirà un file chiamato doc.php nella directory chiamata public_php sotto la home directory dello user. Se la home dello user è /home/user, il file eseguito è /home/user/public_php/doc.php.
L'utilizzo di user_dir sembra non tener conto della impostazione di doc_root, quindi è meglio tenere il controllo sull'accesso a doc_root e user_dir separati.
Caso 4: l'interprete PHP fuori dalle directory web
Una opzione molto sicura è mettere l'interprete binario PHP da qualche parte, fuori dalle directory viste dal server web. Per esempio in /usr/local/bin. la sola controindicazione a questa opzione è che nei vostri script dovrete mettere in testa una linea di codice simile a questa
#!/usr/local/bin/php
inoltre dovrete render il file eseguibile. Insomma dovrete trattarlo come un qualsiasi altro script CGI scritto in PERL o in comandi della shell od in qualsiasi linguaggio che utilizza il meccanismo di escape della shell #! per essere lanciato.
Con questa configurazione, per far gestire correttamente al PHP le informazioni contenute in PATH_INFO ed in PATH_TRANSLATED, il PHP deve essere compilato con l'opzione –enable-discard-path.
Quando il PHP è usato come un modulo Apache eredita i permessi dello user di Apache (tipicamente quelli dello user nobody). Questo ha molti impatti sulla sicurezza ed autorizzazioni.
Per esempio, se voi usate il PHP per accedere ad un database, a meno che il database non abbia un sistema di autorizzazioni proprio (come di solito è), voi dovete consentire l'accesso al database per lo user nobody.
Questo significa che degli script "malintenzionati" possono accedere e modificare il database anche senza username e password.
E' certamente possibile che un web spider possa inciampare nella pagina web dell'amministratore del database e possa cancellare tutti i vostri archivi.
Voi vi potete proteggere da questo con un'autorizzazione Apache, oppure potete progettare il vostro modello di accesso con LDAP, file .htaccess, etc. ed includere questo codice nei vostri script PHP.
Spesso una volta prese le opportune misure di sicurezza, dove l'utente PHP (e quindi quello Apache) corrono veramente pochi rischi, si scopre che il PHP non può più scrivere sui files della directory dello user. O forse non ha più la possibilità di accedere e modificare i databases. Insomma il sistema è così sicuro che non ci permette di scrivere ne i files buoni che quelli cattivi e non ci consente di effettuare transazioni nel database ne buone ne cattive.
Un errore che si fa frequentemente a questo punto è quello di dare ad Apache i permessi di root, o di far crescere i permessi di Apache in qualche modo.
Aumentare i permessi di Apache a quelli di root è estremamente pericoloso e può compromettere l'intero sistema, questa soluzione può essere considerata solo da non professionisti.
Ci sono delle soluzioni più semplici. Utilizzando l'opzione open_basedir è possibile controllare e delimitare quali sono le directory in cui l'uso del PHP è consentito. E' possibile anche impostare un area apache-only, per limitare tutte le attività web a dei files non di sistema o non user.
Sicurezza del Filesystem
Il PHP è soggetto alle misure di sicurezza implicite nella maggior parte dei server basate sul rispetto dei permessi per i files e le directory.
Questo consente di controllare quali file del filesystem possono essere letti.
Occorre porre attenzione che tutti i file disponibili in lettura sul web siano in lettura anche per tutti gli user che hanno accesso al file system.
Poiché il PHP è stato progettato per consentire l'accesso al filesystem a livello di user, è possibile scrivere in PHP degli script che consentono di leggere i system files come /etc/passwd oppure che modificano le connessioni ethernet, inviano pesanti job di stampa, etc
Le implicazioni di questo debbono far si che noi ci assicuriamo che i files che ciascuno legge e scrive siano quelli corretti.
Considerate il seguente script, dove uno user indica che desidererebbe cancellare un file nella sua home directory. Questo assume una situazione dove il PHP è usato per gestire i file per mezzo di un interfaccia web, questo significa che l'utente Apache è abilitato a cancellare file nella sua home directory.
Esempio 1. Un controllo scarso sulle variabili in input porta a...
<?php
// cancella un file dalla home dello user
$username = $_POST['user_submitted_name'];
$homedir = "/home/$username";
$file_to_delete = "$userfile";
unlink ($homedir/$userfile);
echo "$file_to_delete è stato cancellato!";
?>
Poiché lo username è inseribile da un form, è possibile richiedere la cancellazione del file con lo username di un altro. In questo caso è opportuno utilizzare una qualche forma di autenticazione.
Considerate cosa può succedere se il file di cui si richiede la cancellazione è /etc/passwd.
Esempio 2. ...attacco al filesystem
<?php
// cancella ida qualche parte del disco fisso
// a cui ha accesso lo user PHP. Se PHP ha accesso come root :
$username = "../etc/";
$homedir = "/home/../etc/";
$file_to_delete = "passwd";
unlink ("/home/../etc/passwd");
echo "/home/../etc/passwd has been deleted!";
?>
ecco un esempio di script più sicuro
Esempio 3. controllo più sicuro sul file name
<?php
// cancella un file dal disco fisso
// in cui il PHP ha accesso.
$username = $_SERVER['REMOTE_USER']; // using an authentication mechanisim
$homedir = "/home/$username";
$file_to_delete = basename("$userfile"); // strip paths
unlink ($homedir/$file_to_delete);
$fp = fopen("/home/logging/filedelete.log","+a"); //log the deletion
$logstring = "$username $homedir $file_to_delete";
fputs ($fp, $logstring);
fclose ($fp);
echo "$file_to_delete has been deleted!";
?>
Comunque anche questo script non è esente da problemi. Se il vostro sistema di autenticazione consente, agli user, di creare i propri login di user e un user sceglie il login "../etc/", il sistema è nuovamente esposto. per questa ragione è meglio realizzare un sistema di controllo più raffinato:
Esempio 4. un controllo ancora più sicuro sul file name
<? php
$username = $_SERVER['REMOTE_USER']; // using an authentication mechanisim
$homedir = "/home/$username";
if (!ereg('^[^./][^/]*$', $userfile))
die('bad filename'); //die, do not process
if (!ereg('^[^./][^/]*$', $username))
die('bad username'); //die, do not process
//etc...
?>
A seconda del vostro sistema operativo, c'è una gran varietà di files a cui prestare attenzione inclusi i file di descrizione dei devices (/dev/ o COM1), i file di configurazione (i file contenuti in /etc/ e .ini), e aree di memorizzazione dati (/home, My Documents), etc. Per questa ragione è meglio adottare una politica di sicurezza in cui è proibito tutto ciò che non è esplicitamente ammesso.
Sicurezza per i database
Oggigiorno, i database sono il componente cardine di qualsiasi applicazione web che voglia fornire contenuti dinamici.
Poiché nei database possono essere contenuti dati molto sensibili e/o riservati bisogna considerare attentamente la loro protezione.
Per cercare o memorizzare qualsiasi informazione, occorre connettersi al database, sottomettere un query appropriata, prendere i risultati e chiudere la connessione.
Attualmente il linguaggio più comunemente usato per interagire con i database e l' SQL (Structured Query Language).
Vediamo ora come un attaccante possa infiltrarsi nel nostro database con una query SQL.
Come avrete compreso, il PHP non può proteggere il vostro database da solo.
Qui di seguito vi introdurremo alle tecniche di base su come manipolare database con gli script PHP.
Tenete in mente questa regola semplice: difesa profonda.
Inserite in più punti le azioni per aumentare la protezione e più sarà difficile per un attaccante raggiungere il suo scopo.
Un buon design dello schema del database e delle applicazioni è il trattamento per i vostri timori.
Progettare i database
Il primo passo è sempre creare il database, a meno che non usiate un database progettato da altri. Quando un database viene creato il proprietario è lo user che lo ha creato (di solito un superuser). Di solito solo il proprietario è autorizzato a far tutto sugli oggetti del database e, nell'ottica di consentire ad altri utenti di poterlo utilizzare, debbono essere assegnati dei privilegi.
Le applicazioni non debbono mai connettersi al database con lo user del proprietario o del superuser, perché questi user possono eseguire delle query che per esempio possono modificare lo schema del database o cancellarne l'intero contenuto.
Potete creare diversi user per il database, ognuno per i diversi aspetti della vostra applicazione, ognuno con privilegi molto limitati sull'utilizzo degli oggetti del database.
I privilegi richiesti dovrebbero essere assegnati soltanto per evitare che lo stesso utente possa interagire con il database nei diversi casi di uso.
Ciò significa che se gli intrusi accedono al vostro database usando uno di questi user, possono effettuare soltanto le modifiche consentite dalla vostra applicazione.
Vi suggeriamo di non implementare tutta la business logic nell'applicazione web (cioè nei vostri script), piuttosto fatelo nello schema del database, usando viste, triggers, regole (sempre se il vostro RDBMS ve lo consente).
Se il sistema evolve, significa che il database sarà aggiornato con nuove caratteristiche, e che voi dovrete re implementare la logica delle vostre applicazioni in ciascun componente di questa che accede al database .
I trigger (per esempio) possono essere usati per gestire in modo trasparente ed automatico i campi del database, e spesso forniscono un aiuto nel debug della applicazione o nel ricostruire il funzionamento di una transazione.
Connettersi al database
Potete stabilire una connessione con il protocollo SSL per criptare la connessione client/server per aumentare la sicurezza, oppure potete usare ssh per criptare la connessione di rete tra i client e il server del database. Se usate entrambe le opzioni sarà molto difficile per chiunque monitorare il vostro traffico e rubarvi informazioni.
Modello di memorizzazione criptato.
SSL/SSH protegge il viaggio dei dati tra il client e il server, SSL/SSH non protegge i dati memorizzati nel database. SSL/SSH è un protocollo per i collegamenti.
Una volta che un attaccante guadagna l'accesso al vostro database direttamente (bypassando il server web) i dati sensibili ivi conservati possono essere mostrati o mal utilizzati se le informazioni non sono protette da database stesso.
Criptare i dati è un buon modo di ovviare a questo problema ma sono molto pochi i database che offrono questo tipo di criptazione dei dati.
La maniera più semplice di lavorare a questo problema è quello di creare il vostro package di criptazione, e quindi usarlo con i vostri script PHP.
Il PHP può aiutarvi in questo caso con molte estensioni, come Mcrypt e Mhash, che coprono una grande varietà di algoritmi di criptazione.
Gli script criptano i dati prima di memorizzarli sul database e li decriptano nella fase di consultazione del database.
Nel caso di dati veramente nascosti, e se la loro rappresentazione originaria non è necessaria (per esempio perché non mostrata) può essere presa in considerazione una tecnica di hashing.
Un esempio ben conosciuto di hashing è quello di memorizzare con un hash MD5 le password di un database, invece della password stessa.
Esempio 5. usare un campo password con hashing
// storing password hash
$query = sprintf("INSERT INTO users(name,pwd) VALUES('%s','%s');",
addslashes($username), md5($password));
$result = pg_exec($connection, $query);
// querying if user submitted the right password
$query = sprintf("SELECT 1 FROM users WHERE name='%s' AND pwd='%s';",
addslashes($username), md5($password));
$result = pg_exec($connection, $query);
if (pg_numrows($result) > 0) {
echo "Welcome, $username!";
}
else {
echo "Authentication failed for $username.";
}
SQL injection
Molti sviluppatori web non sono consapevoli di come sia possibile infiltrarsi in una query SQL, ed assumono che i comandi SQL siano sicuri.
Questo significa che le query SQL possono essere utilizzate per eludere i controlli standard di accesso ed autenticazione. Qualche volta le query SQL possono addirittura consentire l'accesso alla shell dell' host che ospita il server database.
La tecnica denominata "Direct SQL Command injection" è una tecnica dove un attaccante crea o altera un comando SQL esistente per mostrare dati riservati , per sovrascrivere dati importanti o per eseguire pericolosi comandi di sistema sull 'host del database. Questa tecnica sfrutta l'applicazione per combinare l'input dello user con le parti statiche usate per costruire la query .
Gli esempi che seguono sono veri (sfortunatamente).
La mancanza di controlli di autorizzazione per il superuser nella connessione tra l'input e il database, o dell'user che può essere sfruttato da un'utente, l'attaccante può generare un superuser nella vostra base di dati.
Esempio 6. Dividere il risultato della query in più pagine ... e diventare
superuser(PostgreSQL and MySQL)
$offset = argv[0]; // beware, no input validation!
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET
$offset;";// with PostgreSQL
$result = pg_exec($conn, $query);
// with MySQL
$result = mysql_query($query);
Gli utenti normali cliccano sopra 'next e 'prev' che collegano a dove la variabile $offset è messa nel URL. Lo script prevede che la variabile $offset ricevuta sia numero decimale. Tuttavia, qualcuno prova a rompere il funzionamento del codice aggiungendo quanto segue nella URL
//nel caso di PostgreSQL
0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
select 'crack', usesysid, 't','t','crack'
from pg_shadow where usename='postgres';
--
// nel caso di MySQL
0;
UPDATE user SET Password=PASSWORD('crack') WHERE user='root';
FLUSH PRIVILEGES;
Se questo succede, lo script consentirà un accesso a superuser. Nota che 0 serve per dare un valore valido per la variabile $offset alla query originale e terminarla.
Nota: E' una tecnica comune quella di forzare il parser SQL ad ignorare il resto della query scritta dallo sviluppatore con -- che è il commento in SQL.
Un modo possibile per acquisire le password è quello di eludere le pagine risultanti della vostra ricerca. Quello di cui ha bisogno l'attaccante è solo di cercare se c'è una variabile sottomessa usata nella query SQL e non gestita in modo appropriato.
Questi filtri possono essere impostati di solito in una form precedente per personalizzare le opzioni WHERE, ORDERBY, LIMIT e OFFSET in uno statement SELECT .
Se il vostro database supporta il costrutto UNION l'attacco può consistere nell'appendere un intera query all'originale per fare il list delle password da una tabella qualsiasi. Usare campi criptati per le password è una buona cosa.
Esempio 7. elencare articoli e password (qualsiasi server database )
$query = "SELECT id, name, inserted, size FROM products
WHERE size = '$size'
ORDER BY $order LIMIT $limit, $offset;";
$result = odbc_exec($conn, $query);
La parte statica della query può essere combinata con un altra istruzione di SELECT che mostra tutte le password:
'union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from
usertable;
--
Se questa query (giocando con and e --) viene assegnata ad una delle variabili usate in $query, il gioco è fatto.
Anche gli UPDATE SQL sono soggetti per attaccare il vostro database. Queste query sono anche minacciate dall'utilizzo di alcuni loro pezzi per aggiungere una query completamente nuova.
Ma l'attaccante può ingannarvi con l'opzione SET. In questo caso occorre avere qualche informazione sullo schema del database per poter manipolare la query con successo. Queste informazioni si possono acquisire osservando i nomi delle variabili nella form, o semplicemente forzando brutalmente (in senso informatico). Non ci sono poi cosi tante convenzioni per chiamare i campi che memorizzano password e username.
Esempio 8. Resettare una password per guadagnare i privilegi dello user (qualsiasi database)
$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
ma un utente malizioso sottomette il valore ' or uid like'%admin%'; --
a $uid per cambiare la password di admin, or semplicemente imposta $pwd a
"hehehe', admin='yes', trusted=100 " (con uno spazio davanti) per guadagnare più
privilegi
. così la query viene trasformata in questa:
// $uid == ' or uid like'%admin%';--$query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%';--";
// $pwd == "hehehe', admin='yes', trusted=100 "
$query = "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE ...;"
ed ora un esempio terrificante di come sia possibile accedere alla shell del sistema operativo su alcuni host di database.
Esempio 9. Attacco al sistema operativo dell' host del database (MSSQL Server)
$query = "SELECT * FROM products WHERE id LIKE '%$prod%'";
$result = mssql_query($query);
Se l'attaccante imposta la variabile $prod a% ' exec master..xp_cmdshell 'net user test testpass /ADD' -- , allora $query diventa :
$query = "SELECT * FROM products WHERE id LIKE '%a%' exec master..xp_cmdshell 'net user test testpass /ADD'--";
$result = mssql_query($query);
Il server MSSQL esegue gli statement SQL in batch includendo un comando per aggiungere un nuovo user tra gli account locali del database. Se questa applicazione viene eseguita in questo modo ed è attivo MSSQLSERVER, con sufficienti privilegi, l'attaccante ora ha un account con cui accedere alla macchina.
Nota: Alcuni degli esempi visti sono specifici di un certo database server, questo non significa che altri non possano essere attaccati nella stessa maniera o che possano avere altre vulnerabilità.
Evitare le tecniche di attacco
Potete obiettare che l'attaccante deve possedere informazioni sullo schema del database nella maggior parte dei esempi.
Avete ragione, ma voi non potete sapere quando questo può accadere e come possa accadere, se accade il vostro database è esposto . Se voi state usando un package di gestione database open source o di dominio pubblico, come può essere un sistema di gestione contenuti (CMS) od un forum, gli attaccanti possono avere un pezzo del vostro codice in maniera semplice. Può essere un rischio se non è stato progettato adeguatamente.
La maggior parte di questi attacchi sono basati sullo sfruttamento di codice scritto senza la sicurezza in mente.
Mai fidarsi di nessun tipo di input, in special modo se proviene dal lato client, anche se viene da un box di selezione, da un campo di input nascosto o da un cookie. Il primo esempio ci mostra come una incolpevole query possa provocare disastri.
Mai connettersi ad un database come superuser o come proprietari del database. Utilizzare sempre user con privilegi molto limitati.
Controllare se l'input dato si aspetta un determinato tipo di dato. Il PHP ha un vasto raggio di funzioni di validazione, dalle più semplici che si possono trovare nelle funzioni per variabili e nelle funzioni per il tipo carattere (per esempio is_numeric(), ctype_digit()) fino al supporto per le espressioni regolari compatibili con il Perl.
Se l'applicazione si aspetta un input numerico, considerate di verificare i dati con is_numeric() o cambiare il tipo dato usando settype(), o utilizzate la sua rappresentazione numerica con sprintf().
Esempio 10. un modo più sicuro di comporre una query per paginare
settype($offset, 'integer');
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
// please note %d in the format string, using %s would be meaningless
$query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;",
$offset);
Quotate ciascun input non numerico che passate al database con addslashes() o con addcslashes().
Guardate il primo esempio.
Come ci mostra l'esempio, le virgolette inserite nella parte statica della query non la rendono abbastanza sicura e sono facilmente attaccabili.
Giusto o sbagliato che sia non mostrate nessuna informazione specifica sul database, specialmente sullo schema. Guardate anche le funzioni di Error reporting, error handling e di logging.
Potete usare le stored procedures e cursori definiti in precedenza per astrarre l'accesso dei dati, in modo da non far accedere gli utenti direttamente alle tabelle o alle viste, ma queste soluzioni hanno un altro impatto.
Oltre a questo, potete beneficiare del loggare le query sia dai vostri script che direttamente dallo stesso database, se questo lo supporta. Ovviamente, non è che fare il log delle query previene da qualsiasi tentativo di intrusione, ma è un valido aiuto per circoscrivere quale applicazione è stata attaccata.
il log non è utile di per se, ma per le informazioni che ne possiamo ricavare. Quindi più dettagliato è e meglio è.
Error Reporting
Per la sicurezza del PHP ci sono due facce del reporting di errori.
Una è benefica, nell' incrementare la sicurezza, l'altra la diminuisce.
Una tattica di attacco standard consiste nell'analizzare le risposte di un sistema inviandogli dati impropri, e verificando quali errori vengono restituiti in risposta a dati differenti per tipo e contenuti.
Questo consente all'attaccante di ottenere informazioni sul server per poterne scoprire eventuali debolezze.
Per esempio, se un attaccante ha analizzato le informazioni riguardanti una pagina basata sulla sottomissione di un form in precedenza, può cercare di sovrapporre i propri dati a quelli previsti oppure di modificarli.
Esempio 11.Attacco con le variabili su una pagina HTML personalizzata
<form method="post" action="attacktarget?username=badfoo&password=badfoo">
<input type="hidden" name="username" value="badfoo">
<input type="hidden" name="password" value="badfoo">
</form>
Gli errori PHP che di solito sono restituiti, sono di grande aiuto agli sviluppatori nel debug di uno script, indicano che cosa del file o della funzione hanno sbagliato e a che numero di linea è successo.
Queste sono tutte informazioni che possono essere sfruttate. Non è inusuale per uno sviluppatore PHP usare show_source(), highlight(), hilight_string(), oppure highlight_file() come misura per il debug, ma in un sito online, questo può esporre le vostre variabili nascoste, il vostro codice non testato ed altre informazioni pericolose. E' pericoloso in modo particolare eseguire codice conosciuto con gestori di debug incorporati, oppure che usa tecniche di debug molto comuni. Se un attaccante può determinare quale tecnica state usando, può provare a forzare una pagina mandando diverse stringhe di debug molto comuni.
Esempio 12. Esposizione di variabili di debug molto comuni
<form method="post" action="attacktarget?errors=Y&showerrors=1"&debug=1">
<input type="hidden" name="errors" value="Y">
<input type="hidden" name="showerrors" value="1">
<input type="hidden" name="debug" value="1">
</form>
A prescindere dal modo di gestire gli errori, la possibilità di generare errori in un sistema porta l'attaccante ad acquisire informazioni.
Per esempio, lo stile di un errore generico del PHP indica che il sistema utilizza il PHP, se l'attaccante sta guardando una pagina HTML e vuole verificare quale è il back-end (per guardare eventuali debolezze del sistema), inserendo dati errati, può essere possibile determinare che il sistema è costruito in PHP.
Un errore di funzione può indicare che tipo di server database si sta usando, o dare suggerimenti su come una pagina web è stata programmata o progettata.
Questo consente una analisi più profonda nelle porte aperte dal database, o a cercare per bug o debolezze specifiche della pagina web.
Inserendo diversi pezzi di dati errati, per esempio, un attaccante può determinare l'ordine di un controllo di autenticazione in uno script,(dal numero di linea dell'errore) così come può tentare degli attacchi che possono essere sfruttati in differenti punti dello script.
Un errore di filesystem o un generico errore PHP può indicare quali sono i permessi del web server, così come sono strutturati i files sul web server.
I codici di errore scritti dagli sviluppatori possono aggravare questo problema portando ad un semplice sfruttamento di informazioni che dovrebbero rimanere nascoste.
Tre sono le principali soluzioni a questo problema. Il primo è controllare tutte le funzioni e cercare di incapsulare la maggior parte degli errori.
La seconda è di disabilitare il report degli errori dal codice in esecuzione.
La terza è utilizzare le funzioni PHP per creare il vostro gestore di errori .
In accordo con la vostra politica di sicurezza potete adottare anche tutte e tre le soluzioni.
Un modo di prevenire questi problemi è quello di usare l'error_reporting() proprio del PHP, per aiutarvi a rendere sicuro il vostro codice ed individuare l'utilizzo di variabili potenzialmente pericolose.
Testando il vostro codice prima della distribuzione con E_ALL, voi potete trovare rapidamente le zone in cui le variabili dello script possono essere utilizzate o modificate in modo malevolo. Una volta pronti per la distribuzione, usando E_NONE, voi isolate il codice da ogni tentativo di debug intrusivo e malevolo.
Esempio 13. cercare le variabili pericolose con E_ALL
<?php
if ($username) { // Not initialized or checked before usage
$good_login = 1;
}
if ($good_login == 1) { // If above test fails, not initialized or checked before usage
fpassthru ("/highly/sensitive/data/index.html");
}
?>
Usare Register Globals
Una caratteristica del PHP che può essere usata per migliorare la sicurezza è configurare il PHP con register_globals = OFF. Annullando la visibilità delle variabili sottomesse da qualsiasi user al PHP, si riduce il numero di queste che possono essere oggetto di attacco. Occorrerà più tempo per organizzare un attacco ed in ogni caso le vostre variabili interne saranno isolate da quelle utilizzate nelle form (per esempio).
Esempio 14. Lavorare con register_globals=on
<?php
if ($username) { // can be forged by a user in get/post/cookies
$good_login = 1;
}
if ($good_login == 1) { // can be forged by a user in get/post/cookies,
fpassthru ("/highly/sensitive/data/index.html");
}
?>
Esempio 15.Lavorare con register_globals = off
<?php
if($_COOKIE['username']){
// can only come from a cookie, forged or otherwise
$good_login = 1;
fpassthru ("/highly/sensitive/data/index.html");
}
?>
Usare questa opzione in modo opportuno rende possibile prendere misure preventive per avvisare quando sta avvenendo un attacco Se voi sapete prima, ed esattamente, da dove viene il valore di una variabile , potete controllare se il suo valore o la sua provenienza sono corrette. Anche se questo non garantisce dalla modifica dei dati, questo però richiede che l'attaccante modifichi i vostri dati nel modo corretto.
Esempio 16. controllare l'inquinamento di una variabile
<?php
if ($_COOKIE['username'] &&
!$_POST['username'] &&
!$_GET['username'] ) {
// Perform other checks to validate the user name...
$good_login = 1;
fpassthru ("/highly/sensitive/data/index.html");
} else {
mail("Questo indirizzo email è protetto dagli spambots. È necessario abilitare JavaScript per vederlo.", "Possible breakin attempt", $_SERVER['REMOTE_ADDR']);
echo "Security violation, admin has been alerted.";
exit;
}
?>
Naturalmente non è che porre ad off register_globals rende sicuro il codice. Lo rende sicuro controllare ciascun dato che proviene dall'esterno.
Dati Sottomessi dagli utenti
Le più grandi debolezze in molti programmi PHP non sono implicite nel linguaggio, ma semplicemente sono programmi scritti senza pensare ai problemi di sicurezza. Per questa ragione, dovete sempre considerare le implicazioni di un frammento di codice ed i possibili danni che può arrecare se alle sue variabili viene assegnato un valore errato.
Esempio 5-17. Utilizzo pericoloso delle variabili
<?php
// cancellare un file dalla directory home dello user o forse di qualcun altro
unlink ($evil_var);
// scrivere il log degli accessi o forse un nuovo utente in /etc/passwd ?
fputs ($fp, $evil_var);
// eseguire qualcosa di banale oppure rm -rf *?
system ($evil_var);
exec ($evil_var);
?>
Dovete sempre, verificare con cura il vostro codice per accertarvi che le variabili sottomesse da un browser web siano controllate propriamente, e dovete farvi le seguenti domande :
Questo script accederà solo ai file dovuti ?
Possono essere inseriti dati non usuali o indesiderati ?
Questo script può essere usato in un modo non voluto ?
Può essere usato in unione con altri script in un modo negativo ?
Le transazioni possono essere loggate adeguatamente ?
Rispondendo adeguatamente a queste domande mentre scrivete lo script, piuttosto che dopo, voi prevenite una possibile riscrittura dello stesso per incrementare la vostra sicurezza.
Partendo con questo in mente voi non garantirete la sicurezza del vostro sistema, ma sicuramente vi aiuterà a migliorarla.
Voi potete anche considerare l'ipotesi di disabilitare le opzioni register_globals, magic_quotes, o altre opzioni che possono confondere sull'origine dei dati sottomessi dall'utente.
Effettuare il debug della applicazione utilizzando il reporting degli errori del PHP, può aiutarvi a capire quali sono le variabile utilizzate prima di essere inizializzate o controllate in modo da prevenire un utilizzo inappropriato di queste.
Nascondere il PHP
In generale la sicurezza ottenuta dal nascondere è una delle forme di sicurezza più deboli. Ma in qualche caso un po' più di sicurezza è desiderabile.
Con poche semplici tecniche possiamo nascondere il PHP, in modo da rallentare un attaccante che sta cercando debolezze nel vostro sistema.
Settando expose_php = off nel vostro file php.ini, riducete le informazioni che gli possono essere rese disponibili.
Un altra tattica è configurare Apache per interpretare con il PHP differenti tipi di file , per esempio con una direttiva in .htaccess o nel file di configurazione di Apache stesso. Potete allora usare per i file PHP delle estensioni ingannevoli.
Esempio 18. nascondere PHP come un altro linguaggio
# Make PHP code look like other code types
AddType application/x-httpd-php .asp .py .pl
nasconderlo completamente
Esempio 19. usare un tipo sconosciuto di estensione per l'estensione PHP
# il codice PHP sembra di una estensione sconosciuta
AddType application/x-httpd-php .bop .foo .133t
o nasconderlo come codice html, questo però ha dei riflessi sulle performance perchè vengono interpretate anche le pagine di puro html.
Esempio 20. usare il tipo html come estensione PHP
# tutto il codice php sembra html
AddType application/x-httpd-php .htm .html
La prevenzione attraverso il mascheramento è secondaria rispetto a ciò che è stato visto in precedenza ma presenta pochi svantaggi e può aiutare.