Nuova pagina facebook: PHP Programming

Nel poco tempo libero di cui dispongo a causa dell’università e dello sviluppo di un social site (con relativo framework php) che mi tengono occupatissimo, non riesco a postare articoli interessanti ne inerenti la sicurezza delle web app ne alla programmazione php in generale.

Per questo ho deciso di aprire una pagina facebook in cui pubblicherò i link interessanti che mi propone il mio feed reader durante la colazione,  con l’aggiunta di qualche pensiero personale riguardo il php.

Ecco il link alla pagina, iscrivetevi numerosi 😀

Annunci

Le insidie di WordPress.com : Link Injection

[English Version: here]

Chi di voi , come il sottoscritto, fosse un affezionato utente della piattaforma wordpress.com credo sarebbe d’accordo con me sull’utilità e la praticità della voce “statistiche” nella gestione dei nostri blog. Essa infatti ci riporta diversi dati relativi alle visite al nostro blog: Visite giornaliere, notorietà dei nostri articoli, commenti, parole cercate nei motori di ricerca ma soprattutto i referer: ovvero da quale url gli utenti sono giunti nel nostro blog.

Questa funzionalità ci aiuta a capire se i nostri articoli vengono linkati dall’esterno e in tal caso ci permette di scoprire cosa si dice a proposito di quanto abbiamo scritto.

La pagina delle statistiche di questo Blog

Alcuni di voi avranno già capito dove voglio andare a parare: Il referer è un campo dell’header della request HTTP che il browser dell’utente che raggiunge il nostro blog, scambia con il server in cui il nostro blog è hostato. In particolare, questo campo indica qual’è l’url che conduce l’utente all’elemento corrente, in questo caso al nostro blog.
Come si potrebbe utilizzare questo referer per compiere un attacco al gestore di un blog WordPress.com?

L’hacker potrebbe modificare il campo referer dell’header HTTP e così pubblicare un qualsiasi link nella pagina di amministrazione del blog. Inoltre, l’hacker potrebbe creare diverse richieste http con il campo referer modificato così che la vittima, incuriosita da questa discreta “fonte di visite”, segua il link. A questo punto la vittima si ritroverebbe a scaricare la pagina indicata dall’hacker, esponendosi a diversi potenziali rischi (per l’hacker, il più è fatto).

La prima riflessione su quanto esposto riguarda la pericolosità di questo tipo di attacchi, infatti la piattaforma wordpress.com (come altre piattaforme esistenti e gratuite) risulta essere molto user friendly e non sempre chi ne fa uso possiede conoscenze in ambito di web security.

A questo bisogna aggiungere la semplicità dell’attacco : basta essere in grado di modificare un campo dell’header della richiesta http cosa realizzabile in modo estremamente semplice grazie all’estensione per firefox: modify headers, reperebile Qui

La finestra principale di Modify Headers

anche se personalmente preferisco utilizzare un tool avanzato come Webscarab, che mette a disposizione sotto un’unica interfaccia una miriade di funzionalità per l’analisi delle comunicazioni HTTP/S. Webscarab è un progetto parte dell’OWASP (Open Web Application Security Project).

Un aspetto molto importante riguarda la possibilità di inserire codice javascript nel referer, poiché nella pagina delle statistiche, viene stampato un input utente, l’applicazione è potenzialmente esposta al Cross Site Scripting (sono già alla ricerca di un possibile vettore javascript 😉 )

Concludo dicendo che l’utilizzo di questa tecnica per portare la vittima nella pagina web infetta è molto efficiente, in quanto non richiede un contatto diretto con la vittima (irc, mail , msn ecc).

Css History Hack: discussione e prevenzione

histLa settimana scorsa è spuntato un post nel forum sla.ckers.org che ha attirato subito la mia attenzione. Vista l’impossibilità di accedere all’oggetto history per navigare tra i vari elementi della cronologia, si è pensato ad un metodo alternativo ma molto efficace per accedere ad essa.

Il funzionamento di base è molto semplice e si appoggia su di una funzionalità messa a disposizione dai browser di oggi attraverso i CSS: la specifica a:visited. Cerchiamo di capire meglio con un esempio:

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN”>
<html>
<head>
<title>Css history</title>
</head>
<body>
<script type=”text/javascript”>
var knownurls = [
http://www.google.it&#8217;, ‘http://www.myspace.com&#8217;,
http://www.yahoo.com&#8217;, ‘http://www.google.com&#8217;,
http://www.twitter.com&#8217;, ‘http://it.netlog.com&#8217;,
http://ha.ckers.org&#8217;, ‘http://seoblackhat.com&#8217;,
http://www.cgisecurity.com&#8217;,  , ‘http://www.facebook.com/home.php&#8217;
];
document.write( ‘<style type=”text/css”>a:link{color:#fff;}’ );
document.write( ‘a:visited{height:1px;width:1px;display:block;overflow:hidden;margin:1px;}’ );
document.write( ‘{font-size:1px;overflow:hidden;height:1px;margin:0;padding:0;}</style>’ );
var output = ‘<h1>Urls riconosciuti</h1><ul>’;
var c = document.createElement(‘div’);
document.body.appendChild(c)
for(i in knownurls){
var anchor=document.createElement(‘a’);
anchor.href=knownurls[i];
anchor.innerHTML=knownurls[i];
c.appendChild(anchor)
if(anchor.offsetHeight==1){
output+='<li>’+knownurls[i]+'</li>’;
}
}
document.write(output+'</ul>’);
</script>
</body>
</html>

Questa porzione di codice funziona più o meno in questo modo: ho una lista di url, li stampo in una pagina html e sfrutto il selettore visited. Se l’url è stato visitato dalla vittima, la specifica visited fa si che a quello specifico url vengano assegnate delle caratteristiche (via CSS) che lo rendano riconoscibile ed infine lo inserisce nel testo della pagina html. In questo modo aprendo questa pagina con il browser vedremo una lista di url contenente soltanto indirizzi già visitati .

Naturalmente, per ottenere una lista cospicua di siti visitati è bene aggiungere molti elementi all’array knownurls, ad esempio cercando tra  i siti più visitati dagli utenti di alcuni social bookmarking come delicious e digg.

Il codice javascript che riconosce questi url può essere facilmente nascosto ed utilizzato per lanciare automaticamente attacchi di tipo CSRF, poichè c’è una buona probabilità che l’utente abbia una sessione attiva in uno dei siti precedentemente visitati.

Altro pericoloso utilizzo di questa tecnica permette all’attaccante di ottenere un token di sessione propagato via url (una tecnica di protezione piuttosto ambigua che discuterò in un prossimo articolo) .

Alcuni sviluppatori inseriscono un token di conferma per ogni richiesta nell’url, così da rifiutare ogni richiesta non attendibile. Questo implica una diminuzione del traffico indesiderato e delle richieste non autorizzate, ma d’altra parte espone il token di sessione che potrebbe essere salvato dalla cronologia. Quest’ultima, come abbiamo visto, risulta essere vulnerabile quindi ci ritroveremmo con i nostri token di sessione esposti a favore dell’attaccante.

Quest’ultimo, una volta compreso il formato del token, potrebbe  con un semplice script javascript generare migliaia di token in pochi secondi, con una buona probabilità di ottenere il nostro token di sessione e quindi  accedere a dati sensibili.

Ci tengo a ribadire il concetto che session id e tokens sono strumenti di sicurezza, ma anche essi vanno protetti adeguatamente. Perciò vi consiglio di NON propagare il token di sessione via url ma di farlo attraverso campi nascosti di un form e di rigenerarlo il più possibile.

Se siamo utenti, possiamo ricorrere all’estensione per firefox SafeHistory che ci protegge dal tipo di attacco oggetto di questo articolo.

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.

Validazione dei dati con PHP

Capita spesso che i programmatori php, un po per pigrizia, un po per mancanza di caffeina, si dimentichino di validare i dati a livello server, affidandosi unicamente alla validazione client-side con javascript…. no va bene!! ecco un esempio pratico:

Il seguente form mette a disposizione una select-list da cui scegliere la distribuzione linux che si preferisce tra quelle presenti nella lista:

<form name="censimento" method="POST" action="registra.php">
 <select name="distro">
          <option value="Debian">Debian</option>
          <option value="Slackware">Slackware</option>
          <option value="Ubuntu">Ubuntu</option>
          <option value="Arch Linux">Arch Linux</option>
      </select>  <input type="submit" value="Vota"> <
&ltform>

Lo script presente nella pagina registra.php non dovrà fa altro che modificare una tabella Mysql o un file xml che tiene conto delle votazioni.
Assumiamo di aver scritto una funzione ad hoc per il salvataggio delle nostre informazioni ,di averla chiamata salvaVoto() e che come argomento accetti la stringa con il nome della distro votata, il codice per il salvataggio del voto potrebbe essere il seguente:

<?php
function salvaVoto($_distro) {
 //QUI IL CODICE DI SALVATAGGIO
}
if (isset($_POST["distro"])) {
      salvaVoto($_POST["distro"]);
 } else {
      echo "<b>Errore!</b> Non è stata specificata alcuna distribuzione!!<br />";
  }
?>

I meno attenti di voi diranno: “Che problema c’è? Il widget select presente nel form obbliga ad una scelta imposta dal programmatore!”, ma non è così!!
Anche se il passaggio avviene tramite il metodo POST, che non permette di manipolare il contenuto delle variabili direttamente dalla barra dell’URL, chiunque potrebbe:

  • Esaminare il codice sorgente della pagina contenente il form “censimento”
  • Individuare l’indirizzo della pagina che elabora e salva i dati
  • Scrivere un form risiedente in una qualsiasi pagina html (deve essere accessibile da un browser, quindi anche nella vostra Home) che punti alla stessa pagina di elaborazione, utilizzando il metodo POST e che invii una variabile chiamata “distro”, come nel caso del form originale.

Ecco un esempio di form HTML creato per inviare richieste alla pagina registra.php (assumendo che i due listati precedenti siano collocati all’indirizzo http://www.sito.it):

<form name="censimento" method="POST" action="registra.php">
      <input type="text" name="distro">
       <input type="submit" value="Vota">
;  </form>

In questo modo, anche la più stupida delle marmotte potrebbe inviare dati arbitrari al vostro script di registrazione, mandando a puttane tutto il vostro (pessimo) lavoro. La tabella Mysql che state utilizzando per salvare le votazioni potrebbe contenere qualsiasi stringa, quale sarebbe la vostra reazione se la ritrovaste piena di WINDOWS VISTA al posto di una delle distro Linux che avete proposto?

Questo è solo uno degli scenari (anche moooolto più complessi) in cui i dati esterni devono essere filtrati.

Ecco un modo molto semplice per filtrare la nostra variabile:

<?php
function salvaVoto($_distro) {
  //QUI IL CODICE DI SALVATAGGIO
 }
if (isset($_POST["distro"])) {
      switch($_POST["distro"]) {
          case "Debian":
              salvaVoto($_POST["distro"]);
          break;
          case "Slackware":
              salvaVoto($_POST["distro"]);
          break;
          case "Ubuntu":
             salvaVoto($_POST["distro"]);
          break;
          case "Arch Linux":
              salvaVoto($_POST["distro"]);
          break;
          default:
              echo "<b>Attenzione:</b> Voto nullo!<br />";
 }
?>

Il loop switch, dispone di tutta la casistica legata alle possibili stringhe contenute nella variabile $_POST[“distro”], in questo modo non rischiamo di riempire di schifezze la nostra tabella 😉