Cross-domain eseményvezérlés

Ahhoz, hogy a felhasználó oldalán tudjuk átadni az adatokat a modulból a partner oldalára, le kell küzdenünk a same-origin-policy által felállított korlátokat.

Jelen esetben ennek kikerülése nem számítható támadásnak, mivel olyan műveleteket hajtunk végre és olyan adatokhoz férünk hozzá, amikhez a partner oldalon engedélyt kaptunk!

Ehhez együttműködésre van szükség, de mivel a modulunk megjelenik és működéssel bír a partner honlapján, feltételezem, hogy az együttműködésnek kizáró oka nincs.

A modul a tesztkörnyezetünkben nem csinál semmit, csak egy interakciót vár, majd megpróbálja az esemény létrejöttét közölni a partnerrel.

Ha megkeressük a modult tartalmazó keretet a DOM-ban, akkor észrevehetjük, hogy van egy parent tagja. Megpróbálkozhatunk ezen keresztül hozzáférni a szülő dokumentum funkciójához, de értelemszerűen nem lesz jogosultságunk.

<form action="http://szerver.local/process.php" name="urlap">
[...]
<input onclick="raiseEvent();" type="button" value="Elküld!" />
[...]
</form>
<script type="text/javascript">
function raiseEvent () {
  // ide akarmit irunk, a same-origin-policy miatt nem tudunk átnyúlni
  // a partner oldalára, hogy meghívjuk a saveEvent() függvényt
  parent.saveEvent(); // access denied hibával megáll
  document.forms['urlap'].submit();
}
</script>

És itt jön a képbe a keretek egymásbaágyazásának lehetősége. Semmilyen elméleti technikai korlátja nincs a keretek szabad egymásbaágyazásának, ezt inkább a szépérzék, a használhatóság és a hasznosság határozza meg.

Adjuk hozzá a modul kódjához az alábbi sorokat:

[...]
<script type="text/javascript">
function raiseEvent () {
  document.getElementById('if1').src="http://partner.local/saveEventAPI.html";
  document.forms['urlap'].submit();
}
</script>
[...]

Ebben az esetben annyi történik, hogy az Esemény! gomb megnyomásakor a modul betölt egy API-html-t a partner oldaláról. A trükk pedig ennek a tartalmában van:

<script type="text/javascript">
function raiseEventOnPartner () {
  // egy olipmiai ugrással átlépünk az eredeti - legfelső - html szintre
  // és meghívjuk a mentési függvényt
  parent.parent.saveEvent();
}
raiseEventOnPartner();
</script>

Minden keretből képesek vagyunk fellépni a szülő keret szintjére, így a második belső keretből az elsőn keresztül az ablak szintjére tudunk jutni. Mivel ez a két kód már ugyanarról a domain-ről érkezett, ezért a belső hozzáfér a külső kódjaihoz, és végre is tudja hajtani azokat.

Ezzel végre eljutottunk egy megfelelően hatékony megoldáshoz, ahol a mi szerverünk feldolgozza az űrlap adatait, viszont ezen felül semmilyen egyéb – feleslegesnek mondható – tevékenységet nem végez. Az esemény megtörténtének és esetleges részleteinek feldolgozása a felhasználói oldalon, valamint a partnernél történik meg.

XSS api cross domain script

Jogos kérdés, hogy miért nem a belső iframe-en keresztül mentjük el az állapotváltozást, amikor ott is megtehetnénk, ezért felesleges az ugrálás a keretek között.

Ez igaz, de nagy előny a szülő keret elérése abban az esetben, ha a külső html-ben az eseménytől függően változtatásokat kell végrehajtani, lehetőleg az oldal újratöltése nélkül (például AJAX-alapú alkalmazásoknál, illetve ha az eseménnyel együtt paramétereket is átadunk, nem csak a tényt, hogy megtörtént).

(A képekért külön köszönet illeti Jakub Steiner-t, aki elérhetővé tette a darabjait Creative Commons Attribution, Share Alike licensz alatt.)

Oldalak: Előző oldal