Archivi tag: php

CentOS 6 e PHP 5.3.3: cifratura del codice sorgente mediante BLENC (Blowfish Encoder for PHP source scripts)

A quanti di voi sarà capitato di dover cifrare del codice PHP scritto di vostro pugno, magari perchè contenente informazioni sensibili, quali, ad esempio, username e password di accesso al database?

Ebbene, un metodo semplice per ottenere quanto sopra consiste nell’installare il modulo PHP BLENC (che è ancora in versione beta – qui trovate il manuale). Tale operazione può essere effettuata in modo semiautomatico utilizzando il tool pecl presente sulla nostra macchina.

PHPPrima di procedere, è necessario verificare che il suddetto applicativo sia già installato, digitando il comando:

[root@linuxbox ~]# which pecl

il cui output dovrebbe essere:

/usr/bin/pecl

Successivamente, potremo procedere con l’installazione vera e propria del modulo BLENC, lanciando il comando:

[root@linuxbox ~]# pecl install -f blenc

A questo punto potremo creare il file blenc.ini da posizionare all’interno della dir /etc/php.d, il cui contenuto dovrà  essere simile al seguente:

; Enable blenc extension module
extension=blenc.so

All’interno del file /etc/php.ini, utilizzando la entry blenc.key_file, possiamo definire il percorso in cui trovare le chiavi per decifrare gli scrip criptati, ovvero:

[blenc]

blenc.key_file = /usr/local/etc/blenckeys

Ora passiamo alla creazione del file encrypt.php, il cui contenuto dovrà essere:

<?php

$file_to_crypt=file_get_contents("security.php");

$key=blenc_encrypt($file_to_crypt, "connect.php");

$key_file = ini_get('blenc.key_file');

file_put_contents($key_file, $key."\n", FILE_APPEND);

?>

il quale ci consentirà di cifrare gli scrip di nostro interesse.

In particolare, security.php contiene il codice sorgente che vogliamo criptare, connect.php sarà il nostro file cifrato e la chiave generata mediante la funzione blenc_encrypt() verrà salvata all’interno dell’apposito file /usr/local/etc/blenckeys, ricavato mediante il parsing della direttiva blenc.key_file presente in /etc/php.ini.

Da notare che all’interno della funzione file_put_contents(), il secondo argomento contiene il carattere \n (newline), poichè per il corretto funzionamento del modulo è necessario che per ogni riga sia presente una sola chiave (dove ogni chiave si riferisce univocamente ad un file cifrato). Tale “accortezza” non è presente all’interno del manuale ufficiale.

Come ultimo step riavviamo httpd:

[root@linuxbox ~]# service httpd reload

ed abbiamo finito.

Alla prossima.

Graficizzare i risultati dei check di Nagios mediante pnp4nagios su Centos 6

Un sistema di monitoring degno di questo nome deve prevedere la possibilità di tracciare dei grafici in base ai risultati dei check automatici da esso effettuati.

Purtroppo Nagios non supporta tale funzionalità in maniera nativa, ma, per fortuna, sono disponibili tutta una serie plugin pensati appositamente per il suddetto NMS, in grado di svolgere la funzione in oggetto.

Il plugin che ho scelto prende il nome di pnp4nagios ed è scaricabile da qui.

pnp4nagios

Una volta completato il download vediamo come installare e configurare il tutto.

Preparazione della macchina ed installazione di pnp4nagios

Per prima cosa scompattiamo la tarball mediante il comando:

 [root@NMS ~]# tar -xzvf pnp4nagios-0.6.21.tar.gz

Come prerequisiti per la compilazione degli eseguibili presenti all’interno della directory appena creata è necessario installare sul nostro sistema il compilatore (gcc), una libreria perl, ovvero perl-time-HiRes (utile per testare la configurazione ad installazione avvenuta), una libreria PHP (necessaria per l’esportazione dei grafici in formato pdf) ed il software rrdtool:

[root@NMS ~]# yum install gcc
[root@NMS ~]# yum install perl-Time-HiRes
[root@NMS ~]# yum install php-gd
[root@NMS ~]# yum install rrdtool perl-rrdtool

Ad installazione avvenuta, posizioniamoci nella dir appena creata

[root@NMS ~]# cd pnp4nagios-0.6.21

e lanciamo i comandi:

[root@NMS pnp4nagios-0.6.21]# ./configure
[root@NMS pnp4nagios-0.6.21]# make all
[root@NMS pnp4nagios-0.6.21]# make fullinstall

In particolare, il primo comando serve a verificare che il sistema sia dotato di tutti gli applicativi necessari per la compilazione dei sorgenti relativi a pnp4nagios; il secondo comando esegue la compilazione vera e propria mentre il terzo comando installa il plugin sulla nostra macchina.

La dir target dell’installazione sarà la seguente: /usr/local/pnp4nagios/

Configurazione dell’interfaccia Web di pnp4nagios

Per ragioni di sicurezza occorre dapprima disabilitare la pagina install.php, semplicemente rinominandola:

[root@NMS ~]# mv /usr/local/pnp4nagios/share/install.php /usr/local/pnp4nagios/share/install.php.ignore

In seguito dobbiamo linkare la dir share di pnp4nagios all’interno di /var/www/html, in modo che sia raggiungibile via Web:

[root@NMS ~]# ln -s /usr/local/pnp4nagios/ /var/www/html/pnp4nagios

Inoltre, bisogna fare in modo che il suddetto plugin utilizzi lo stesso file di autenticazione HTTP utilizzato da Nagios:

[root@NMS ~]# cat /etc/httpd/conf.d/nagios.conf | grep AuthUserFile

Tale file (per default) dovrebbe essere il seguente: /etc/nagios/passwd

Ergo sostituiamo la entry presente nativamente su /etc/httpd/conf.d/pnp4nagios.conf con alla voce AuthUserFile con /etc/nagios/passwd

Ricarichiamo la configurazione di httpd per rendere effettive le suddette modifiche:

[root@NMS ~]# service httpd reload

Configurazione di Nagios

Supponendo che la modalità di funzionamento di pnp4nagios sia la synchronous, le modifiche da apportare alla configurazione di Nagios (/etc/nagios/nagios.cfg) sono le seguenti:

1) Abilitare i perfdata, sostituendo

process_performance_data=0

con

process_performance_data=1 

2) Decommentare le diciture:

host_perfdata_command=process-host-perfdata service_perfdata_command=process-service-perfdata

All’interno del file /etc/nagios/objects/commands.cfg occorre sostituire le diciture:

'process-host-perfdata' command definition define command{        command_name    process-host-perfdata        command_line    /usr/bin/printf "%b" "$LASTHOSTCHECK$t$HOSTNAME$t$HOSTSTATE$t$HOSTATTEMPT$t$HOSTSTATETYPE$t$HO$        } 'process-service-perfdata' command definition define command{        command_name    process-service-perfdata        command_line    /usr/bin/printf "%b" "$LASTSERVICECHECK$t$HOSTNAME$t$SERVICEDESC$t$SERVICESTATE$t$SERVICEATTEMP$        }

con:

define command {        command_name    process-service-perfdata        command_line    /usr/bin/perl /usr/local/pnp4nagios/libexec/process_perfdata.pl } define command {        command_name    process-host-perfdata        command_line    /usr/bin/perl /usr/local/pnp4nagios/libexec/process_perfdata.pl -d HOSTPERFDATA }

Ciò si rende necessario per consentire a pnp4nagios di processare le perfdata provenienti dai plugin utilizzati per effettuare i check (se le supportano).

Per quanto riguarda il file /etc/nagios/objects/templates.cfg occorre aggiungere la stringa:

action_url      /pnp4nagios/index.php/graph?host=$HOSTNAME$&srv=_HOST_

nella definizione degli host, e la stringa:

action_url      /pnp4nagios/index.php/graph?host=$HOSTNAME$&srv=$SERVICEDESC$ 

nella definizione dei servizi.

A configurazione di Nagios completata lanciamo un restart del demone in questione:

[root@NMS ~]# service nagios restart

Verifica della configurazione di Nagios e pnp4nagios

Per effettuare i check della configurazione relativa a Nagios ed al suo plugin pnp4nagios è possibile scaricare lo script perl verify_pnp_config:

[root@NMS ~]# wget http://verify.pnp4nagios.org/verify_pnp_config

Rendiamolo eseguibile:

[root@NMS ~]# chmod +x  verify_pnp_config

Lanciamo dunque il comando:

[root@NMS ~]# ./verify_pnp_config -m sync -c /etc/nagios/nagios.cfg -p /usr/local/pnp4nagios/etc

e se il check da risultato positivo significa che la procedura di configurazione è andata a buon fine.

Come ulteriore check, verifichiamo che nella dir /usr/local/pnp4nagios/var/perfdata vi sia una dir per ogni host su cui è stata abilitata l’action_url e che tali directory contengano dei file con estensione *.rrd (round robin data) ed *.xml.

Nel caso in cui queste dir non contengano i suddetti file (recanti nomenclatura del tipo nomeservizio.rrd e nomeservizio.xml) significa che i plugin utilizzati per effettuare i check non supportano le perfdata.

Infine, all’interno della directory /usr/local/pnp4nagios/share/templates.dist sono presenti le pagine Web contenenti la struttura dei grafici per i plugin nativi di Nagios. Se avete installato qualche plugin aggiuntivo, solitamente viene resa disponibile per il download anche la pagina Web per i grafici ad esso associati. Tale pagina dovrà essere salvata all’interno della directory /usr/local/pnp4nagios/share/templates.

Fine del post, alla prossima.

PS: per fare in modo che pnp4nagios possa creare i file *.rrd è necessario disabilitare SElinux mediante il comando:

[root@NMS ~]# setenforce 0

Paginazione PHP con HTTP POST

La paginazione è uno dei metodi più gettonati per ripartire i contenuti dinamici di un sito Web su più pagine. Spesso, però, per ragioni di sicurezza (e non solo) risulta più conveniente sostituire il GET con il POST, avvalendosi di un pò di codice Javascrip da utilizzare lato client.

 

web.jpg

Ecco un il codice (testato e funzionante):

$query = "SELECT * FROM Utenti";

$risultato = $mysqli->query($query);

$count = mysqli_num_rows($risultato);
$per_pagina = 30; //secondo parametro di LIMIT
$tot_pagine = ceil($count / $per_pagina); //approssima la divisione all'intero
$pagina_corrente = 1;

 if(isset($_POST['pagina']) && is_numeric($_POST['pagina']))
 {
            $pagina_corrente = $_POST['pagina'];
 }
$primo = ($pagina_corrente - 1) * $per_pagina; //primo parametro di LIMIT
$query = $query."ORDER BY C.Cognome ASC LIMIT $primo, $per_pagina";

$risultato = $mysqli->query($query1);

lato server, mentre lato client il codice Javascrip è così definito:

<scrip type="text/javascrip">
 function invia()
 {
     document.loginform4.method="post";
     document.loginform4.action="visualizzacontatti.php";
     document.loginform4.submit();
 }
 </scrip>

Inoltre, il numero di pagina calcolato nell’ambito del codice PHP verrà salvato in un campo di input hidden:

<input type="hidden" name="pagina" id="pagina" value="<?php echo $pagina_corrente?>"/>

I numeri di pagina cliccabili saranno così definiti:

<div id="paginazione">
<p align ="center">
<?php
if($pagina_corrente == 1 || $count == 0) //se siamo nella prima pagina oppure non vi sono record
{

echo "&lt;&lt; Precedente";
}
else
{
$pagina_precedente = ($pagina_corrente - 1);
?>
<a href ="javascrip:invia()" onclick="document.loginform4.pagina.value=<?php echo $pagina_precedente?>"><?php echo "<< Precedente"?></a>
<?php
}

echo ' Pagine: ';

for($i = 1; $i <= $tot_pagine; $i++)
{
if($i == $pagina_corrente)
{
echo "[$i]";
}
else
{
?>
<a href ="javascrip:invia()" onclick="document.loginform4.pagina.value=<?php echo $i?>"><?php echo $i ?></a>
<?php
}
}
if($pagina_corrente == $tot_pagine) // se siamo nell'ultima pagina
{
echo "Successiva >>";
}
else
{
$prossima_pagina = ($pagina_corrente + 1);
?>
<a href ="javascrip:invia()" onclick="document.loginform4.pagina.value=<?php echo $prossima_pagina?>"><?php echo "Successiva >>"?></a>
<?php
}
?>
</p>
</div>

Enjoy!

PHP e mysql: input sanitization di base

Tutti gli addetti ai lavori sanno (o almeno dovrebbero sapere) che nell’ambito della sicurezza Web occorre prestare particolare attenzione ai dati trasmessi al server mediante gli appositi form HTML.

Tale operazione si rende indispensabile poichè un utente malevolo potrebbe “postare” delle stringhe “ad hoc” per “iniettare” del codice SQL arbitrario (SQL injection) oppure inserire all’interno della pagina del codice HTML (o javascrip) che provoca il reindirizzamento su un sito infetto (iframe, redirect et similia, ovvero tutto ciò che riguarda gli attacchi XSS).

background_xss.gif

Per questo motivo ho deciso di postare qualche riga di codice PHP che consente di filtrare in modo rapido ed efficace i dati immessi dagli utenti.

In particolare, vengono svolti due controlli: il primo si occupa dell’individuazione (e la successiva rimozione) dei caratteri speciali di MySQL (apice singolo, apice doppio, backslash, cancelletto, ecc.), mentre il secondo verifica che non vi siano dei tag HTML all’interno delle stringhe immesse nei campi di input.

Ecco il codice:

if(isset($_POST['username']) && $_POST['username'] != '')
{
$username = mysqli_real_escape_string($mysqli,strip_tags($_POST['username']));
}

else
{
        $errore = 'Devi inserire lo username';
}

Come potete notare, il campo su cui è stato implementato il filtro è quello relativo allo username. Per prima cosa viene verificato che la variabile $_POST[‘username’] sia settata e non sia vuota.

Se tale controllo va a buon fine, tramite la funzione strip_tags() viene rimosso l’eventuale codice HTML e successivamente, mediante mysqli_real_escape_string() vengono filtrati i caratteri speciali relativi a MySQL.

Ovviamente, attraverso tale meccanismo non darete agli utenti la possibilità di scegliere username del tipo:

<username>

o ancora:

username#

ma si sa, in questi casi bisogna certamente scendere a compromessi e capire dove sta il male minore.

A presto.

Visualizzare mediante PHP il nome del database utilizzato

Recentemente ho dovuto modificare parzialmente uno dei CRM che ho sviluppato. Non volendo inficiare il DB in produzione ho dovuto “clonarlo”, in modo da poter fare i miei esperimenti in assoluta libertà.

Poichè volevo essere sicuro che ciascuna pagina utilizzasse effettivamente il database “clone” (avendo a disposizione un solo server), ho implementato un controllo in PHP che mi restituisse il nome del DB in uso all’apertura della pagina.

Ecco lo scrip:

$db = $mysqli -> query("SELECT DATABASE()");

while($riga1 = $db -> fetch_assoc())
{
    echo $riga1["DATABASE()"];
}

La chiave di lettura delle righe di codice sopra riportate sta proprio nella funzione DATABASE() di MySQL, la quale consente di individuare il nome del DB in uso.

Da riga di comando avremo una situazione simile alla seguente:

mysql> SELECT DATABASE();
+------------+
| DATABASE() |
+------------+
| prova        |
+------------+
1 row in set (0.00 sec)

mysql>

Il post termina qui, a presto.

Aumentare la durata delle sessioni mediante il file php.ini

Recentemente si è manifestata la necessità di aumentare la durata delle sessioni relative ad uno dei CRM che ho sviluppato, in modo da non costringere l’utente ad effettuare nuovamente il login dopo un certo periodo di inattività.

PHP

Il modo più semplice ed efficace per ottenere tale risultato consiste nel modificare il valore associato al parametro session.gc_maxlifetime del file php.ini, portandolo da 1440 (24 minuti) a 28800 (8 ore).

Per fare ciò occorre editare il suddetto file mediante il comando:

nightfly@nightbox:~$ sudo nano /etc/php5/apache2/php.ini

Una volta completata l’operazione, è necessario riavviare apache2 digitando:

nightfly@nightbox:~$ sudo service apache2 restart

ed abbiamo finito.

A presto.

NB: ovviamente la sessione verrà comunque terminata alla chiusura del browser.

Hardening del file php.ini

Una delle operazioni più importanti per la messa in sicurezza dei Webserver consiste nella corretta configurazione del file php.ini.

Infatti, alcune funzioni presenti nel file in questione che per default risultano abilitate, potrebbero essere la causa di eventuali falle di sicurezza non indifferenti.

PHP

Editiamo quindi il file php.ini presente nella directory /etc/php5/apache2/:

nightfly@nightbox:~$ sudo nano /etc/php5/apache2/php.ini

Una volta aperto il file in scrittura modifichiamo le seguenti direttive:

1) settiamo ad off i parametri safe_mod e safe_mod_gid entrambi deprecati dalla versione 5.3.0 di PHP:

safe_mod = Off 
safe_mod_gid = Off

2) restringiamo l’open_basedir alla sola directory /var/www:

open_basedir = /var/www

In questo modo limiteremo la possibilità di aprire file mediante PHP (ad esempio utilizzando la funzione f)) esclusivamente alla directory specificata.

3) Disabilitiamo alcune funzioni di PHP che potrebbero compromettere il nostro sistema:

disable_functions = exec, passthru, shell_exec, system, proc_open, popen, curl_exec, curl_multi_exec, parse_ini_file, show_source, php_uname, getmyuid, getmypid, leak, listen, diskfreespace

4) disabilitiamo la visualizzazione degli errori, in modo da non dare punti di riferimento ad eventuali attaccanti che potrebbero utilizzare delle tecniche specifiche (ad esempio il blind SQL injection) per minare alla sicurezza del nostro server:

display_errors = Off

5) disabilitiamo la direttiva register_globals, ormai deprecata, in quanto non consente di fare alcuna distinzione tra le variabili passate mediante POST e quelle passate mediante GET:

register_globals = Off

6) disabilitiamo la direttiva allow_url_fopen che consente l’apertura di file mediante URL:

allow_url_fopen = Off

7) settiamo ad off la direttiva allow_url_include che consente l’inclusione di file mediante URL:

allow_url_include = Off

8) se le vostre applicazioni server side non prevedono l’upload di alcun tipo di file occorre disabilitare la direttiva file_uploads:

file_uploads = Off

9) affinchè i cookie non siano accessibili da scrip client side (editati, ad esempio, in javascrip oppure in vbscrip), riducendo notevolmente i rischi di attacchi XSS, occorre abilitare l’opzione session.cookie_httponly:

session.cookie_httponly = 1

10) per salvare le sessioni in una dir non convenzionale, evitando così che eventuali utenti con accesso shell possano intercettarle, dobbiamo operare sul parametro session.save_path:

session.save_path = "/tmp/phpsessions"

11) Infine, per irrobustire le difese del core PHP consiglio di installare la patch suhosin mediante il comando:

nightfly@nightbox:~$ sudo apt-get install php5-suhosin

Adesso il nostro server può essere considerato “quasi” sicuro, almeno per quanto riguarda la configurazione di PHP.

A presto.

PHP: date in italiano

La funzione date() è sicuramente un ottimo strumento per rappresentare le date nel formato gg/mm/aaaa et similia, ma diventa deltutto insufficiente quando si ha a che fare con le date in italiano.

PHP

A tal proposito, torna certamente utile la funzione strftime() che abbinata alla funzione setlocale() ci consente di ottenere la visualizzazione della data nella lingua che preferiamo.

Ma facciamo un esempio pratico. Per prima cosa richiamiamo la funzione setlocale() nel modo seguente:

setlocale(LC_TIME, 'ita', 'it_IT.utf8');

Il secondo parametro, ovvero ita è essenziale per il i server Windows, mentre il terzo parametro, ovvero ‘it_IT.utf8’ è indispensabile per i server *nix.

Richiamiamo adesso la funzione

$data = strftime("%A, %d %B %Y");

Dove %A è il nome del giorno della settimana, %d è il giorno del mese in notazione gg, %B è il nome del mese e %Y è l’anno nel formato yyyy.

Ora, poichè la funzione echo di per sè non visualizza correttamente i caratteri accentati (per i giorni Lunedì, Martedì, ecc.) è necessario utilizzare la funzione utf8_decode(). Avremo quindi:

echo utf8_encode($data)

Un esempio di output così ottenuto è il seguente:

martedì, 14 giugno 2011

Alla prossima.

NB: per ottenere una lista delle localizzazioni attive sulla nostra linux box basta lanciare il comando:

nightfly@nightbox:~$ locale -a

PHP: eliminare una variabile di sessione

Recentemente mi è arrivata un’email di un tizio che non riusciva a rimuovere una variabile di sessione. In particolare, tale variabile rimaneva attiva nonostante la chiamata alla funzione unset(),effettuata nel modo seguente:

unset($_SESSION['variabile']);

Analizzando il codice, ho subito identificato il problema, ovvero la chiusura della sessione con un session_write_) prima ancora di svuotare la variabile incriminata.

Ergo, la soluzione consiste nel riaprire la sessione, svuotare la variabile e richiudere la sessione immediatamente:

session_start();

unset($_SESSION['variabile']);

session_write_);

See ya.

Joomla! o non Joomla!: questo è il problema

Joomla! o non Joomla!: questo è il problema. Già, proprio così, ed è anche un problema abbastanza serio. Da quando è nato questo magnifico CMS (insieme ai suoi fantastici fratelli, quali Drupal, Mambo e compagnia bella) la vita per noi web-developer (se così possiamo definirci) è diventata quasi un inferno. Ed è così che mi ritrovo magicamente in una sorta di limbo, in cui programmare vecchio stile è diventata una pratica fuori moda, per non dire sconveniente e controproducente. Parole grosse le mie? Forse, ma rispecchiano (quasi interamente) la realtà. Ed è così che giornalmente devo fare i conti con un branco di wannabe web-developer che si vantano di questa o quell’applicazione, di questo o di quel portale, nonostante la loro unica abilità consista nel saper installare qualche plugin di Joomla! e saper rimuovere (non con poche difficoltà) qualsiasi richiamo al vero creatore dell’applicazione. Ma che senso ha tutto questo? Cosa ne è del controllo del codice, della logica di programmazione, dell’analisi dei requisiti, ecc.?

Così finiremo per ritrovarci in un mondo fatto per il 90% da finti programmatori PHP e da finti webmaster, che rivendono i loro lavori per 4 soldi (ho visto portali venduti alla modica cifra di 190 €), perchè effettivamente di risorse mentali per le loro creazioni ne hanno impiegate ben poche.

E che dire poi dell’aspetto relativo alla sicurezza? Se io sul mio portale (relativamente sicuro) installo un plugin di un “indiano” semi-sconosciuto e semi-ignorante che non ha provveduto a realizzare i meccanismi più elementari di controllo dell’input e che crede che OWASP sia una specialità culinaria turca, posso dire allegramente addio a tutte le altre politiche di sicurezza, ed il defacing sarà solo questione di tempo.

C’è poi la questione dei colloqui di lavoro. Direte voi: cosa diamine c’entrano i colloqui di lavoro con Joomla!? C’entrano… eccome! Alla domanda (che puntualmente mi viene posta) “conosci Joomla!?” Io puntualmente rispondo: “si, ma faccio volentieri a meno di usarlo!”. Ed ecco che negli occhi del mio interlocutore leggo (non necessariamente in quest’ordine): smarrimento, delusione, derisione e compassione, seguite dalla fatidica frase: “ti faremo sapere”. E la mia reazione è sempre la stessa: no comment (anche se dovrei reagire in modo diverso, ad esempio chiedendogli se conosce il significato dei tag <tr> e <td>).

Certo, Joomla! è intuitivo da usare, ci semplifica notevolmente la vita ma… ricordate… non è tutto oro quello che luccica!

Alla prossima.