Lewati ke konten
RazinSaid

Keamanan Electron: contextIsolation & Praktik Aman

RS
Razin Said
7 menit baca
Bagian 7 dari 12 · Seri Belajar Electron dari Nol sampai Mahir

Keamanan Electron itu topik yang sering banget di-skip orang, padahal taruhannya gede. Soalnya app Electron bukan sekadar halaman web biasa — dia jalan langsung di komputer pengguna, punya akses ke file, jaringan, sampai sistem operasi. Jadi kalau ada celah yang kebuka, satu baris JavaScript jahat aja bisa berubah jadi pintu masuk ke seluruh mesin. Nah, di bab ini kita bakal belajar cara nutup pintu itu rapat-rapat. Tapi sebelum lanjut, enaknya kamu udah ngerti dulu gimana proses-proses di Electron saling ngobrol lewat IPC di Electron, soalnya urusan security sama IPC ini nyambung erat banget.

Kenapa keamanan Electron itu krusial (app punya akses OS)

Di browser biasa, kode sebuah situs hidup dalam kandang yang ketat. Dia nggak bisa baca file di laptop kamu, apalagi jalanin perintah sistem. Electron beda cerita. Main process-nya punya akses penuh ke Node.js dan API sistem operasi.

Coba bayangin skenario ini. App kamu nampilin komentar dari pengguna lain, atau muat gambar dari internet. Kalau salah satu konten itu nyelipin skrip jahat, dan skrip itu punya jalan tembus ke Node.js, penyerang bisa hapus file, nyolong data, bahkan nanem malware.

Intinya gini: di Electron, celah XSS yang di web biasanya "cuma" bikin repot, di sini bisa naik kelas jadi pengambilalihan komputer. Makanya pertahanan berlapis itu bukan pilihan, tapi keharusan.

contextIsolation: tembok antara UI dan Node

Fitur pertama, dan ini yang paling penting, namanya contextIsolation. Sejak Electron versi modern (v12 ke atas, dan masih gitu sampai v28+), nilainya udah default true. Jangan pernah kamu matiin.

Fungsinya apa? Dia misahin dunia JavaScript halaman kamu (renderer) dari dunia JavaScript skrip preload dan API internal Electron. Keduanya jalan di konteks yang beda, ibarat dua ruangan yang dipisah tembok.

Tanpa isolasi ini, kode di halaman bisa ngutak-atik objek bawaan Electron atau Node. Penyerang tinggal numpang di objek global itu. Tapi begitu isolasi nyala, halaman nggak bisa nyentuh internal Electron sama sekali.

const win = new BrowserWindow({
  webPreferences: {
    contextIsolation: true, // default, biarkan tetap true
    preload: path.join(__dirname, 'preload.js'),
  },
});

Aturannya simpel: kalau ada tutorial jadul yang nyuruh kamu nulis contextIsolation: false, ya abaikan aja. Tutorial itu udah usang dan berbahaya.

nodeIntegration: false & sandbox

Dua setelan ini tugasnya nglengkapin contextIsolation.

nodeIntegration itu ngatur boleh-nggaknya halaman renderer ngakses Node.js langsung. Jawabannya: jangan. Biarin nodeIntegration: false (ini juga udah default kok). Dengan begitu, kode di halaman nggak bisa manggil require('fs') terus ngehapus file kamu.

sandbox bakal mbungkus proses renderer dalam kotak pasir milik sistem operasi (mirip yang dipakai Chromium). Renderer yang ke-sandbox aksesnya terbatas banget, bahkan ke API Electron sekalipun. Sejak versi modern, sandbox ini nyala otomatis, tapi nggak ada salahnya ditulis eksplisit.

const win = new BrowserWindow({
  webPreferences: {
    nodeIntegration: false, // halaman tidak boleh sentuh Node
    sandbox: true,          // kurung renderer dalam kotak pasir
    contextIsolation: true,
    preload: path.join(__dirname, 'preload.js'),
  },
});

Kombinasi ketiganya ini fondasi keamanan Electron. Anggap aja kayak sabuk pengaman yang selalu kamu pasang sebelum jalan.

Ekspos API seminimal mungkin lewat preload

Terus, gimana caranya halaman bisa ngelakuin hal berguna kalau semua aksesnya ditutup? Nah, jawabannya ada di skrip preload yang dipaduin sama contextBridge.

Skrip preload ini jalan di jembatan antara dua dunia tadi. Lewat contextBridge.exposeInMainWorld, kamu bisa nyodorin fungsi-fungsi tertentu ke halaman, dengan aman dan terukur.

Kuncinya di sini: ekspos cuma fungsi yang bener-bener dibutuhin. Jangan pernah buka seluruh ipcRenderer ke halaman. Itu sama aja kayak ngasih kunci rumah ke orang asing.

Contoh yang salah (jangan ditiru ya):

// JANGAN seperti ini, terlalu terbuka
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electron', { ipcRenderer });

Contoh yang bener, cuma buka fungsi yang spesifik:

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('api', {
  simpanCatatan: (teks) => ipcRenderer.invoke('catatan:simpan', teks),
  ambilCatatan: () => ipcRenderer.invoke('catatan:ambil'),
});

Di halaman, kamu tinggal panggil window.api.simpanCatatan('halo'). Halaman nggak tau apa-apa soal channel di baliknya, dan nggak bisa manggil channel lain di luar yang kamu izinin. Itu yang namanya prinsip hak akses minimal.

Validasi IPC

Buka API yang sempit doang belum cukup. Di sisi main process, kamu harus mperlakuin tiap pesan IPC kayak masukan dari pihak asing yang nggak bisa dipercaya.

Kenapa? Soalnya renderer bisa aja udah dikuasai penyerang. Jadi jangan pernah percaya gitu aja sama argumen yang dikirim. Cek tipe datanya, cek nilainya, terus tolak apa pun yang keliatan aneh.

const { ipcMain } = require('electron');

ipcMain.handle('catatan:simpan', (event, teks) => {
  // validasi tipe & isi
  if (typeof teks !== 'string' || teks.length > 5000) {
    throw new Error('Input tidak valid');
  }

  // contoh: cegah path traversal jika menyangkut berkas
  const bersih = teks.trim();
  return simpanKeDatabase(bersih);
});

Beberapa kebiasaan baik soal IPC:

  • Pakai nama channel yang spesifik dan konsisten, terus daftarin cuma channel yang emang kepakai.
  • Validasi semua argumen: tipe, panjang, format, sama rentang nilainya.
  • Kalau argumennya berisi nama file atau path, pastiin nggak ada usaha buat keluar dari folder yang diizinin.
  • Jangan balikin data sensitif lebih dari yang halaman butuhin.

Anggap aja main process itu penjaga gerbang. Tugasnya curiga, bukan ramah.

Celah lain yang sering lolos dari perhatian itu soal navigasi. Kalau app kamu tiba-tiba bisa pindah ke situs sembarangan, penyerang punya peluang buat muat kode jahat di dalam jendela app kamu.

Makanya, batasi ke mana app boleh pindah. Tangkep event will-navigate, terus tolak tujuan yang bukan punya kamu.

const { shell } = require('electron');

app.on('web-contents-created', (_, contents) => {
  // batasi navigasi internal
  contents.on('will-navigate', (event, url) => {
    const tujuan = new URL(url);
    if (tujuan.origin !== 'https://app.contohku.id') {
      event.preventDefault();
    }
  });

  // jendela baru: buka di browser, bukan di dalam app
  contents.setWindowOpenHandler(({ url }) => {
    if (url.startsWith('https://')) {
      shell.openExternal(url);
    }
    return { action: 'deny' };
  });
});

Ada dua hal yang perlu kamu perhatiin. Pertama, buat link eksternal, pakai shell.openExternal biar tautannya kebuka di browser sistem, bukan di dalam jendela Electron. Tapi jangan asal terusin semua URL ya — saring dulu, biar cuma skema aman kayak https:// yang boleh lewat. URL berbahaya kayak file:// atau skema aneh lainnya harus ditolak.

Kedua, setWindowOpenHandler itu tugasnya ngganti perilaku window.open. Dengan ngembaliin { action: 'deny' }, kamu nyegah jendela baru muncul begitu aja. Kamu yang pegang kendali, bukan kontennya.

Soal konten remote: hati-hati banget kalau mau muat halaman dari internet ke dalam app. Kalau bisa, muat asetnya secara lokal aja. Kalau kepaksa harus muat konten luar, pastiin lewat https, batasi origin-nya, dan jangan sekali-kali ngasih dia akses ke API privilese.

Checklist keamanan

Sebelum rilis, jalanin daftar ini satu per satu:

  • contextIsolation: true (jangan dimatiin)
  • nodeIntegration: false
  • sandbox: true
  • Pakai skrip preload + contextBridge, ekspos fungsi seminimal mungkin
  • Jangan ekspos seluruh ipcRenderer ke halaman
  • Validasi tiap channel & argumen IPC di main process
  • Batasi navigasi lewat will-navigate
  • Buka link eksternal pakai shell.openExternal, saring skema URL
  • Kendaliin jendela baru pakai setWindowOpenHandler
  • Muat konten lokal kalau bisa; konten remote selalu lewat https
  • Selalu pakai versi Electron terbaru biar dapet tambalan keamanan

Tempel daftar ini di deket meja kerja kamu. Security itu soal kebiasaan, bukan keberuntungan.

Penutup

Keamanan Electron emang nambah kerjaan dikit, tapi hasilnya sepadan banget. Dengan contextIsolation, nodeIntegration: false, dan sandbox, kamu udah mbangun fondasi yang kokoh. Skrip preload yang minimalis njaga biar halaman cuma bisa ngelakuin hal yang kamu izinin. Terus validasi IPC sama kontrol navigasi nutup celah-celah yang masih nyisa.

Inget prinsip utamanya: jangan percaya sama konten, perlakuin tiap masukan sebagai sesuatu yang mencurigakan, dan kasih hak akses sekecil mungkin. App yang aman itu bukan yang paling pinter, tapi yang paling disiplin.

Sekarang app kamu udah lebih tangguh. Langkah berikutnya, ngemas dan ngebagiin dia ke pengguna dengan bener. Lanjut ke package & distribusi aplikasi Electron, atau balik lagi ke seri Belajar Electron buat ngeliat peta lengkap perjalanan kamu.

Bagikan: XWhatsAppFacebookLinkedIn

Artikel terkait