JSONP és Cross-Site Scripting

Aki próbált már JavaScript-ből idegen szerverrel kommunikálni, az valószínűleg belefutott már a Cross-Site Scripting problémakörbe. Röviden arról van szó, hogy biztonsági okokból egy oldalon lévő JavaScript csak az oldalt tartalmazó domain-el képes kommunikálni (onnan bármit letölteni, vagy küldeni oda). Ez bizonyára sok gonoszságtól megvéd minket, mégis vannak helyzetek, mikor jól jönne, ha idegen domainről tudnánk adatokat letölteni. Például RSS feed-et szeretnénk olvasni JavaScript-ből, vagy valami távoli szolgáltatást szeretnénk valami API-n keresztül elérni. A napokban én is egy hasonló problémával találtam szemben magam. Nevezetesen volt két oldalam (mondjuk foo.com és bar.com). Az egyiken leraktam egy cookie-t, amit a másik oldalon szerettem volna kiolvasni, és mindezt JavaScript-ből. Mivel a bar.com oldal nem láthatja a foo.com oldal cookie-jait, ezért ezt csak úgy lehet megoldani, hogy a foo.com kiolvassa a saját cookie-ját, és valahogy átadja ezt a bar.com-nak. A baj csak az, hogy a bar.com-on lévő JavaScript nem tudja semmiképp elérni a foo.com oldalt. Több dologgal próbálkoztam, iframe-el, megpróbáltam a foo.com-ról betölteni a JavaScript-et, stb. de semmi nem vezetett eredményre. Ekkor találtam rá a JSONP-re.

A JSONP igazából egy igen egyszerű kis trükk a fenti probléma megkerülésére. Ha egy ideig szenved vele az ember, valószínűleg egy idő után magától is kitalálja. A JSONP-nek annyi a lényege, hogy úgy adunk át adatokat a foo.com-ról a bar.com-nak, hogy foo.com az átadandó adatokból generál egy JavaScript-et, és ezt a bar.com egy egyszerű script taggal behúzza. Mindebből úgy alakíthatunk ki kétirányú kommunikációt, hogy a kliens URL paramétereket használ az adatok továbbítására, amire a szerver a megfelelő javascript generálásával válaszol. Hogy mindez tisztább legyen, nézzünk egy példát. Tekintsük a fenti problémát, vagyis a foo.com domainről szeretnénk lekérni a cookie megadott nevű mezőjének értékét. Ehhez hozzunk létre egy cookie.php állományt a foo.com domainen, amit a következőképpen paraméterezhetünk: foo.com/cookie.php?cookie_name=<változó neve>&callback=<javascript fv.>. Itt a változó neve ugye egyértelmű, a callback paramétert meg nemsokára megértjük. A cookie.php valahogy így fog kinézni:

<?php
$cookie_name = $_GET['cookie_name'];
$data = array();
$data[$cookie_name] = $_COOKIE[$cookie_name];
echo $_GET['callback'] . '(' . json_encode($data) . ');';
?>

ha a fenti scriptet cookie_name=test és callback=jsonp_callback paraméterrel hívjuk, az eredmény valami ilyesmi lesz:

jsonp_callback({'test': 'Test cookie value.'});

Ez pedig egy teljesen szabványos javascript hívás. Nincs más dolgunk,mint a fenti url-t egy script tag-el behúzni a hívó oldalra:

<script type="text/javascript" src="http://foo.com/cookie.php?cookie_name=test&callback=jsonp_callback"></script>

A script tag tartalmát a böngésző azonnal végrehajtja, ami ugye így azt eredményezi, hogy meghívódik a jsonp_callback függvény a {‘test’: ‘Test cookie value.’} paraméterrel. Nincs más dolgunk, mint definiálni a jsonp_callback fv.-t, és kész is vagyunk:

function jsonp_callback(data) {
alert(data.test);
}

Igazából ennyi az egész. Persze a gyakorlatban általában nem fix paraméterekkel akarunk hívni egy szerver szolgáltatást. A dinamikus paraméterezéshez dinamikusan kell előállítanunk a script tag-eket, ez DOM műveletekkel viszonylag egyszerűen megvalósítható, de van egy még kényelmesebb megoldás, ez pedig a jquery javascript library ajax hívása. Ezzel a megoldással egy egyszerű függvényhívásra van csak szükség, ami valahogy így néz ki:

$.ajax({
dataType: 'jsonp',
data: 'cookie_name=test'
url: 'http://foo.com/cookie.php',
success: function (data) {
alert(data.test);
},
});

Így tehát JQuery használatával, és némi PHP ismerettel már egész kényelmes Cross-Site Scripting megoldást készíthetünk. Akit a JSONP mélyebben érdekel, az utánanézhet a wikipediában.

Hozzászólás írása