HTTPS만 사용하기
HTTP나 WS는 중간자 공격에 취약하므로, 모든 외부 리소스는 HTTPS를 사용해야 합니다.
// main.js
// Bad: HTTP
mainWindow.loadURL('http://example.com');
// Good: HTTPS
mainWindow.loadURL('https://example.com');
<!-- index.html -->
<!-- Bad -->
<script src="http://cdn.example.com/lib.js"></script>
<!-- Good -->
<script src="https://cdn.example.com/lib.js"></script>
원격 콘텐츠에는 Node.js 비활성화
원격 콘텐츠를 로드할 때 nodeIntegration를 꺼두고, 필요한 기능은 preload.js 스크립트로 제한적으로 제공합니다.
// main.js
const win = new BrowserWindow({
webPreferences: {
nodeIntegration: false, // Node.js 통합 비활성화
contextIsolation: true, // 컨텍스트 격리 활성화
preload: path.join(__dirname, 'preload.js')
}
});
win.loadURL('https://example.com');
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
// 필요한 기능만 안전하게 노출
fetchData: () => ipcRenderer.invoke('fetch-data')
});
컨텍스트 격리 필수 적용
contextIsolation을 활성화해 렌더러와 Electron 환경을 분리, 악성 스크립트의 API 접근을 차단합니다.
// main.js
const win = new BrowserWindow({
webPreferences: {
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
}
});
프로세스 샌드박스 활성화
렌더러 프로세스가 시스템 리소스에 직접 접근하지 못하도록 샌드박스를 적용합니다.
// main.js
app.enableSandbox(); // 앱 전체에 샌드박스 강제 활성화
const win = new BrowserWindow({
webPreferences: {
sandbox: true // 개별 창에서도 명시 가능
}
});
권한 요청 제어
원격 콘텐츠가 권한(알림, 위치 등)을 요청할 때, 반드시 검증 로직을 거쳐야 합니다.
// main.js
const { session } = require('electron');
session.defaultSession.setPermissionRequestHandler((webContents, permission, callback) => {
const url = new URL(webContents.getURL());
// example.com만 허용
if (permission === 'notifications' && url.origin === 'https://example.com') {
return callback(true);
}
callback(false);
});
웹 보안 유지
webSecurity: false는 Same-Origin Policy를 해제하여 보안 구멍을 만듭니다.
// main.js
// Bad
new BrowserWindow({ webPreferences: { webSecurity: false } });
// Good
new BrowserWindow({}); // 기본값 유지
CSP로 콘텐츠 제한
CSP를 통해 허용된 스크립트·스타일 소스를 엄격히 제한합니다. XSS 공격을 방어하는 강력한 수단입니다.
<!-- index.html -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' https://apis.example.com;">
// 또는 main.js에서 http 헤더로 설정
// main.js
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': ["default-src 'self'"]
}
});
});
혼합 콘텐츠 차단
allowRunningInsecureContent를 사용하면 HTTPS 페이지에서 HTTP 리소스 로드를 허용하는 것은 혼합 콘텐츠 취약점을 만듭니다.
// main.js
// Bad
new BrowserWindow({ webPreferences: { allowRunningInsecureContent: true } });
// Good
new BrowserWindow({});
실험적 기능 배제
experimentalFeatures나 enableBlinkFeatures는 보안 위험이 있으므로 사용하지 않습니다.
// main.js
// Bad
new BrowserWindow({ webPreferences: { experimentalFeatures: true } });
// Good
new BrowserWindow({});
WebView 팝업 제한
<webview allowpopups>를 사용하면 외부 페이지가 새 창을 마음대로 생성할 수 있어 위험합니다.
<!-- index.html -->
<!-- Bad -->
<webview allowpopups src="page.html"></webview>
<!-- Good -->
<webview src="page.html"></webview>
WebView 옵션 검증
will-attach-webview 이벤트를 활용해 동적으로 생성되는 <webview>의 옵션을 검사·제어합니다.
// main.js
app.on('web-contents-created', (e, contents) => {
contents.on('will-attach-webview', (event, webPrefs, params) => {
// preload 스크립트 제거
delete webPrefs.preload;
// Node.js 통합 금지
webPrefs.nodeIntegration = false;
// 허용된 URL만 로드
if (!params.src.startsWith('https://example.com/')) {
event.preventDefault();
}
});
});
네비게이션 및 창 제어
will-navigate와 setWindowOpenHandler로 허용된 도메인·URL만 허용하고, 그 외 요청은 차단합니다.
// main.js
app.on('web-contents-created', (e, contents) => {
contents.on('will-navigate', (event, url) => {
if (new URL(url).origin !== 'https://example.com') event.preventDefault();
});
contents.setWindowOpenHandler(({ url }) => {
// 외부 링크는 시스템 브라우저로만 열기
if (url.startsWith('https://example.com')) {
return { action: 'allow' };
}
shell.openExternal(url);
return { action: 'deny' };
});
});
최신 Electron 버전 사용
최신 버전은 Chromium과 Node.js의 보안 패치를 포함하므로 필수입니다.
IPC 메시지 송신자 검증
ipcMain.handle·ipcMain.on으로 수신한 메시지의 event.senderFrame.url을 검증하여, 신뢰된 출처만 허용합니다.
// main.js
ipcMain.handle('get-secret', (event) => {
const origin = new URL(event.senderFrame.url).origin;
if (origin !== 'https://example.com') {
throw new Error('Unauthorized');
}
return getSecretData();
});
Electron API 노출 최소화
contextBridge.exposeInMainWorld로 필요한 메서드만 안전하게 래핑해 노출하고, 원본 ipcRenderer나 Electron API 객체 전체는 절대 노출하지 않습니다.
// preload.js
// Bad
contextBridge.exposeInMainWorld('api', { send: ipcRenderer.send });
// Good
contextBridge.exposeInMainWorld('api', {
doAction: (arg) => ipcRenderer.invoke('do-action', arg)
});
'Electron' 카테고리의 다른 글
[Electron] 키보드 단축키 구현 (0) | 2025.04.07 |
---|---|
[Electron] 성능 최적화: 리소스 효율성과 반응성을 높이는 전략 (0) | 2025.04.07 |
[Electron] MessagePorts: 메시지 채널을 통한 프로세스 간 직접 통신 (0) | 2025.03.24 |
[Electron] 프로세스 샌드박싱: 보안을 위한 실행 격리 (0) | 2025.03.24 |
[Electron] IPC: 프로세스 간 통신으로 앱 기능 확장하기 (0) | 2025.03.24 |