⚡ Electron5. 보안 모델알려진 CVE & 보안 체크리스트

알려진 CVE & 보안 체크리스트

이 문서가 답하는 질문: “Electron 앱은 실제로 어떻게 뚫렸고, 무엇을 점검하면 그 사고들을 막을 수 있는가?” 한 줄 답 (Pyramid Top): “공식 Security Checklist 13항목은 지난 10년의 실제 CVE를 일반화한 결과다 — 하나라도 빠지면 어떤 사고가 일어났는지 역사가 보여준다.”


Why — 왜 존재하는가

Electron 앱의 공격면은 웹 + Node + OS의 합집합이다. 사고가 일어나면 사용자 디스크 전체가 위험에 노출된다. 공식 Security Checklist13개 항목으로 구성되며 — 각 항목 뒤에는 그것을 어겨서 일어났던 실제 사건이 있다.

사고의 출처빈도대응 항목
nodeIntegration이 켜진 채 외부 콘텐츠 로드가장 많음#2, #3
입력 검증 없는 IPC빠르게 증가#5
외부 URL 검증 없는 shell.openExternal자주#10
미사인 빌드 / 가짜 업데이트중간(배포 챕터)

How — 어떻게 점검하는가


What — 13항목 + 실제 CVE

13항목 체크리스트 (공식)

#항목위반 시 사고
1신뢰 가능한 콘텐츠만 로드http://로 외부 콘텐츠 로드 → MITM 통한 RCE
2Renderer에서 nodeIntegration: falseDiscord RCE (2018)
3contextIsolation: trueSlack 2020 — preload 변수 누설
4sandbox: true 활성화Node API 잔존 통로
5session permission 핸들러 등록임의 origin이 카메라 사용
6webSecurity 끄지 말 것CORS 무력화 → 데이터 유출
7CSP 정의XSS 1차 방어선 누락
8allowRunningInsecureContent: false 유지mixed content 통한 인젝션
9실험 기능(experimentalFeatures) 비활성미검증 API 노출
10<webview>enableBlinkFeatures 검사보안 무력화된 Blink 기능
11will-navigate로 네비게이션 검증외부 URL 우회 로드
12setWindowOpenHandler로 새 창 검사popup → 새 BrowserWindow 권한 상승
13shell.openExternal은 사용자 입력 검증URL 인젝션 → 시스템 명령 실행

실제 CVE 케이스 분석

CVE-2018-15685 — Discord (RCE via window.open)

  • 원인: nodeIntegration: true 상태에서 window.open()이 nodeIntegration을 상속.
  • 공격: XSS 발견 → window.open → 새 창에서 require('child_process').exec().
  • 대응: nodeIntegration 디폴트 false (Electron 5), nativeWindowOpen 분리, setWindowOpenHandler로 webPreferences 강제.

CVE-2018-1000136 — Atom Editor (Markdown preview RCE)

  • 원인: MathML 파서 XSS + nodeIntegration: true.
  • 공격: .md 파일 미리보기 → <math> 태그로 JS 실행 → require(‘fs’)로 임의 파일 읽기.
  • 대응: 마크다운 렌더링은 별도 BrowserWindow에서 nodeIntegration: false로 격리.

CVE-2020-15174 — Electron Builder (file:// path traversal)

  • 원인: file:// origin이 없음(null) → SOP 우회.
  • 공격: 로컬 HTML에서 다른 디렉토리 파일 fetch.
  • 대응: 커스텀 protocol(app://) 사용 — file:// 직접 사용 금지.

CVE-2018-1000006 — Electron Protocol Handler (Windows)

  • 원인: Windows 커스텀 protocol에서 인자가 쉘 인자처럼 처리됨.
  • 공격: myapp://...--gpu-launcher="cmd /c calc"--gpu-launcher 플래그 인젝션 → 임의 명령 실행.
  • 대응: app.setAsDefaultProtocolClient 사용 시 인자 검증, Chromium의 --gpu-launcher 같은 위험 플래그 차단.

CVE-2019-1020009 — WhatsApp Desktop (Open Redirect → RCE)

  • 원인: 메시지의 preview 링크 검증 미흡 + nodeIntegration.
  • 공격: 악성 링크 클릭 → 정보 누출 + 일부 환경에서 RCE.
  • 대응: 외부 링크는 shell.openExternal + URL allowlist.

자동 점검 — Electronegativity

# 정적 분석 도구
npm install -g @doyensec/electronegativity
electronegativity -i ./my-electron-app -o report.csv
점검 항목예시
AUXCLICK_JS_CHECKauxclick 이벤트 미가드
CONTEXT_ISOLATION_JS_CHECKcontextIsolation 비활성
DANGEROUS_FUNCTIONS_JS_CHECKeval, exec 호출
HTTP_RESOURCES_JS_CHECKhttp:// 자원 로드
INSECURE_CONTENT_HTML_CHECKmixed content
NODE_INTEGRATION_JS_CHECKnodeIntegration: true
30+ 룰

Electron Fuses — 빌드 타임 보안 고정

// forge.config.js
const { FuseV1Options, FuseVersion } = require('@electron/fuses');
 
module.exports = {
  plugins: [
    { name: '@electron-forge/plugin-fuses',
      config: {
        version: FuseVersion.V1,
        [FuseV1Options.RunAsNode]: false,            // ELECTRON_RUN_AS_NODE 차단
        [FuseV1Options.EnableCookieEncryption]: true,
        [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
        [FuseV1Options.EnableNodeCliInspectArguments]: false,
        [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
        [FuseV1Options.OnlyLoadAppFromAsar]: true,
      }
    }
  ]
};

이들은 실행 파일 바이트에 새겨진다. 사용자가 바이너리를 패치해도 — 그 바이트가 바뀌면 Electron이 거부.

안전한 webPreferences 한 묶음

new BrowserWindow({
  webPreferences: {
    contextIsolation: true,
    sandbox: true,
    nodeIntegration: false,
    nodeIntegrationInWorker: false,
    nodeIntegrationInSubFrames: false,
    webSecurity: true,
    allowRunningInsecureContent: false,
    experimentalFeatures: false,
    enableBlinkFeatures: '',  // 빈 문자열
    preload: path.join(__dirname, 'preload.js'),
  }
});

What-if — 잘못 쓰면

  • 함정 1: 체크리스트의 일부만 적용 → “보안 했어요” 착각. CVE의 90%는 한 항목만 위반해서 일어났다.
  • 함정 2: Electronegativity가 false positive가 많아 무시 → 진짜 위반도 같이 묻힌다. 점진적으로 noise 줄이며 활용.
  • 함정 3: Fuses 적용 안 함 → 사용자가 ELECTRON_RUN_AS_NODE 환경변수로 Node 인터프리터처럼 앱 바이너리 실행 → 임의 코드.
  • 함정 4: 외부 보안 감사 없음 → 자기 코드를 자기가 검토 → 같은 함정을 못 본다. 1년에 한 번 외부 audit 권장.

Insight — 흥미로운 이야기

“Electron 보안의 역사는 디폴트의 역사다”

Electron 5(2019): nodeIntegration 디폴트 false. 그 전엔 모든 앱이 시한폭탄이었다. Electron 12(2021): contextIsolation 디폴트 true. preload 변수 누설 시대 종료. Electron 22(2023): sandbox 디폴트 true. Renderer가 Chromium sandbox로 격리. Electron 28(2023): remote 모듈 완전 제거. 원격 객체 노출 패턴 영구 폐지.

흥미로운 건 — Electron 팀이 디폴트를 바꿀 때마다 기존 앱의 일부가 깨졌다. 그때마다 커뮤니티에서 “왜 이런 변경을?”이라는 반발이 있었지만, 시간이 지나 보면 그 변경 없이는 다음 사고가 막을 수 없었다. 보안 디폴트는 불편을 통해 사고를 막는 거래다 — 이 13항목 체크리스트는 그 거래의 합의된 가격표다.


요약

  • 13항목은 역사의 일반화 — 빠뜨릴 자유가 없다.
  • Electronegativity로 자동 1차 점검, 수동 audit으로 보강.
  • Fuses는 런타임 가드를 빌드 타임으로 고정하는 마지막 layer.
  • 사고는 한 항목 위반에서 시작한다 — 전체 통과가 baseline.