Dependency Injection in PHP parte 2: Container

La settimana scorsa avevo pubblicato un articolo introduttivo alla dependency injection; questa volta faremo un passo avanti  definendo un Container, ovvero una classe il cui scopo è quello di contenere i nostri oggetti e di permettere di recuperarli in modo pratico.

Un primo container primitivo è un oggetto dotato di due metodi set() e get() i quali permettano l’accesso alle istanze rilevanti per la nostra applicazione:

class Container
{
   protected $_Data = array();
   public function get($item_name)
   {
      if (isset($this->_Data[$item_name]) )
         return $this->_Data[$item_name];
      return null;
   }
   public function set($item_name,$item)
   {
      $this->_Data[$item_name] = $item;
   }

}

Questo può sembrare triviale ma in realtà è tutto ciò di cui abbiamo bisogno! : Grazie al container possiamo dedicare una parte dell’applicazione, che chiameremo bootstrapping, alla creazione delle istanze di oggetti che utilizzeremo in altri punti dell’esecuzione.

$connection = new MysqlConnection($host,$user,$password,$db);
$db = new Database($connection);

$logger = new TxtLogger('myfile.txt');

$view = new View('master');

$container = new Container();
$container->set('database',$db);
$container->set('logger',$logger);
$container->set('master_view',$view);

A questo punto non ci resta che passare l’istanza di container ai business objects che necessitano delle istanze di database, logger e view per poi poterle riutilizzare:

//...
public function businessMethod()
{
   $this->doAction();
   $this->container->get('logger')->log(' Action done :) ');

   $userDao = new UserDao( $this->container->get('database') );
}

Per rendere il codice più leggibile potremmo sovrascrivere il magic method __get() in modo da cercare l’attributo richiesto direttamente nel container:

public function __get($name)
{
   if (null !== $this->container->get($name) )
   return $this->container->get($name);

   throw new BusinessObjectException('Unkown property '.$name);
}
public function anAction()
{
   $this->database->query('INSER INTO table VALUES (bla bla bla)');
}

Per approfondire il design di un container, vi consiglio di leggere queste slides scritte da Fabien Potencier, il creatore di Symfony.

Annunci

Dependency Injection in php: Introduzione

Quando ti trovi di fronte ad un progetto che utilizza di un buon numero di classi e istanze, serve un piano d’attacco. Non puoi semplicemente cominciare a scrivere,  altrimenti è molto probabile che, al momento della verità (Il testing), arrivino i problemi. Lo dico con certezza perchè io stesso, fino a qualche tempo fa, ero uno di quelli che iniziava subito a scrivere codice e che poi si ritrovava con mille tab aperte in eclipse per cercare di fixare il problema.

La dependency injection è l’arte di semplificare la creazione e la configurazione delgi oggetti e recentemente, dopo aver spopolato in ambiente Java, ha cominciato a prendere piede in ambiente php; in questi articoli cercherò di riportare ciò che ho imparato a riguardo.

Pensate di avere un oggetto che dovere utilizzare in diversi punti dell’applicazione e che per essere utilizzato ha bisogno di un minimo di configurazione, ad esempio:

/**
*   Nota : pensiamo che il Device non
*   l'abbiamo scritto noi, ma che stiamo utilizzando
*   classi di terze parti altrimenti avremmo potuto
*   modificare il costruttore del device e
*   scrivere new Device($logger,$request)
*/

$device = new Device();
$logger = new XmlLogger();
$logger->prepare('path/to/file.xml');
$device->setLogger($logger);
$device->setRequest(HttpRequest::create()  );
$response = $device->doYourWork();

Prima di riuscire a fare il suo lavoro, il device ha bisogno di alcune righe di preparazione e questo ogni volta che in un punto diverso dell’esecuzione avete bisogno di usare una istanza di device! Diciamo che Device, in questo caso, ha due dipendenze: XmlLogger e HttpRequest.

Premesso che eviterò di usare stati globali come la peste (per maggiori informazioni leggete qui) e quindi qualsiasi forma di Singleton, scrivere quel codice di configurazione ogni volta che si vuole chiamare doYourWork() potrebbe essere una grossa seccatura, specialmente se Device fosse più complesso.

Una prima soluzione ci è data dalla Dependency injection: scriviamo un Container:

class DeviceContainer
{
public function getDevice()
{
   $this->device = new Device();
   $logger = new XmlLogger();
   $logger->prepare('path/to/file.xml');
   $this->device->setLogger($logger);
   $this->device->setRequest(HttpRequest::create()  );
   return $this->device;

}
}

Ora, ogni volta che avremo bisogno di utilizzare il Device, dobbiamo solamente chiamare il metodo getDevice() della classe container ed otterremo una istanza correttamente configurata :

class DeviceUserA
{
   function work()
   {
       $container = new DeviceContainer();
       $device = $container->getDevice();
       $device->doYourWork();
    }

}

Questa soluzione è ancora poco flessibile perchè potreste necessitare all’interno della vostra applicazione di diversi tipi di device ognuno con una configurazione diversa. Dobbiamo modificare il nostro DeviceContainer:

class DeviceContainer
{
public function getDeviceA()
{
   if (null !== $this->device_a)
      return $this->device_a
   $this->device_a = new DeviceA();
   $logger = new XmlLogger();
   $logger->prepare('path/to/file.xml');
   $this->device_a->setLogger($logger);
   $this->device_a->setRequest(HttpRequest::create()  );
   return $this->device_a;

}
public function getDeviceB()
{
   if (null !== $this->device_b)
      return $this->device_b

   $this->device_b = new DeviceB();

   $dsn = 'mysql:dbname=testdb;host=127.0.0.1';
   $user = 'dbuser';
   $password = 'dbpass';
   $connection = new PDO($dns,$user,$password);
   $logger = new MysqlLogger($connection);

   $this->device_b->setLogger($logger);
   $this->device_b->setRequest(HttpRequest::create()  );
   return $this->device_b;

}

}

Potete notare che ogni metodo getDeviceX() ha due righe di codice che consentono di non reistanziare l’oggetto ogni volta
ma di riutilizzare istanze già create dal container.

Per questo articolo è tutto, nel prossimo vedremo come scrivere un container che funzioni in ogni situazione (o quasi) senza bisogno di modificare la classe container.

Ora torno a lavorare a qoo framework, non vedo l’ora di pubblicarlo ed essere criticato 😉 .

Nuovo progetto: qoo framework

Ebbene si, lo ammetto ho sempre avuto un debole per la programmazione ad oggetti e per il design delle architetture software. Nondimeno ho sempre avuto un debole per i framework php e da circa 4 anni ho sempre scritto collezioni di classi a mano per capire quali idee ci fossero dietro alcuni tra i framework php più famosi.

Recentemente ho deciso che la prossima collezione di classi avrebbe dovuto essere più versatile delle altre e che avrei dovuto utilizzarla per realizzare più applicazioni. Questa decisione, unita al fatto di voler provare a gestire un progetto medio-grande ed Open source mi hanno portato alla ‘creazione’ di questo progetto, un framework php5 molto leggero, orientato alla leggibilità del codice ed alla flessibilità di utilizzo. L’ ho chiamato qoo framework.

La prima domanda che potrebbe venirvi in mente è “Abbiamo bisogno di un nuovo framework?” dopotutto ce ne sono tantissimi, alcuni dei quali (Zend e Symfony ad esempio) validissimi ed usatissimi, altri (Lithium, interessantissimo) sono emergenti e molto promettenti. Ognuno ha i suoi pro ed i suoi contro, zend e symfony sono incredibilmente potenti ma necessitano di un certo sforzo iniziale per poter rendere produttivo lo sviluppatore. Lithium adotta una architettura particolare che fa l’occhiolino alla programmazione orientata agli aspetti e Code igniter a volte soffre di eccessiva semplificazione (dal mio punto di vista) che porta a mancanza di eleganza. Eleganza che per me è una caratteristica molto importante quando si tratta di codice php.

Eccomi quindi alla ricerca di un framework elegante.

Qoo framework è ancora in fase di sviluppo, mancano il Data abstraction layer e la parte dedicata alla creazione di API, ma per il momento posso elencarvi le caratteristiche principali che ho voluto e che voglio dare a qoo:

  • Semplicità
  • Leggerezza
  • Leggibilità
  • Flessibilità
  • Scalabilità
  • Modularità

Le prime tre caratteristiche sono dovute al fatto che sono stanco di vedere e soprattutto di scrivere codice che se non commentato in ogni minimo dettaglio perde di comprensibilità; inoltre, non voglio dover scrivere 2000 righe di codice per fare qualcosa che in php non oo richiederebbe un decimo del codice. Non voglio costringere chi usa il framework ad avere mille file di configurazione e dover sottostare a strettissime regole di interazione tra le componenti. Questo ci porta al quarto punto: flessibilità. Si perchè essendo una webapp un sistema complesso, voglio dare la possibilità di fare la stessa cosa in modi diversi, lasciando libero chi scrive il codice. Per questo in qoo ci sono diversi modi di inizializzare le componenti del sistema e di riutilizzarle in seguito in altri punti dell’applicazione: non ti piace singleton? Benissimo usa il registry! Non ti piace registry? benissimo usa la dependency injection e la parameter propagation!. Il quinto punto si chiama scalabilità e questo riguarda strettamente l’architettura mvc stessa di qoo, disegnata per poter essere frammentata in più layer gestiti sotto una struttura gerarchica (spesso ci si riferisce a questa soluzione con l’acronimo HMVC). Infine “Modularità” si riferisce al fatto che l’implementazione di ogni classe fa si che esse siano debolmente dipendenti le une dalle altre e che le le dipendenze sono gestite tramite metodi get/set.

Seguiranno in futuro altre news, ma per il momento vi lascio alcuni link utili:

Windows Vista + Eclipse + Git + Github

Di recente mi sono stati proposti due progetti davvero interessanti riguardanti il web2.0 per prepararmi al meglio ho deciso di riorganizzare le mie carissime e fidatissime classi php che utilizzo da tempo in un piccolo framework che mi permetta di prototipizzare alcune operazioni comuni.

Come servizio di project hosting mi sono affidato a Github, che come suggerisce il nome utilizza repository Git, ecco quindi i passi da compiere per avere un sistema funzionante:

  • Create un account su Github.com
  • Create un repository su github.com con il nome del vostro progetto
  • Seguite questa guida per installare Git su sistemi Windows
  • Aprite Eclipse e nel tab Help cliccate su “install new software”, selezionate il repository “all available site” e nel filtro di ricerca scrivete Git, dovreste trovare un pacchetto chiamato EGit ed uno chiamato JGit, io ho installato EGit quindi faccio riferimento a quello
  • Al posto di creare un nuovo progetto su eclipse, andate su File->Import->Git->Projects from Git ed inserite i dati relativi al vostro repository git locale
  • Importate con il wizard per semplicità

A questo punto avete il vostro progetto collegato al repository Git hostato da Github.com, manca solo un piccolo dettaglio che mi ha creato 5 minuti di panico :

Per qualche motivo, Eclipse cerca di default le vostre chiavi RSA per SSH nella cartella {USER}/ssh invece di {USER}/.ssh . In rete ho trovato due modi per risolvere il problema in windows. Il primo consiglia di creare un link alla directory .ssh e chiamarlo ssh in modo che eclipse la trovi (il comando in questione dovrebbe essere mklink \D .ssh ssh). Il secondo metodo che ha funzionato per me, consiste nel modificare una opzione di eclipse, ovvero andate su Window->Preferences->General->Network Connection->SSH2 e mettete il punto davanti alla benedetta directory ssh.

Buon teamwork a tutti 🙂

Session Storage: sicurezza delle applicazioni php ed insicurezze degli hosting condivisi

Php salva i dati relativi alle sessioni tramite la serializzazione, ovvero l’array $_SESSION viene linearizzato e salvato in un supporto come un file o un record di un database.

session storage

Il comportamento di questa caratteristica di php può essere controllato da due variabili di configurazione di php:

Session.save_handler: indica a php come viene salvata la sessione e in quale supporto, il valore di default è file (quindi php out of the box scrive i dati di sessione su un file) mentre gli altri valori possibili son user, mm e sqlite.

Session.save_path: indica la directory in cui le sessioni saranno salvate e mantenute. Il valore di default è /tmp (la mia installazione di xampp ad esempio salva in C:\xampp\tmp).

Al giorno d’oggi la maggior parte dei servizi di hosting forniscono un servizio di hosting condiviso, molto gettonato perchè abbassa di molto i costi sia per la gestione che per l’utente finale. Un setup del genere però, in cui diverse web application girano sullo stesso server in cui tutte le sessioni vengono salvate nella stessa directory porta degli enormi problemi di sicurezza, vediamo un esempio:

Un cliente del servizio di hosting ha due applicazioni web che girano sullo stesso server. Entrambe fanno uso di sessioni ma sono destinate ad utilizzi diversi; entrambe fanno uso di form per inserire dati nel sito ma la prima delle due applicazioni salva nella sessione anche l’input utente non filtrato, mentre la seconda mantiene in sessione soltanto dati filtrati.

Poiché le due applicazioni condividono lo spazio in cui vengono salvate le sessioni un possibile attacco consiste nel:

  1. Popolare la sessione della prima applicazione (che non filtra l’input) con dati creati ad hoc
  2. Modificare il cookie della sessione relativa all’applicazione 2 inserendo il session id dell’applicazione 1 (questa sessione contiene dati non filtrati)
  3. L’attaccante utilizza i dati non filtrati dell’applicazione 1 per danneggiare l’applicazione 2.

Altro scenario ancora più pericoloso riguarda la condivisione di spazio di salvataggio delle sessioni e dell’implementazione dell’applicazione: capita spesso che i servizi di hosting php mettano a disposizione alcuni CMS per facilitare l’avvio di un sito web, ecco quali sono i rischi:

  1. L’attaccante è un utente dell’applicazione 1 con certi privilegi, magari è un editor o un moderatore. In particolare l’applicazione 1 si apoggia su un CMS.
  2. L’applicazione 2 condivide la directory di salvataggio delle sessioni (sempre lo stesso servizio di web hosting un pò farlocco) ma in più si appoggia allo stesso CMS dell’applicazione 1.
  3. L’attaccante logga nella applicazione 2 come utente senza privilegi, copia il session id dell’applicazione 1 nel cookie dell’applicazione 2 e si ritrova loggato con gli stessi privilegi che aveva nell’applicazione 1 (perchè le due applicazioni fanno uso dello stesso sistema di gestione dei ruoli).

Vediamo quindi che potenzialmente, se un servzio di hosting, ospitante un sito non protetto, non si occupasse di dividere le directory dedicate alle sessioni, i problemi di sicurezza del sito non protetto potrebbero ripercuotersi anche sugli altri.

Ne consegue che la precauzione necessaria al fine di evitare queste falle di sicurezza è la divisione delle directory di salvataggio delle sessioni in applicazioni, così che ogni applicazione avrà la propria directory, ad esempio:

/tmp/applicazione1

/tmp/applicazione2 ecc..

Se il servizio di hosting in cui ci appoggiamo non ha già pensato a modificare la variabile del php.ini possiamo provare ad aggiungere la seguente riga di codice nella nostra applicazione:

ini_set(‘session.save_path‘, ‘/tmp/nostra_app‘);

Incrociando le dita e sperando che la funzione ini_set non sia inibita dal servizio di hosting.

Un’altra soluzione è quella di aggiungere una variabile all’interno della nostra sessione che identifichi univocamente la nostra applicazione, ad esempio:

session_start();

if (!isset($_SESSION[‘application’]) || ((string)$_SESSION[‘application’] !== ‘application_1’)) {

session_regenerate_id();

$_SESSION = array(‘application’ => ‘application_1′);

}

In questo modo il cookie modificato dall’attaccante sarà diverso dai cookie della nostra applicazione e l’id di sessione viene rigenerato.

Un metodo forse ancora più efficiente per gestire il mantenimento delle sessioni nel web server consiste nell’implementazione manuale delle funzioni di session storaging tramite la funzione set_session_save_handler(): essa permette di definire quali funzioni si occupano delle diverse operazioni di gestione delle sessioni.

Capire la Session Fixation

Proprio in questi giorni mi sto occupando della stesura di un testo abbastanza lungo su “Come proteggere le sessioni” in php, per la precisione, 2 minuti fa mi stavo dedicando proprio a quel testo, ma nel frattempo mi è arrivata un’ispirazione ed intendo seguirla scrivendo questo articolo.

In breve, ogni sessione che inizializiamo nelle applicazioni web viene riconosciuta tramite il session id: una stringa che ci lega univocamente ai dati della nostra attuale sessione nel server web. Solitamente questo session id viene propagato di comunicazione in comunicazione (browser/server) in 2 modi:

  • via URL
  • via COOKIE

Il primo dei due approcci è, secondo me, veramente sconsigliabile, in quanto espone al massimo il vostro id di sessione e potrebbe addirittura finire in qualche segnalibro o in qualche social network (la mia crociata è iniziata).Questo tipo di approccio può essere evitato impostando nel file php.ini, la variabile  session.use_trans_sid a 0. Il secondo non garantisce l’assoluta sicurezza, ma contribuisce ad aumentare la complessità della nostra identità online.

Il nostro Session Id potrebbe essere scovato in diversi modi:

  • potrebbe semplicemente essere individuato indovinando l’id giusto, magari dopo averne generati e provati parecchi
  • si potrebbe sfruttare una vulnerabilità del browser della vittima per inviarsi i suoi cookie
  • attraverso la cosiddetta Session Fixation

La session fixation consiste nel forzare la vittima ad utilizzare un id di sessione già noto, così da evitare di doverselo procurare. Un semplice esempio di session fixation può essere fatto utilizzando la esposizione di cui vi parlavo del session id via url:

La vittima riceve un link malevolo del tipo http://www.applicazioneNota.org?PHPSESSID=rr2339g9929f922nenoq123v9uu   così che la vittima sia forzata ad effettuare il login con quell’id di sessione associato.

Una volta loggata, la vittima inizializzerà i suoi dati di sessione , la quale è totalmente accessibile per l’attaccante che può così accedere ai dati sensibili della vittima. Ecco un esempio che non fa uso del SID via url, ma sfrutta la conoscenza del cookie giusto contenente il PHPSESSID

<?phpsession_name(“_crfl”);

session_start();

if (isset($_POST[“mySecret”]))

$_SESSION[“mySecret”] = $_POST[“mySecret”];

if (isset($_SESSION[“mySecret”]))

echo “<p>Il tuo <b>segreto</b> è: “.$_SESSION[“mySecret”].”</p>”;

else

echo “<p>Tu non hai alcun segreto con me</p>”;

echo “informazione di servizio:”.session_id();

?>

<form action=”” method=”post”>

<input type=”text” name=”mySecret” />

<input type=”submit”>

</form>

Ora munitevi di un plugin per firefox che vi mostri e vi permetta di modificare i cookie per poter testare a fondo questo metodo, ad esempio io utilizzo l’ottima estensione Web Developer

Salvate il listato sovrastante come fixation.php nel vostro server web e raggiungetelo con un browser (ad esempio firefox con l’estensione che vi ho linkato). Al primo accesso troverete il messaggio “tu non hai alcun segreto con me” e il vostro id di sessione

Ora aprite la stessa pagina con un diverso browser oppure da un altro pc (se possibile), alla prima visita con il diverso browser otterrete la stessa scritta, ma se proviamo a digitare qualcosa nel browser e inviamo il form, otterremo la scritta: Il tuo segreto è: <SEGRETO_SESSIONE2> con annesso il vostro id di sessione, diverso dal primo ottenuto con l’altro browser, copiate questo id.

Riprendete firefox e nel menu cookie, cliccate su visualizza cookie, una volta individuato il cookie che contiene l’id di sessione (nel nostro caso si chiama _clrf)  cliccate su modifica, come nell’immagine sottostante e inserite l’id che avete copiato dalla finestra del secondo browser.

CatturaA questo punto avete modificato il cookie di sessione della sessione1 , provate a fare Refresh ed otterrete la scritta:

Il tuo segreto è: <SEGRETO_SESSIONE2>

La vittima vedrebbe così perdere la propria identità.

Andiamo avanti con le possibili cause di perdita della propria identità a causa dei cookie

In php, per impostare un cookie, si usa la sintassi:

<?php

setcookie(“Nome_del_cookie”,“Valore”,$durata_del_cookie, “/”, “www.miosito.com” )

?>

dove i primi 3 argomenti si spiegano da soli, il quarto ci dice che il cookie varrà per tutto il dominio e l’ultimo parametro fornisce proprio il dominio.

Se la vittima visitasse il sito creato ad hoc dall’attaccante, contenente il codice:

<?php

setcookie(“PHPSESSID”,“ticonosco”,time()+3600, “/”, “www.unsitointernet.com” )

?>

e poi visitasse veramente http://www.unsitointernet.com si ritroverebbe con un cookie con il sesssion_id noto all’attaccante, una volta che la vittima abbia effettuato il login, l’attaccante potrebbe accedere alla sua sessione semplicemente replicando il suo SID

Vi risulterà quindi evidente, che il session id non basta per validare un utente, ma bisogna fornire dati di conferma, che saranno comunque replicabili, ma almeno renderanno il tutto più difficile ad esempio controllando di volta in volta lo USER AGENT, o meglio, un digest MD5 dello stesso, poichè difficilmente un utente nell’ambito di una sessione si metterà a cambiare user agent.

In definitiva però la cosa migliore da fare in questi casi è sfruttare la funzione session_regenerate_id():

essa non fa altro che sostituire l’attuale PHPSESSID con un id nuovo, così, ripetendo questa operazione ad ogni richiesta renderemo la sessione più difficile da agganciare.

Altra buona abitudine è quella di controllare che l’id di sessione sia stato generato dal server,così da avere la conferma che non provenga dall’esterno (ottimo contro l’ultimo tipo di attacco mostrato)

if (!isset($_SESSION[‘SERVER_GENERATED_SID’])) {

session_destroy(); // destroy all data in session

}

session_regenerate_id(); // generate a new session identifier

$_SESSION[‘SERVER_GENERATED_SID’] = true;

CSRF Cross site request forgeries: difendersi con i tokens

In un precedente articolo, ho introdotto il concetto di CSRF, questa volta ci occuperemo di ovvero fornire i concetti di base della protezione contro questo tipo di attacchi. funny_cat_pictures_092

Ci tengo a precisare che anche questo articolo conterrà metodi di base per la protezione delle proprie applicazioni, in quanto la scienza dietro gli attacchi è davvero molto sviluppata al momento.Abbiamo visto che un attacco di tipo CSRF consiste nel fare in modo che la vittima invii delle richieste inconsapevolmente, sfruttando le sue credenziali. Riportiamo brevemente un esempio per riprendere le fila del discorso: Mentre visitiamo la pagina “posta in arrivo” del nostro account email non protetto, apriamo un altra scheda del browser, capitando in un sito malevolo creato ad hoc per attaccare la pagina del nostro email service provider. Cliccando su un semplice link del tipo:

<a href=”happy_page.php” onClick=”document.send_mail_form.submit()”>Click</a>

e magari avendo semplicemente (ci sono modi di gran lunga migliori per inviare richieste POST) predisposto il seguente form nascosto:

<form name=”send_mail_form” method=”POST” action=”www.unsafe-email-service.com”>

<input type=”hidden” name=”receiver” value=”randomReceiver1”>

<input type=”hidden” name=”receiver” value=”randomReceiver2”>

<input type=”hidden” name=”receiver” value=”randomReceiver3”>

..

<input type=”hidden” name=”receiver” value=”randomReceiverN”>

<input type=”hidden” name=”object” value=”Object”>

<input type=”hidden” name=”text” value=”randomText”>

</form>

comporterebbe l’immediato e totalmente involontario invio delle email con il nostro account di posta elettronica.

Come possiamo mitigare il rischio CSRF ,quantomeno, rendere gli attacchi meno scontati e facilmente implementabili? Un metodo largamente usato, in quanto offre un buon rapporto sicurezza/facilità di implementazione è quello dei Token.

Un token è una stringa di testo che funge da validazione per le richieste che inviamo alla nostra applicazione e deve essere correlata alla sessione dell’utente.

Potremmo sintetizzare il concetto nei seguenti passi: Creo il token, lo salvo nella sessione e lo richiedo per validare ogni input utente. Se il sistema rileva una richiesta POST non accompagnata dal corretto token ci permetterà di riconoscere un probabile tentativo di attacco e di interrompere l’esecuzione.

Parte 1: creiamo il token.

Presupponendo che la sessione sia già stata inizializzata

<?php

$token = sha1(uniqid(rand(),TRUE));

$_SESSION[‘token’] = $token;

?>

Parte 2: generiamo il form

<form name=”myForm” method=”POST” action=”myPage.php”>

<input type=”hidden” name=”token” value =” <?php echo $_SESSION[‘token’]; ?> “ />

<input type=”text” name=”myText” />

<input type=”submit” value=”invia”>

Parte 3: controlliamo la validità della richiesta

<?php

if (!isset ($_POST[‘token’] OR !isset ($_SESSION[‘token’] OR $_POST[‘token’] != $_SESSION[‘token’] OR)

{

//errore! Interrompere l’esecuzione

} else {

//Il token è validato, possiamo procedere

}

?>

n questo modo costringiamo l’attaccante a scoprire il token assegnato alla sessione della vittima, inoltre questo fa si che l’attacco colpisca soltanto quell’utente, o comunque costringe l’attaccante a replicare il token di ogni vittima, affinchè le richieste artificiali da lui create bypassino il sistema di controllo.

Come migliorare questo approccio?

Come ho già detto, questo metodo non garantisce la sicurezza assoluta, quindi se state cercando di mettere al sicuro i form di un sito di e-commerce, non vi consiglierei mai di fermarvi a questo livello di validazione; possiamo però migliorare il nostro codice di controllo in diversi modi. Vediamone alcuni.

Generiamo un token per ogni form della pagina che l’utente visualizza, così da aumentare la complessità del problema, potremmo dire che creare un token per ogni richiesta dell’applicazione sarebbe un’ottima cosa.

Associamo un tempo di validità del token dopo il quale non verrebbe più considerato valido:

<?php

$token = sha1(uniqid(rand(),TRUE));

$_SESSION[‘token’] = $token;

$_SESSION[‘token_time’] = time();

?>

e successivamente controlliamo che non sia passato troppo tempo dalla creazione del token, altrimenti lo consideriamo scaduto :

<?php

if (!isset ($_POST[‘token’] OR !isset ($_SESSION[‘token’] OR $_POST[‘token’] != $_SESSION[‘token’] OR)

{

//errore! Interrompere l’esecuzione

} else {

$token_time = time() – $_SESSION[‘token_time’];

//Facciamo in modo che la durata massima del token sia di 10 minuti

if ($token_time < 600)

{

//interrompiamo l’interruzione informando che la pagina è scaduta

}

//Il token è validato, possiamo procedere

}

?>

Concludo proponendovi una riflessione: anche la miglior difesa contro attacchi di tipo CSRF può essere superata se non vi affianchiamo una ottima difesa per gli attacchi di tipo XSS, in quanto questi ultimi potrebbero essere utilizzati come “apripista” verso le nostre difese.