Bab 1: Frames & Windows

3 menit baca

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

javascript
// 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

javascript
// 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

javascript
// 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);
javascript
// 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

javascript
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)
⚠️Jebakan!
  1. Popup blocker: Browser blokir popup yang BUKAN dari aksi user langsung (click)
javascript
// ❌ DIBLOKIR - bukan dari aksi user
setTimeout(() => window.open('page.html'), 1000);

// ✅ LOLOS - dari event click
button.onclick = () => window.open('page.html');
  1. 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.

javascript
// ✅ Same origin - bisa akses
// Halaman: https://site.com/page1
// Popup:   https://site.com/page2

// ❌ Cross origin - DIBLOKIR
// Halaman: https://site.com
// Popup:   https://other.com

Pengecualian: Subdomain

Kalau dua halaman punya domain induk yang sama, mereka bisa "sepakat" untuk berkomunikasi:

javascript
// Di halaman forum.site.com:
document.domain = 'site.com';

// Di halaman site.com:
document.domain = 'site.com';

// Sekarang keduanya bisa saling akses!

⚠️ document.domain sudah deprecated di browser modern. Gunakan postMessage sebagai gantinya.

iframe

iframe adalah halaman web di dalam halaman web — kayak TV di dalam TV.

html
<iframe src="https://example.com" id="myFrame" width="500" height="300"></iframe>
javascript
// 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.

javascript
// === 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
javascript
// === 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

javascript
// 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);
    }
  });
}
javascript
// Di halaman popup (setelah OAuth selesai)
// Kirim token ke halaman utama
window.opener.postMessage(
  { type: 'LOGIN_SUCCESS', token: 'abc123xyz' },
  window.location.origin
);
⚠️Jebakan!
  1. Lupa verifikasi origin: SELALU cek event.origin sebelum proses data
  2. Pakai '*' sebagai targetOrigin: Data bisa dibaca siapa saja — bahaya!
  3. iframe belum load: Kirim postMessage setelah iframe onload

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

html
<!-- 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

javascript
// 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

html
<!-- 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 -->
// Cookie tidak dikirim saat di-iframe cross-site Set-Cookie: session=abc123; SameSite=Strict Set-Cookie: session=abc123; SameSite=Lax // default modern browser
⚠️Jebakan!
  1. Hanya pakai JS frame-busting: Bisa di-bypass dengan sandbox attribute
  2. Lupa set header di semua halaman sensitif: Satu halaman tanpa proteksi = celah
  3. Pakai SAMEORIGIN tapi punya subdomain yang vulnerable: Attacker bisa pakai subdomain

🏆 Challenge

Buat sistem komunikasi parent-iframe:

  1. Halaman utama (parent.html) punya input text dan tombol "Kirim"
  2. iframe (child.html) menampilkan pesan yang dikirim
  3. iframe punya tombol "Balas" yang mengirim pesan balik ke parent
  4. WAJIB verifikasi origin di kedua sisi
javascript
// 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.