Oynalar va freymlar

Oynalararo aloqa

Same Origin (bir xil manba) siyosati โ€” bu oyna va freymlarning bir-biriga kirishini cheklovchi asosiy xavfsizlik qoidasidir. U zararli sahifaning boshqa oyna tarkibiga (masalan, sizning bank hisobingiz ochilgan tabga) kirib ma'lumot o'g'irlashiga to'sqinlik qiladi. Ushbu darsda bu siyosat qanday ishlashini, iframe bilan ishlashni va turli manbalar orasida xavfsiz aloqa o'rnatuvchi postMessageni chuqur o'rganamiz.

Bu darsdagi API'lar (iframe.contentWindow, postMessage, message hodisasi va boshqalar) โ€” brauzer muhitiga tegishli. Kurs playground'ida ular ishlamaydi. Barcha kod bloklari faqat statik misol โ€” ularni haqiqiy brauzer sahifasida sinang.

Same Origin (bir xil manba) nima?

Ikki URL bir xil manbaga (origin) ega deyiladi, agar ularning protokoli, domeni va porti bir xil bo'lsa. Quyidagi URL'lar bir xil manbaga tegishli:

Quyidagilar esa boshqa manba hisoblanadi:

Same Origin siyosati shuni bildiradi: agar bizda boshqa oyna (masalan, window.open bilan ochilgan yoki iframedagi) havolasi bo'lsa-yu, u oyna bir xil manbadan bo'lsa โ€” biz uning tarkibiga to'liq kira olamiz. Agar u boshqa manbadan bo'lsa โ€” biz uning URL'ini o'zgartira olamiz (u yerga o'ta olamiz), lekin ichidagi tarkibni (o'zgaruvchilar, DOM) o'qiy olmaymiz.

Bir xil manbadagi oynaga to'liq kirish

Agar boshqa oyna (masalan, popup yoki iframe) bir xil manbadan bo'lsa, biz uning barcha ichki tarkibiga kira olamiz: DOM'ini o'qish/o'zgartirish, o'zgaruvchilarini olish va h.k. Masalan, iframe bilan:

<iframe src="/same-origin.html" id="iframe"></iframe>

<script>
  iframe.onload = function() {
    // ichki oynaga kirish
    let iframeWindow = iframe.contentWindow;

    // uning hujjatini o'qish
    let doc = iframe.contentDocument;
    // yoki: iframe.contentWindow.document

    doc.body.style.background = 'lightgreen'; // ishlaydi (same origin)
  };
</script>

Bu yerda muhim tushunchalar:

iframe.onload hodisasi freym ichidagi hujjat to'liq yuklanganda ishga tushadi. Freym yuklanmagan turib uning contentDocumentiga kirsak, hali eski (bo'sh) hujjatni ko'rishimiz mumkin. Shuning uchun freym tarkibi bilan ishlashni onload ichida qiling.

Boshqa manbadagi oyna: cheklovlar

Agar oyna yoki iframe boshqa manbadan bo'lsa, uning tarkibiga kira olmaymiz. Quyidagi kod xatolik beradi:

<iframe src="https://example.com" id="iframe"></iframe>

<script>
  iframe.onload = function() {
    // boshqa manbadagi iframe hujjatiga kirish โ€” XATOLIK
    let doc = iframe.contentDocument; // null yoki xatolik

    // location o'qishga ham ruxsat yo'q
    let href = iframe.contentWindow.location.href; // Xatolik: SecurityError
  };
</script>

Biroq boshqa manbadagi oynada ham ba'zi narsalarga ruxsat bor:

Same Origin โ€” bu xavfsizlik chegarasi. Uni chetlab o'tishga urinmang. Agar boshqa manbadagi sahifa bilan ma'lumot almashish kerak bo'lsa, buni faqat postMessage orqali โ€” ikkala tomon ham rozi bo'lgan holda qiling.

Subdomenlar: document.domain

Agar ikkita oyna bir asosiy domenning turli subdomenlaridan bo'lsa (masalan, john.site.com va peter.site.com), ular tarixan document.domainni umumiy qiymatga ('site.com') qo'yish orqali bir-biriga kira olishardi:

// john.site.com va peter.site.com sahifalarining
// ikkalasida ham bu bo'lsa, ular bir-biriga kira oladi:
document.domain = 'site.com';
document.domain mexanizmi eskirgan (deprecated) hisoblanadi va zamonaviy brauzerlarda o'chirilmoqda. Yangi kodlarda bunga tayanmang โ€” subdomenlar orasida aloqa uchun ham postMessage ishlatgan ma'qul.

iframe: name va yuklanish nuozansi

Yana bir nozik nuqta: yangi iframe yaratilganda, unda darhol bo'sh hujjat paydo bo'ladi (about:blank), keyin esa srcdagi hujjat yuklanadi. Ya'ni qisqa vaqt ichida ikkita turli hujjat mavjud bo'ladi. Agar biz srcdagi hujjat bilan ishlamoqchi bo'lsak, uning yuklanishini kutishimiz kerak:

<iframe src="document.html" id="iframe"></iframe>

<script>
  // xato: bu vaqtda hali eski (bo'sh) hujjat bo'lishi mumkin
  // iframe.contentDocument.body ...

  iframe.onload = function() {
    // to'g'ri: endi src'dagi hujjat yuklandi
    let body = iframe.contentDocument.body; // (same origin bo'lsa)
  };
</script>

postMessage: manbalararo xavfsiz aloqa

postMessage interfeysi turli manbalardagi oynalarga bir-biri bilan xavfsiz gaplashishga imkon beradi. Bu Same Origin siyosatining maxsus ruxsat berilgan yo'lidir: ikkala oyna ham rozi bo'lgan holda ma'lumot almashadi.

Aloqa ikki qismdan iborat: xabar yuboruvchi va xabar qabul qiluvchi.

Xabar yuborish: postMessage

Xabar yubormoqchi bo'lgan oyna qabul qiluvchi oynaning postMessage metodini chaqiradi:

// win โ€” biz xabar yubormoqchi bo'lgan oyna
// (masalan, iframe.contentWindow yoki window.open natijasi)
win.postMessage(data, targetOrigin);
let win = iframe.contentWindow;

// faqat http://example.com bo'lsa qabul qiladi:
win.postMessage('Salom!', 'http://example.com');

// obyekt ham yuborsa bo'ladi:
win.postMessage({ type: 'greet', text: 'Salom' }, 'http://example.com');
targetOriginni hech qachon ehtiyotsiz '*' qilmang, agar xabarda maxfiy ma'lumot bo'lsa. Aks holda, agar qabul qiluvchi oynaning manbasi kutilmaganda o'zgargan bo'lsa (masalan, boshqa saytga o'tib ketgan bo'lsa), maxfiy ma'lumot begona saytga tushib qolishi mumkin. Har doim aniq manba manzilini bering.

Xabar qabul qilish: message hodisasi

Xabarni qabul qilish uchun qabul qiluvchi oyna message hodisasini tinglaydi:

window.addEventListener('message', function(event) {
  // XAVFSIZLIK: birinchi navbatda manbani tekshiramiz!
  if (event.origin !== 'http://javascript.info') {
    // begona manbadan kelgan xabar โ€” e'tiborsiz qoldiramiz
    return;
  }

  alert('Xabar keldi: ' + event.data);

  // agar javob yubormoqchi bo'lsak:
  event.source.postMessage('Xabaringizni oldim', event.origin);
});

message hodisasi obyekti (event) muhim xossalarga ega:

Muhim nozik jihat: message hodisasi uchun odatiy on<event> xossasi ishlamaydi. Ya'ni window.onmessage = ... qilmang โ€” u ba'zi holatlarda kutilganidek ishlamasligi mumkin. Doim window.addEventListener('message', ...) ishlating.

Origin tekshirish โ€” eng muhim xavfsizlik qoidasi

postMessage xavfsizligining o'zagi โ€” ikki tomonlama tekshiruv:

  1. Yuborishda: targetOrigin orqali xabar faqat kutilgan manbaga borishini kafolatlaysiz;
  2. Qabul qilishda: event.originni tekshirib, faqat ishonchli manbadan kelgan xabarni qabul qilasiz.

Ikkala tomon ham tekshirilishi shart. Agar qabul qiluvchi event.originni tekshirmasa, istalgan sayt (masalan, zararli iframe yoki popup) unga soxta xabar yuborib, uni chalg'itishi mumkin. Masalan, agar sahifangiz kelgan xabarga qarab foydalanuvchi hisobiga o'zgartirish kiritsa, origin tekshiruvi bo'lmasa โ€” bu jiddiy zaiflikka aylanadi.

window.addEventListener('message', function(event) {
  // Ishonchli manbalar ro'yxati
  let allowed = ['https://myapp.com', 'https://admin.myapp.com'];

  if (!allowed.includes(event.origin)) {
    return; // begona โ€” rad etamiz
  }

  // faqat shu yergacha yetgan xabarlar ishonchli
  handleMessage(event.data);
});
Xabar tarkibiga (event.data) ham ishonchsizlik bilan yondashing: uni to'g'ridan-to'g'ri innerHTMLga qo'ymang yoki eval qilmang. Manba to'g'ri bo'lsa ham, ma'lumot formatini tekshirib, faqat kutilgan turdagi qiymatlarni qabul qiling.

Amaliy misol: iframe bilan ikki tomonlama aloqa

Asosiy sahifa (masalan, https://myapp.com) va boshqa manbadagi iframe (https://widget.com) o'rtasida aloqa quyidagicha ko'rinadi.

Asosiy sahifada โ€” iframe'ga xabar yuborish va javobni tinglash:

let iframe = document.querySelector('#widget');

iframe.onload = function() {
  // widget'ga so'rov yuboramiz
  iframe.contentWindow.postMessage(
    { action: 'getUser' },
    'https://widget.com'
  );
};

window.addEventListener('message', function(event) {
  if (event.origin !== 'https://widget.com') return;
  alert('Widget javobi: ' + JSON.stringify(event.data));
});

Iframe ichida (https://widget.com) โ€” so'rovni qabul qilish va javob berish:

window.addEventListener('message', function(event) {
  if (event.origin !== 'https://myapp.com') return;

  if (event.data.action === 'getUser') {
    // asosiy sahifaga javob qaytaramiz
    event.source.postMessage(
      { user: 'John', id: 42 },
      event.origin
    );
  }
});
Bu naqsh (pattern) juda keng tarqalgan: to'lov widjetlari, chat oynalari, reklama freymlari, autentifikatsiya oynalari โ€” barchasi postMessage orqali ishlaydi. Har doim originni ikki tomondan ham tekshiring.

Xulosa