⚡ Electron5. 보안 모델📖 개요

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에서는어디서 다루나
XSSdocument.cookie 탈취, origin 안의 사고require('child_process').exec → RCE01, 07
외부 navigation그냥 새 페이지 로드renderer가 공격자 origin으로 이동 후 IPC 호출06
new-windowpopup 차단 정도file:// 로컬 HTML로 nodeIntegration 켜진 창이 뜸06
preload 누설해당 없음preload의 Node 객체가 renderer로 새면 끝02
CSP 누락XSS 위험XSS = RCE 위험05
권한 prompt브라우저 UI가 띄움디폴트로 모두 허용 — 개발자가 핸들러 등록 안 하면 mic·camera·geolocation 다 통과06

여기서 핵심은 공격 표면의 비대칭성이다. 브라우저는 자기가 격리를 책임지지만, Electron의 격리는 개발자가 직접 켜야 한다. 그래서 보안이 별도 챕터이며, 체크리스트가 13개나 된다.


본문 7개 인덱스

#파일다루는 주제핵심 키워드
01Threat ModelElectron 위협 모델 — XSS → RCE 경로, 웹 vs Electron 위협 비교XSS · RCE · prototype pollution · supply chain · main vs renderer
02Context IsolationcontextIsolation: true의 의미 — renderer JS와 preload JS가 다른 V8 컨텍스트에 사는 이유V8 isolate · world · contextBridge · prototype pollution 차단
03SandboxChromium sandbox로 Node API 제거 — preload에서 무엇이 되고 무엇이 안 되는가, v20+ 디폴트 trueseccomp · setuid sandbox · sandboxed preload · process.contextIsolated
04nodeIntegration & remotenodeIntegration: false가 디폴트인 이유, remote 모듈 제거 역사 — 근본적으로 노출하지 말 것nodeIntegration · @electron/remote · enableRemoteModule · v14 제거
05CSP & webRequestContent-Security-Policy 설정, session.webRequest.onHeadersReceived로 헤더 강제, webSecurity: false 금기CSP · unsafe-inline · onHeadersReceived · webSecurity · CORS
06Permissions & NavigationsetPermissionRequestHandler (camera/mic/geolocation), will-navigate/setWindowOpenHandler — 외부 URL 차단setPermissionRequestHandler · will-navigate · setWindowOpenHandler · shell.openExternal
07Known 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 pollutionObject.prototype 오염 → renderer가 Node API 흭득Signal 2018 (CVE-2018-10994)
CSP 미설정XSS → 인라인 스크립트 실행 → IPC 채널 학습 → RCEWhatsApp 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 사용

이 표가 0713항목 체크리스트왜 그 13개인지의 답이다. 하나하나가 실제로 누군가 당한 적이 있는 항목이다.


어떻게 읽나

  • 새 프로젝트 시작 직전: 01 → 07. 한 시간이면 어떤 디폴트를 깔고 시작할지 결정 가능.
  • 기존 앱 보안 리뷰: 01 → 04 → 05 → 06 → 07. 현재 깔린 옵션체크리스트를 일일이 대조.
  • 이미 사고가 났을 때: 07부터. 비슷한 CVE 패턴을 먼저 찾는다.
  • 개념만 정리하고 싶을 때: 02 → 03. contextIsolationsandbox가 가장 자주 헷갈리는 두 단어다.

한 단락 요약

Electron 보안은 하나의 옵션이 아니라 다층 격리다. contextIsolation(JS 컨텍스트 분리) + sandbox(Node 제거) + CSP(XSS 자체 차단) + nodeIntegration: false(노출 최소화) + navigation/permission 핸들러(외부 통제) + Electron Fuses(빌드 타임 봉인)의 6+1층함께 켜져야 비로소 XSS → RCE 경로가 끊어진다. 이 챕터를 끝내면 “Electron은 안전한가요?”라는 질문 대신 *“이 13항목 중 몇 개를 켰는가? 그리고 어떤 Fuse로 봉인했는가?”*라는 질문을 던지게 된다.