Izpētiet drošu starpdomēnu komunikāciju, izmantojot PostMessage API. Uzziniet par tās iespējām, drošības riskiem un labāko praksi ievainojamību mazināšanai tīmekļa lietojumprogrammās.
Starpdomēnu Komunikācija: Drošības Modeļi ar PostMessage API
Mūsdienu tīmeklī lietojumprogrammām bieži ir nepieciešams mijiedarboties ar resursiem no dažādām izcelsmēm. Vienas izcelsmes politika (Same-Origin Policy - SOP) ir būtisks drošības mehānisms, kas ierobežo skriptu piekļuvi resursiem no citas izcelsmes. Tomēr ir likumīgi scenāriji, kuros starpdomēnu komunikācija ir nepieciešama. postMessage API nodrošina kontrolētu mehānismu šī mērķa sasniegšanai, taču ir ļoti svarīgi saprast tās potenciālos drošības riskus un ieviest atbilstošus drošības modeļus.
Izpratne par Vienas Izcelsmes Politiku (SOP)
Vienas izcelsmes politika ir fundamentāls drošības jēdziens tīmekļa pārlūkprogrammās. Tā ierobežo tīmekļa lapas no pieprasījumu veikšanas uz citu domēnu, nekā tas, kas apkalpoja tīmekļa lapu. Izcelsmi nosaka shēma (protokols), resursdators (domēns) un ports. Ja kāds no šiem atšķiras, izcelsmes tiek uzskatītas par dažādām. Piemēram:
https://example.comhttps://www.example.comhttp://example.comhttps://example.com:8080
Šīs visas ir dažādas izcelsmes, un SOP ierobežo tiešu skriptu piekļuvi starp tām.
Iepazīstinām ar PostMessage API
postMessage API nodrošina drošu un kontrolētu mehānismu starpdomēnu komunikācijai. Tā ļauj skriptiem sūtīt ziņojumus uz citiem logiem (piemēram, iframe, jauniem logiem vai cilnēm), neatkarīgi no to izcelsmes. Saņemošais logs var klausīties šos ziņojumus un attiecīgi tos apstrādāt.
Pamata sintakse ziņojuma sūtīšanai ir:
otherWindow.postMessage(message, targetOrigin);
otherWindow: Atsauce uz mērķa logu (piemēram,window.parent,iframe.contentWindowvai loga objekts, kas iegūts nowindow.open).message: Dati, ko vēlaties nosūtīt. Tas var būt jebkurš JavaScript objekts, ko var serializēt (piemēram, virknes, skaitļi, objekti, masīvi).targetOrigin: Norāda izcelsmi, uz kuru vēlaties nosūtīt ziņojumu. Šis ir būtisks drošības parametrs.
Saņēmēja pusē ir jāklausās message notikums:
window.addEventListener('message', function(event) {
// ...
});
event objekts satur šādas īpašības:
event.data: Ziņojums, ko nosūtījis otrs logs.event.origin: Tā loga izcelsme, kas nosūtīja ziņojumu.event.source: Atsauce uz logu, kas nosūtīja ziņojumu.
Drošības Riski un Ievainojamības
Lai gan postMessage piedāvā veidu, kā apiet SOP ierobežojumus, tas arī rada potenciālus drošības riskus, ja netiek rūpīgi ieviests. Šeit ir dažas izplatītas ievainojamības:
1. Mērķa Izcelsmes Neatbilstība
event.origin īpašības nevalidēšana ir kritiska ievainojamība. Ja saņēmējs akli uzticas ziņojumam, jebkura vietne var sūtīt ļaunprātīgus datus. Vienmēr pārbaudiet, vai event.origin atbilst gaidītajai izcelsmei, pirms apstrādājat ziņojumu.
Piemērs (Ievainojams Kods):
window.addEventListener('message', function(event) {
// NEDARIET TĀ!
processMessage(event.data);
});
Piemērs (Drošs Kods):
window.addEventListener('message', function(event) {
if (event.origin !== 'https://trusted-origin.com') {
console.warn('Saņemts ziņojums no neuzticamas izcelsmes:', event.origin);
return;
}
processMessage(event.data);
});
2. Datu Injekcija
Saņemto datu (event.data) uzskatīšana par izpildāmu kodu vai to tieša ievietošana DOM var novest pie starpvietņu skriptošanas (XSS) ievainojamībām. Vienmēr attīriet un validējiet saņemtos datus pirms to izmantošanas.
Piemērs (Ievainojams Kods):
window.addEventListener('message', function(event) {
if (event.origin === 'https://trusted-origin.com') {
document.body.innerHTML = event.data; // NEDARIET TĀ!
}
});
Piemērs (Drošs Kods):
window.addEventListener('message', function(event) {
if (event.origin === 'https://trusted-origin.com') {
const sanitizedData = sanitize(event.data); // Ieviesiet pareizu datu attīrīšanas funkciju
document.getElementById('message-container').textContent = sanitizedData;
}
});
function sanitize(data) {
// Šeit ieviesiet robustu datu attīrīšanas loģiku.
// Piemēram, izmantojiet DOMPurify vai līdzīgu bibliotēku
return DOMPurify.sanitize(data);
}
3. Man-in-the-Middle (MITM) Uzbrukumi
Ja komunikācija notiek pa nedrošu kanālu (HTTP), MITM uzbrucējs var pārtvert un modificēt ziņojumus. Drošai komunikācijai vienmēr izmantojiet HTTPS.
4. Starpvietņu Pieprasījumu Viltošana (CSRF)
Ja saņēmējs veic darbības, pamatojoties uz saņemto ziņojumu, bez pienācīgas validācijas, uzbrucējs varētu viltot ziņojumus, lai apmānītu saņēmēju veikt neparedzētas darbības. Ieviesiet CSRF aizsardzības mehānismus, piemēram, iekļaujot slepenu marķieri (token) ziņojumā un pārbaudot to saņēmēja pusē.
5. Aizstājējzīmju (Wildcards) Izmantošana targetOrigin
Iestatot targetOrigin uz *, jebkura izcelsme var saņemt ziņojumu. No tā jāizvairās, ja vien tas nav absolūti nepieciešams, jo tas mazina izcelsmes balstītas drošības jēgu. Ja jums tomēr jāizmanto *, nodrošiniet, ka esat ieviesis citus spēcīgus drošības pasākumus, piemēram, ziņojumu autentifikācijas kodus (MACs).
Piemērs (Izvairieties no šī):
otherWindow.postMessage(message, '*'); // Izvairieties no '*' izmantošanas, ja vien tas nav absolūti nepieciešams
Drošības Modeļi un Labākā Prakse
Lai mazinātu riskus, kas saistīti ar postMessage, ievērojiet šos drošības modeļus un labāko praksi:
1. Stingra Izcelsmes Validācija
Vienmēr validējiet event.origin īpašību saņēmēja pusē. Salīdziniet to ar iepriekš definētu uzticamu izcelsmju sarakstu. Salīdzināšanai izmantojiet stingro vienādību (===).
2. Datu Attīrīšana un Validācija
Attīriet un validējiet visus datus, kas saņemti caur postMessage, pirms to izmantošanas. Izmantojiet atbilstošas datu attīrīšanas metodes atkarībā no tā, kā dati tiks izmantoti (piemēram, HTML atsoļa zīmju aizstāšana, URL kodēšana, ievades validācija). HTML attīrīšanai izmantojiet bibliotēkas, piemēram, DOMPurify.
3. Ziņojumu Autentifikācijas Kodi (MACs)
Iekļaujiet ziņojumā ziņojuma autentifikācijas kodu (MAC), lai nodrošinātu tā integritāti un autentiskumu. Sūtītājs aprēķina MAC, izmantojot kopīgu slepeno atslēgu, un iekļauj to ziņojumā. Saņēmējs pārrēķina MAC, izmantojot to pašu kopīgo slepeno atslēgu, un salīdzina to ar saņemto MAC. Ja tie sakrīt, ziņojums tiek uzskatīts par autentisku un nemainītu.
Piemērs (Izmantojot HMAC-SHA256):
// Sūtītājs
async function sendMessage(message, targetOrigin, sharedSecret) {
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify(message));
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(sharedSecret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signature = await crypto.subtle.sign("HMAC", key, data);
const signatureArray = Array.from(new Uint8Array(signature));
const signatureHex = signatureArray.map(b => b.toString(16).padStart(2, '0')).join('');
const securedMessage = {
data: message,
signature: signatureHex
};
otherWindow.postMessage(securedMessage, targetOrigin);
}
// Saņēmējs
async function receiveMessage(event, sharedSecret) {
if (event.origin !== 'https://trusted-origin.com') {
console.warn('Saņemts ziņojums no neuzticamas izcelsmes:', event.origin);
return;
}
const securedMessage = event.data;
const message = securedMessage.data;
const receivedSignature = securedMessage.signature;
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify(message));
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(sharedSecret),
{ name: "HMAC", hash: "SHA-256" },
false,
["verify"]
);
const signature = await crypto.subtle.sign("HMAC", key, data);
const signatureArray = Array.from(new Uint8Array(signature));
const signatureHex = signatureArray.map(b => b.toString(16).padStart(2, '0')).join('');
if (signatureHex === receivedSignature) {
console.log('Ziņojums ir autentisks!');
processMessage(message); // Turpināt ziņojuma apstrādi
} else {
console.error('Ziņojuma paraksta verifikācija neizdevās!');
}
}
Svarīgi: Kopīgā slepenā atslēga ir droši jāģenerē un jāuzglabā. Izvairieties no atslēgas iekodēšanas tieši kodā.
4. Nonce un Laika Zīmogu Izmantošana
Lai novērstu atkārtošanas uzbrukumus, iekļaujiet ziņojumā unikālu nonce (vienreiz lietojams numurs) un laika zīmogu. Saņēmējs var pārbaudīt, vai nonce nav izmantots iepriekš un vai laika zīmogs ir pieņemamā laika posmā. Tas samazina risku, ka uzbrucējs atkārtoti nosūtīs iepriekš pārtvertus ziņojumus.
5. Mazāko Privilēģiju Princips
Piešķiriet otram logam tikai minimāli nepieciešamās privilēģijas. Piemēram, ja otram logam nepieciešams tikai lasīt datus, neļaujiet tam rakstīt datus. Izstrādājiet savu komunikācijas protokolu, paturot prātā mazāko privilēģiju principu.
6. Satura Drošības Politika (CSP)
Izmantojiet satura drošības politiku (CSP), lai ierobežotu avotus, no kuriem var ielādēt skriptus, un darbības, ko skripti var veikt. Tas var palīdzēt mazināt XSS ievainojamību ietekmi, kas varētu rasties no nepareizas postMessage datu apstrādes.
7. Ievades Validācija
Vienmēr validējiet saņemto datu struktūru un formātu. Definējiet skaidru ziņojuma formātu un nodrošiniet, ka saņemtie dati atbilst šim formātam. Tas palīdz novērst neparedzētu uzvedību un ievainojamības.
8. Droša Datu Serializācija
Ziņojumu serializēšanai un deserializēšanai izmantojiet drošu datu serializācijas formātu, piemēram, JSON. Izvairieties no formātu izmantošanas, kas ļauj izpildīt kodu, piemēram, eval() vai Function().
9. Ierobežojiet Ziņojuma Lielumu
Ierobežojiet ziņojumu lielumu, kas tiek sūtīti caur postMessage. Lieli ziņojumi var patērēt pārmērīgus resursus un potenciāli novest pie pakalpojuma atteikuma uzbrukumiem.
10. Regulāri Drošības Auditi
Veiciet regulārus sava koda drošības auditus, lai identificētu un novērstu potenciālās ievainojamības. Pievērsiet īpašu uzmanību postMessage ieviešanai un nodrošiniet, ka tiek ievērota visa labākā drošības prakse.
Piemēra Scenārijs: Droša Komunikācija starp Iframe un tā Vecāklapu
Apsveriet scenāriju, kurā iframe, kas mitināts https://iframe.example.com, ir nepieciešams sazināties ar tā vecāklapu, kas mitināta https://parent.example.com. Iframe ir jānosūta lietotāja dati uz vecāklapu apstrādei.
Iframe (https://iframe.example.com):
// Ģenerējiet kopīgu slepeno atslēgu (aizstājiet ar drošu atslēgas ģenerēšanas metodi)
const sharedSecret = 'YOUR_SECURE_SHARED_SECRET';
// Iegūstiet lietotāja datus
const userData = {
name: 'John Doe',
email: 'john.doe@example.com'
};
// Nosūtiet lietotāja datus uz vecāklapu
async function sendUserData(userData) {
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify(userData));
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(sharedSecret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signature = await crypto.subtle.sign("HMAC", key, data);
const signatureArray = Array.from(new Uint8Array(signature));
const signatureHex = signatureArray.map(b => b.toString(16).padStart(2, '0')).join('');
const securedMessage = {
data: userData,
signature: signatureHex
};
parent.postMessage(securedMessage, 'https://parent.example.com');
}
sendUserData(userData);
Vecāklapa (https://parent.example.com):
// Kopīgā slepenā atslēga (jāsakrīt ar iframe atslēgu)
const sharedSecret = 'YOUR_SECURE_SHARED_SECRET';
window.addEventListener('message', async function(event) {
if (event.origin !== 'https://iframe.example.com') {
console.warn('Saņemts ziņojums no neuzticamas izcelsmes:', event.origin);
return;
}
const securedMessage = event.data;
const userData = securedMessage.data;
const receivedSignature = securedMessage.signature;
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify(userData));
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(sharedSecret),
{ name: "HMAC", hash: "SHA-256" },
false,
["verify"]
);
const signature = await crypto.subtle.sign("HMAC", key, data);
const signatureArray = Array.from(new Uint8Array(signature));
const signatureHex = signatureArray.map(b => b.toString(16).padStart(2, '0')).join('');
if (signatureHex === receivedSignature) {
console.log('Ziņojums ir autentisks!');
// Apstrādājiet lietotāja datus
console.log('Lietotāja dati:', userData);
} else {
console.error('Ziņojuma paraksta verifikācija neizdevās!');
}
});
Svarīgas Piezīmes:
- Aizstājiet
YOUR_SECURE_SHARED_SECRETar droši ģenerētu kopīgo slepeno atslēgu. - Kopīgajai slepenajai atslēgai ir jābūt vienādai gan iframe, gan vecāklapā.
- Šis piemērs izmanto HMAC-SHA256 ziņojumu autentifikācijai.
Noslēgums
postMessage API ir spēcīgs rīks, kas nodrošina starpdomēnu komunikāciju tīmekļa lietojumprogrammās. Tomēr ir ļoti svarīgi saprast potenciālos drošības riskus un ieviest atbilstošus drošības modeļus, lai šos riskus mazinātu. Ievērojot šajā rokasgrāmatā aprakstītos drošības modeļus un labāko praksi, jūs varat droši izmantot postMessage, lai veidotu stabilas un drošas tīmekļa lietojumprogrammas.
Atcerieties vienmēr piešķirt prioritāti drošībai un sekot līdzi jaunākajai drošības praksei tīmekļa izstrādē. Regulāri pārskatiet savu kodu un drošības konfigurācijas, lai nodrošinātu, ka jūsu lietojumprogrammas ir aizsargātas pret potenciālām ievainojamībām.