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.
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:
http://site.comhttp://site.com/http://site.com/my/page.html
Quyidagilar esa boshqa manba hisoblanadi:
http://www.site.comโ boshqa domen (subdomen ham hisobga olinadi);http://site.orgโ boshqa domen;https://site.comโ boshqa protokol;http://site.com:8080โ boshqa port.
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.contentWindowโiframeichidagiwindowobyektiga havola;iframe.contentDocumentโiframeichidagidocument(iframe.contentWindow.documentbilan bir xil, faqat qisqaroq).
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:
iframe.contentWindow.location = '...'โ boshqa oynaning URL'ini yozish (uni boshqa manzilga o'tkazish) mumkin. Ammo o'qish mumkin emas;iframe.contentWindow.postMessage(...)โ xabar yuborish mumkin (bu haqda quyida).
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);
dataโ yuboriladigan ma'lumot. U structured clone algoritmi bilan nusxalanadi, ya'ni obyektlar, massivlar va boshqa turlar ham yuborilishi mumkin (funksiyalardan tashqari);targetOriginโ qabul qiluvchi oynaning kutilayotgan manbasi. Bu xavfsizlik uchun juda muhim: faqat shu manbadagi oyna xabarni oladi.'*'berilsa โ istalgan manba oladi (xavfsiz emas).
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:
event.dataโpostMessagedan kelgan ma'lumot;event.originโ xabar yuboruvchi oynaning manbasi (masalan,'http://javascript.info'). Buni tekshirish shart;event.sourceโ xabar yuboruvchi oynaga havola. UningpostMessageini chaqirib javob qaytarsa bo'ladi.
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:
- Yuborishda:
targetOriginorqali xabar faqat kutilgan manbaga borishini kafolatlaysiz; - 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);
});
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
);
}
});
postMessage orqali ishlaydi. Har doim originni ikki tomondan ham tekshiring.Xulosa
- Same Origin siyosati protokol, domen va port bir xil bo'lgan sahifalarga bir-birining tarkibiga to'liq kirishga ruxsat beradi;
- Boshqa manbadagi oynaga faqat
locationni yozish vapostMessageyuborish mumkin โ tarkibni o'qib bo'lmaydi; iframe.contentWindowvaiframe.contentDocumentorqali (same origin bo'lsa) freym ichiga kiriladi; freym tarkibinionloadichida ishlating;win.postMessage(data, targetOrigin)โ turli manbalar orasida xabar yuboradi;targetOriginni aniq bering;- Xabar
messagehodisasi orqali qabul qilinadi;event.data,event.origin,event.sourcexossalari muhim; - Eng muhim qoida โ doim
event.originni tekshiring va xabar ma'lumotiga ishonchsizlik bilan yondashing.