Tunneling e proxy server per Ajax e non solo

Lunedì, 10 Dicembre 2007

A causa della sua capacità di comunicare con il server, l'oggetto XmlHttpRequest (XHR), usato nella tecnologia Ajax (acronimo di Asynchronous JavaScript and XML, la cui pronuncia dovrebbe essere "egiacs" anche se noi italiani preferiamo "aiacs"), ha un blocco di protezione che gli impedisce di eseguire richieste esterne al dominio in cui opera. Questa protezione è necessaria per impedire Injection Javascript (tecniche di "iniezione" di codice estremamente pericoloso con lo scopo di violare il sistema) di svariato tipo, con l'obiettivo ultimo di "irrompere" nel sistema.
Questo limite è oggi tenuto in seria considerazione e si sta pensando, in qualche modo, di risolverlo - direttamente nell'oggetto XmlHttpRequest - senza pregiudicare la sicurezza (vedi anche: Third proposal for cross-site extensions to XMLHttpRequest ).

Comunque sia la situazione oggi è la seguente:

XHR 

Il codice Javascript che usa l'oggetto XmlHttpRequest (presente nella pagina yourWebApp.html) può fare richieste esclusivamente verso il dominio miodominio.com, cioè al dominio a cui appartiene il codice Javascript. Uno scenario diquesto tipo, quindi:

XHR

...non funzionerà!

Inoltre, come indicato su The Same Origin Policy di Mozilla:

[..] Mozilla considers two pages to have the same origin if the protocol, port (if given), and host are the same for both pages. To illustrate, this table gives examples of origin comparisons to the URL http://store.company.com/dir/page.html.

The Same Origin Policy

Non è solo il dominio a rendere le cose difficili. In aggiunta considerate che ogni browser ha una sua implementazione dell'oggetto XmlHttpRequest e quindi sue regole personali.
Comunque sia per risolvere il problema esistono varie tecniche.

1. Proxy Server

Questa tecnica sfrutta un linguaggio lato server per "ingannare", per così dire, l'oggetto XHR in modo tale da creare un tunnel tra l'oggetto XHR e il nostro dominio esterno target. Il PHP, ad esempio, è in grado di recuperare informazioni da altri domini in vari modi, in base anche al tipo di installazione e restrizioni impostate sul nostro server. Nel caso generale quello che si tende a fare è questo:

XHR

In poche parole l'oggetto XHR colloquia con il nostro dominio, dove una pagina appositamente scritta recupera le informazioni da un dominio esterno. Si realizza quindi un proxy in PHP, cioè una "pagina tramite" che recupera le informazioni per noi, restituendole all'oggetto XHR. Uno dei più semplici proxy server è proprosto, ad esempio, da Yahoo:

PHP:
  1. <?php
  2. // PHP Proxy example for Yahoo! Web services.
  3. // Responds to both HTTP GET and POST requests
  4. //
  5. // Author: Jason Levitt
  6. // December 7th, 2005
  7. //
  8.  
  9. // Allowed hostname (api.local and api.travel are also possible here)
  10. define ('HOSTNAME', 'http://search.yahooapis.com/');
  11.  
  12. // Get the REST call path from the AJAX application
  13. // Is it a POST or a GET?
  14. $path = ($_POST['yws_path']) ? $_POST['yws_path'] : $_GET['yws_path'];
  15. $url = HOSTNAME.$path;
  16.  
  17. // Open the Curl session
  18. $session = curl_init($url);
  19.  
  20. // If it's a POST, put the POST data in the body
  21. if ($_POST['yws_path']) {
  22.     $postvars = '';
  23.     while ($element = current($_POST)) {
  24.         $postvars .= key($_POST).'='.$element.'&';
  25.         next($_POST);
  26.     }
  27.     curl_setopt ($session, CURLOPT_POST, true);
  28.     curl_setopt ($session, CURLOPT_POSTFIELDS, $postvars);
  29. }
  30.  
  31. // Don't return HTTP headers. Do return the contents of the call
  32. curl_setopt($session, CURLOPT_HEADER, false);
  33. curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
  34.  
  35. // Make the call
  36. $xml = curl_exec($session);
  37.  
  38. // The web service returns XML. Set the Content-Type appropriately
  39. header("Content-Type: text/xml");
  40.  
  41. echo $xml;
  42. curl_close($session);
  43. ?>

In questo codcie si fa uso delle curl, una nota libreria PHP usata per bypassare eventuali restrizioni sui più semplici e noti comandi di fopen(), fread() etc... Basterebbe, infatti, aprire la destinazione sul nostro dominio esterno con una delle tante funzioni messe a disposzione da PHP, come readfile(). Sfortunatamente, spesso, queste funzioni sono disabilitate o limitate su alcuni hosting, per questioni di sicurezza. Le curl, tuttavia, sono quasi sempre disponibili.

 

2. Il buon vecchio TAG IFRAME

Ricordo ancora quando nel 1996 implementai una delle prime tecniche di Remote Scripting (come veniva chiamato all'epoca, quando Ajax era solo un detersivo). Il TAG IFRAME è oggi ancora usato, abusato, adorato e disprezzato, in dipendenza del programmatore che lo usa. Il TAG IFRAME viene spesso additato come "cosa da non fare", una pericolosa porta per un hacker. Ultimamente, poi, con l'introduzione nei browser dell'oggetto XmlHttpRequest, l'IFRAME è stato disprezzato ancor di più dai "puristi Ajax". In realtà è usato ancora molto, sia per l'inserimento di Widgets, Gadgets e Antipixel nei Blog (se avete un Blog è probabile che la vostra pagina sia piena di "buchi" IFRAME e nemmeno lo sapete), sia per bypassare l'eventuale blocco o mancanza dello stesso oggetto XmlHttpRequest.
Un IFRAME apre di fatto un browser all'interno del browser. Questa finestra IFRAME può visualizzare un dominio esterno ed è accessibile dal codice Javascript presente nella pagina madre. Ecco quindi un modo diverso per bypassare il blocco dell'oggetto XmlHttpRequest.

 

3. Altre tecniche

Esistono poi numerose altre alternative a seconda dei casi e delle circostanze in cui ci troviamo (possibilità di installare tool specifici o manipolare il Web Server a basso livello). Si può usare il mod_rewrite o mod_proxy di apache o librerie come JSON per superare il problema.
Altre tecniche (segui  i link più sotto sul "vedi anche") sono simpatiche varianti, tuttavia alcune hanno restrizioni sui browser che le supportano, quindi attenti. La migliore, almeno a mio parere, rimane l'uso di un semplice proxy server in PHP.

 

4. Flash

Mi permetto di inserire Flash tra le tecniche di superamento del cross-domain, non foss'altro in quanto l'ho citato in un precedente Post: Ajax senza HTTPRequest. Flash, ovviamente, non ha niente a che fare con l'oggetto XHR e, più che mai, niente a che fare con Javascript. Tuttavia bisogna tenere a mente alcuni importanti caratteristiche:

  1. Un filmato Flash può interagire con Javascript e il DOM della pagina Web
  2. Javascript può interagire con un filmato Flash
  3. Adobe AIR è un sistema in cui HTML, Javascript/Ajax e Flash convivono in modo armonico e funzionale

Flash, a differenza dell'oggetto XHR, non ha restrizioni così vincolanti sull'accesso cross-domain. In Flash esistono tutta una serie di caratteristiche volte alla sicurezza ed al controllo dell'accesso a domini differenti da quello in cui sta "girando" il nostro filmato. Tuttavia sono facilmente impostabili da codice e dipendono sostanzialmente dalle scelte dello svilupparore che ha scritto il codice. Accedere, quindi, ad un file RSS di un qualsiasi dominio è, in Flash, cosa assai semplice. Inoltre, invece di usare una pagina PHP come proxy, si può sfruttare la capacità di Javascript di comunicare con Flash e quindi usare quest'ultimo come proxy.

 

Un esempio di PHP proxy server per tutti

Un semplice esempio di come scrivere una pagina PHP che esegue un proxy server minimale, da me usato spesso e volentieri... ;)

PHP:
  1. function getContent ( $url ) {
  2.     $ch = curl_init();
  3.     $timeout = 5;     // set to zero for no timeout
  4.     curl_setopt ($ch, CURLOPT_URL, $url );
  5.     curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
  6.     curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
  7.     $file_contents = curl_exec($ch);
  8.     curl_close($ch);
  9.     // display file
  10.     return ( $file_contents );
  11. }

Questa semplice funzione sfrutta proprio le curl libraries per accedere ad una pagina, che potrebbe essere, ad esempio, un XML Feed RSS. In questo modo una chiamata Ajax  riceverà il risultato tramite questo semplice proxy server PHP.

 

Vedi anche

3 Commenti a “Tunneling e proxy server per Ajax e non solo”

  1. Giovambattista Fazioli Scrive:

    Rilasciato da poco, vedi anche: Ajax Cross Domain

  2. upnews.it Scrive:

    undolog » Blog Archive » Tunneling e proxy server per Ajax e non solo…

    A causa della sua capacità di comunicare con il server, l’oggetto XmlHttpRequest (XHR), usato nella tecnologia Ajax (acronimo di Asynchronous JavaScript and XML, la cui pronuncia dovrebbe essere “egiacsâ€? anche se noi italiani preferiamo “aiacs…

  3. Napolux Scrive:

    Ottimo articolo, me lo “deliciousizzo” al volo :D

Lascia un Commento

XHTML: È possibile utilizzare questi marcatori: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>