Electron contextIsolation + preload 보안 패턴

문제

Electron에서 nodeIntegration: true로 하면 렌더러(웹페이지)에서 require('fs')로 파일 시스템에 직접 접근할 수 있다. 외부 URL을 로드하는 경우 심각한 보안 위험이 된다.

해결

// main.js - 메인 프로세스
const mainWindow = new BrowserWindow({
  webPreferences: {
    preload: path.join(__dirname, 'preload.js'),
    contextIsolation: true,   // 렌더러와 Node.js 컨텍스트 분리
    nodeIntegration: false,   // 렌더러에서 require 차단
  }
});
// preload.js - 브릿지 역할
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  scanNetwork: () => ipcRenderer.invoke('scan-network'),
  getVersion: () => ipcRenderer.invoke('get-version'),
  onScanResult: (callback) =>
    ipcRenderer.on('scan-result', (_, data) => callback(data)),
});
// renderer.js - 웹페이지에서 사용
const results = await window.electronAPI.scanNetwork();

핵심 포인트

  • contextIsolation: true면 preload 스크립트의 전역 객체와 렌더러의 전역 객체가 완전히 분리된다. contextBridge.exposeInMainWorld로 허용된 함수만 노출한다.
  • 렌더러에서 할 수 있는 건 오직 window.electronAPI에 정의된 것뿐이다. require('child_process') 같은 위험한 접근은 불가능하다.
  • ipcRenderer.invoke는 Promise를 반환하는 양방향 통신이다. 메인 프로세스에서 ipcMain.handle로 받아서 처리한다.