Bab 1: Frames & Windows
1.1 Popup Windows
Apa Itu Popup?
Popup adalah jendela browser baru yang dibuka oleh JavaScript. Dulu popup sering dipakai buat iklan (menyebalkan!), makanya browser modern punya popup blocker.
Analogi: Popup itu kayak buka tab baru, tapi dalam bentuk jendela terpisah. Kayak kamu lagi di toko, terus ada pelayan yang buka pintu ke ruangan lain buat kamu.
Kapan Popup Masih Berguna?
- OAuth login (login via Google/Facebook)
- Payment gateway (halaman pembayaran)
- Preview dokumen
Membuka Popup
// Sintaks dasar
const popup = window.open(url, name, params);
// Contoh: buka popup login
const loginPopup = window.open(
'https://accounts.google.com/login',
'loginWindow',
'width=500,height=600,left=100,top=100'
);
// Cek apakah popup diblokir
if (!loginPopup) {
alert('Popup diblokir! Izinkan popup untuk login.');
}Parameter window.open
// Parameter ketiga mengatur tampilan popup
const params = [
'width=500', // lebar
'height=400', // tinggi
'left=200', // posisi dari kiri layar
'top=100', // posisi dari atas layar
'menubar=no', // sembunyikan menu bar
'toolbar=no', // sembunyikan toolbar
'scrollbars=yes', // tampilkan scrollbar
'resizable=yes' // bisa di-resize
].join(',');
const win = window.open('https://example.com', 'myPopup', params);Komunikasi dengan Popup
// Dari halaman utama → popup
const popup = window.open('popup.html', 'myPopup', 'width=400,height=300');
// Tunggu popup selesai load
popup.onload = function() {
popup.document.body.innerHTML = '<h1>Halo dari halaman utama!</h1>';
};
// Cek apakah popup masih terbuka
const timer = setInterval(() => {
if (popup.closed) {
clearInterval(timer);
console.log('Popup ditutup user');
}
}, 1000);// Dari popup → halaman utama (opener)
// Di dalam popup.html:
const mainWindow = window.opener;
mainWindow.document.title = 'Diubah dari popup!';
// Tutup popup sendiri
window.close();Metode Popup
const popup = window.open('page.html', 'win', 'width=300,height=300');
// Pindahkan popup
popup.moveTo(100, 200); // pindah ke koordinat (100, 200)
popup.moveBy(50, 0); // geser 50px ke kanan
// Resize popup
popup.resizeTo(500, 400); // ubah ukuran jadi 500x400
popup.resizeBy(100, 50); // tambah 100px lebar, 50px tinggi
// Focus / blur
popup.focus(); // bawa popup ke depan
popup.blur(); // kirim popup ke belakang (jarang dipakai)- Popup blocker: Browser blokir popup yang BUKAN dari aksi user langsung (click)
// ❌ DIBLOKIR - bukan dari aksi user
setTimeout(() => window.open('page.html'), 1000);
// ✅ LOLOS - dari event click
button.onclick = () => window.open('page.html');- Same-origin policy: Kamu cuma bisa akses konten popup kalau URL-nya se-origin (domain sama)
1.2 Komunikasi Cross-Window
Same-Origin Policy
Browser punya aturan ketat: kamu cuma bisa akses window lain kalau origin-nya sama (protocol + domain + port harus identik).
Analogi: Kayak apartemen — kamu bebas masuk kamar sendiri, tapi nggak bisa masuk kamar tetangga tanpa izin.
// ✅ Same origin - bisa akses
// Halaman: https://site.com/page1
// Popup: https://site.com/page2
// ❌ Cross origin - DIBLOKIR
// Halaman: https://site.com
// Popup: https://other.comPengecualian: Subdomain
Kalau dua halaman punya domain induk yang sama, mereka bisa "sepakat" untuk berkomunikasi:
// Di halaman forum.site.com:
document.domain = 'site.com';
// Di halaman site.com:
document.domain = 'site.com';
// Sekarang keduanya bisa saling akses!⚠️
document.domainsudah deprecated di browser modern. GunakanpostMessagesebagai gantinya.
iframe
iframe adalah halaman web di dalam halaman web — kayak TV di dalam TV.
<iframe src="https://example.com" id="myFrame" width="500" height="300"></iframe>// Akses iframe dari halaman utama
const iframe = document.getElementById('myFrame');
// Akses window di dalam iframe
const iframeWindow = iframe.contentWindow;
// Akses document di dalam iframe (hanya same-origin!)
const iframeDoc = iframe.contentDocument;
// Dari dalam iframe → akses halaman utama
const parentWindow = window.parent; // satu level ke atas
const topWindow = window.top; // paling atas (kalau nested)postMessage — Komunikasi Aman Cross-Origin
postMessage adalah cara RESMI dan AMAN untuk komunikasi antar window yang beda origin.
Analogi: Kayak kirim surat resmi antar negara — ada amplop (data), alamat tujuan (targetOrigin), dan penerima harus verifikasi pengirim.
// === PENGIRIM (halaman utama) ===
const iframe = document.getElementById('myFrame');
const iframeWin = iframe.contentWindow;
// Kirim pesan ke iframe
iframeWin.postMessage(
{ action: 'login', user: 'Yazid' }, // data (bisa object)
'https://target-site.com' // targetOrigin (WAJIB spesifik!)
);
// ❌ JANGAN pakai '*' di production!
// iframeWin.postMessage(data, '*'); // siapa aja bisa terima = bahaya// === PENERIMA (di dalam iframe) ===
window.addEventListener('message', function(event) {
// SELALU verifikasi pengirim!
if (event.origin !== 'https://trusted-site.com') {
return; // abaikan pesan dari origin tidak dikenal
}
console.log('Data diterima:', event.data);
// { action: 'login', user: 'Yazid' }
// Balas ke pengirim
event.source.postMessage(
{ status: 'ok', message: 'Login berhasil' },
event.origin
);
});Contoh Lengkap: OAuth Login Flow
// Di halaman utama
function loginWithGoogle() {
const popup = window.open(
'/auth/google',
'googleLogin',
'width=500,height=600'
);
// Tunggu pesan dari popup
window.addEventListener('message', function handler(event) {
if (event.origin !== window.location.origin) return;
if (event.data.type === 'LOGIN_SUCCESS') {
console.log('Token:', event.data.token);
popup.close();
window.removeEventListener('message', handler);
}
});
}// Di halaman popup (setelah OAuth selesai)
// Kirim token ke halaman utama
window.opener.postMessage(
{ type: 'LOGIN_SUCCESS', token: 'abc123xyz' },
window.location.origin
);- Lupa verifikasi origin: SELALU cek
event.originsebelum proses data - Pakai
'*'sebagai targetOrigin: Data bisa dibaca siapa saja — bahaya! - iframe belum load: Kirim
postMessagesetelah iframeonload
1.3 Serangan Clickjacking
Apa Itu Clickjacking?
Clickjacking adalah serangan di mana user diklik sesuatu yang BERBEDA dari yang mereka kira. Halaman jahat menaruh iframe transparan di atas tombol palsu.
Analogi: Bayangkan ada kertas transparan di atas tombol ATM. Kamu kira pencet "Cek Saldo", tapi sebenarnya pencet "Transfer 10 Juta".
Cara Kerja Serangan
<!-- Halaman JAHAT (evil.com) -->
<style>
#decoy {
position: relative;
z-index: 1;
}
iframe {
position: absolute;
top: 0;
left: 0;
opacity: 0; /* TRANSPARAN - user nggak lihat */
z-index: 2; /* Di ATAS tombol palsu */
pointer-events: auto;
}
</style>
<button id="decoy">Klik untuk Hadiah Gratis! 🎁</button>
<!-- iframe transparan berisi halaman bank -->
<iframe src="https://bank.com/transfer?to=hacker&amount=10000000"></iframe>User kira klik "Hadiah Gratis", tapi sebenarnya klik tombol "Transfer" di halaman bank yang transparan!
Pertahanan 1: X-Frame-Options (Server-Side)
// HTTP Header dari server
X-Frame-Options: DENY // Tidak boleh di-iframe sama sekali
X-Frame-Options: SAMEORIGIN // Hanya boleh di-iframe oleh origin yang sama
Pertahanan 2: Content-Security-Policy (Modern)
// HTTP Header - lebih fleksibel
Content-Security-Policy: frame-ancestors 'none' // = DENY
Content-Security-Policy: frame-ancestors 'self' // = SAMEORIGIN
Content-Security-Policy: frame-ancestors trusted.com // hanya dari trusted.com
Pertahanan 3: JavaScript Frame-Busting
// Cek apakah halaman kita di-iframe
if (window.top !== window.self) {
// Kita di dalam iframe! Keluar!
window.top.location = window.self.location;
}⚠️ Frame-busting JS bisa di-bypass. Selalu gunakan HTTP header sebagai pertahanan utama.
Pertahanan 4: Atribut sandbox pada iframe
<!-- Batasi kemampuan iframe -->
<iframe src="page.html" sandbox="allow-scripts allow-forms"></iframe>
<!-- Tanpa sandbox value = sangat terbatas -->
<iframe src="page.html" sandbox></iframe>
<!-- Tidak bisa: submit form, run script, navigasi top, popup -->Pertahanan 5: Cookie SameSite
// Cookie tidak dikirim saat di-iframe cross-site
Set-Cookie: session=abc123; SameSite=Strict
Set-Cookie: session=abc123; SameSite=Lax // default modern browser
- Hanya pakai JS frame-busting: Bisa di-bypass dengan
sandboxattribute - Lupa set header di semua halaman sensitif: Satu halaman tanpa proteksi = celah
- Pakai
SAMEORIGINtapi punya subdomain yang vulnerable: Attacker bisa pakai subdomain
🏆 Challenge
Buat sistem komunikasi parent-iframe:
- Halaman utama (
parent.html) punya input text dan tombol "Kirim" - iframe (
child.html) menampilkan pesan yang dikirim - iframe punya tombol "Balas" yang mengirim pesan balik ke parent
- WAJIB verifikasi origin di kedua sisi
// Hint struktur parent.html:
// 1. Buat iframe yang load child.html
// 2. addEventListener('message', ...) untuk terima balasan
// 3. Tombol kirim → iframe.contentWindow.postMessage(...)
// Hint struktur child.html:
// 1. addEventListener('message', ...) untuk terima pesan
// 2. Tampilkan pesan di DOM
// 3. Tombol balas → window.parent.postMessage(...)Sudah paham materi ini?
Tandai sebagai selesai untuk melacak progress-mu.