05 — Security (챕터 인덱스)
이 챕터가 답하는 질문: “같은 웹앱이라도 브라우저에서는 안전했던 코드가, Electron 안에서는 왜 OS 전체를 위협하게 되는가? 그 위협을 어떻게 막는가?” 한 줄 답 (Pyramid Top): *“웹은 same-origin으로 격리하지만, Electron은 그 격리가 Node API를 막지 못한다 — 그래서 contextIsolation + sandbox + CSP + 노출 최소화의 *다층 격리(defense in depth)를 직접 켜야 XSS → RCE 경로를 끊는다.”
한 문장 답 (Pyramid Top)
브라우저에서 XSS는 그 origin의 데이터까지만 새는 사고다. Electron에서 XSS는 — Node API가 renderer에 노출돼 있으면 —
require('child_process').exec('rm -rf ~')한 줄로 OS RCE가 된다. 그래서 보안은 하나의 옵션이 아니라 여러 옵션의 교차다 —contextIsolation(JS 컨텍스트 격리),sandbox(Chromium 샌드박스로 Node 제거), CSP(XSS 자체 차단),webPreferences최소 노출, navigation/permission 핸들러 통제, 그리고Electron Fuses(빌드 타임에 위험 옵션 봉인). 이 챕터는 위협 모델(01)에서 시작해 contextIsolation(02), sandbox(03), nodeIntegration/remote(04), CSP & webRequest(05), permissions & navigation(06), 실제 CVE 사례 + 13항목 체크리스트(07)로 7층 다층 격리를 완성한다.
챕터 지도 (Mermaid)
Why — 왜 보안이 별도 챕터인가
브라우저 웹은 디폴트로 격리돼 있다. <iframe> 안의 XSS는 그 origin을 벗어나지 못하고, fetch는 CORS에 막히고, eval은 CSP에 막힌다. 개발자가 아무것도 하지 않아도 격리가 작동한다.
Electron은 디폴트의 의미가 다르다. 같은 <script>alert(1)</script>이 — nodeIntegration: true였던 v4 이전 디폴트에서는 — <script>require('child_process').exec(...)</script>로 바뀔 수 있었다. 그 결과가 Discord(2018), Signal(2018), WhatsApp Desktop(2020), VS Code(2021) 등의 실제 CVE다.
| 위협 | 브라우저에서는 | Electron에서는 | 어디서 다루나 |
|---|---|---|---|
| XSS | document.cookie 탈취, origin 안의 사고 | require('child_process').exec → RCE | 01, 07 |
| 외부 navigation | 그냥 새 페이지 로드 | renderer가 공격자 origin으로 이동 후 IPC 호출 | 06 |
new-window | popup 차단 정도 | file:// 로컬 HTML로 nodeIntegration 켜진 창이 뜸 | 06 |
| preload 누설 | 해당 없음 | preload의 Node 객체가 renderer로 새면 끝 | 02 |
| CSP 누락 | XSS 위험 | XSS = RCE 위험 | 05 |
| 권한 prompt | 브라우저 UI가 띄움 | 디폴트로 모두 허용 — 개발자가 핸들러 등록 안 하면 mic·camera·geolocation 다 통과 | 06 |
여기서 핵심은 공격 표면의 비대칭성이다. 브라우저는 자기가 격리를 책임지지만, Electron의 격리는 개발자가 직접 켜야 한다. 그래서 보안이 별도 챕터이며, 체크리스트가 13개나 된다.
본문 7개 인덱스
| # | 파일 | 다루는 주제 | 핵심 키워드 |
|---|---|---|---|
| 01 | Threat Model | Electron 위협 모델 — XSS → RCE 경로, 웹 vs Electron 위협 비교 | XSS · RCE · prototype pollution · supply chain · main vs renderer |
| 02 | Context Isolation | contextIsolation: true의 의미 — renderer JS와 preload JS가 다른 V8 컨텍스트에 사는 이유 | V8 isolate · world · contextBridge · prototype pollution 차단 |
| 03 | Sandbox | Chromium sandbox로 Node API 제거 — preload에서 무엇이 되고 무엇이 안 되는가, v20+ 디폴트 true | seccomp · setuid sandbox · sandboxed preload · process.contextIsolated |
| 04 | nodeIntegration & remote | nodeIntegration: false가 디폴트인 이유, remote 모듈 제거 역사 — 근본적으로 노출하지 말 것 | nodeIntegration · @electron/remote · enableRemoteModule · v14 제거 |
| 05 | CSP & webRequest | Content-Security-Policy 설정, session.webRequest.onHeadersReceived로 헤더 강제, webSecurity: false 금기 | CSP · unsafe-inline · onHeadersReceived · webSecurity · CORS |
| 06 | Permissions & Navigation | setPermissionRequestHandler (camera/mic/geolocation), will-navigate/setWindowOpenHandler — 외부 URL 차단 | setPermissionRequestHandler · will-navigate · setWindowOpenHandler · shell.openExternal |
| 07 | Known CVEs & Checklist | 실제 CVE 사례 5~7개 (Discord·WhatsApp·Signal·VS Code), Electron Security Checklist 13항목 전수 | CVE-2018-15685 · CVE-2020-15174 · electronegativity · securify · Electron Fuses |
What-if — Checklist 누락이 부르는 사고
다음은 실제로 일어난 사고의 패턴이다. 자세한 CVE 번호와 분석은 07에 있다.
| 누락 | 어떤 사고 | 사례 |
|---|---|---|
nodeIntegration: false 미설정 | <iframe> XSS 한 방 → require('child_process') | Discord 2018 (CVE-2018-15685 — 외부 iframe RCE) |
contextIsolation: true 미설정 | preload의 객체에 prototype pollution → Object.prototype 오염 → renderer가 Node API 흭득 | Signal 2018 (CVE-2018-10994) |
| CSP 미설정 | XSS → 인라인 스크립트 실행 → IPC 채널 학습 → RCE | WhatsApp Desktop 2020 (CVE-2020-15174 — mailto: link XSS) |
will-navigate 미통제 | 사용자가 외부 origin으로 끌려가서 renderer가 공격자 서버에 IPC 전송 | Rocket.Chat 2020 (CVE-2020-15920) |
setWindowOpenHandler 미통제 | target=_blank 새 창에 nodeIntegration 유지 | Atom 2017 (CVE-2017-1002101 흐름) |
shell.openExternal(userInput) | URL 인자 검증 누락 → file:/// 경로 실행 | VS Code 2021 (CVE-2021-43908 흐름) |
webSecurity: false 켜둠 | CORS 무력화 → 임의 origin의 JS가 로컬에 접근 | 다수의 학습용 sample 그대로 production 사용 |
이 표가 07의 13항목 체크리스트가 왜 그 13개인지의 답이다. 하나하나가 실제로 누군가 당한 적이 있는 항목이다.
어떻게 읽나
- 새 프로젝트 시작 직전: 01 → 07. 한 시간이면 어떤 디폴트를 깔고 시작할지 결정 가능.
- 기존 앱 보안 리뷰: 01 → 04 → 05 → 06 → 07. 현재 깔린 옵션과 체크리스트를 일일이 대조.
- 이미 사고가 났을 때: 07부터. 비슷한 CVE 패턴을 먼저 찾는다.
- 개념만 정리하고 싶을 때: 02 → 03. contextIsolation과 sandbox가 가장 자주 헷갈리는 두 단어다.
한 단락 요약
Electron 보안은 하나의 옵션이 아니라 다층 격리다.
contextIsolation(JS 컨텍스트 분리) +sandbox(Node 제거) + CSP(XSS 자체 차단) +nodeIntegration: false(노출 최소화) + navigation/permission 핸들러(외부 통제) + Electron Fuses(빌드 타임 봉인)의 6+1층이 함께 켜져야 비로소 XSS → RCE 경로가 끊어진다. 이 챕터를 끝내면 “Electron은 안전한가요?”라는 질문 대신 *“이 13항목 중 몇 개를 켰는가? 그리고 어떤 Fuse로 봉인했는가?”*라는 질문을 던지게 된다.