Skip to content

보안 모델

This content is not available in your language yet.

Suji가 frontend(renderer)에서 backend로 향하는 모든 호출에 적용하는 보안 정책. Electron 모델과 동등한 곳, 다른 곳을 정확히 구분한다.

window.__suji__ 오브젝트에 노출되는 것:

항목종류비고
invoke(backend, request)native (V8 binding)backend cmd 호출, Promise<JSON>
emit(event, data)native (V8 binding)EventBus 브로드캐스트
core(request)JS helperinvoke("__core__", ...) shorthand
on(event, cb) / off(event, cb)JS helperEventBus listener
platformstring 상수`“macos"

Electron 비교: Electron의 contextIsolation: true는 preload script만 main world에 노출. Suji는 모든 surface를 main world의 window.__suji__에 직접 바인딩 (Electron의 contextIsolation:false 동등). 노출 surface가 매우 작아 (invoke + emit 2개 native만) 실제 위험은 cmd-level 정책에 의존한다.

로드맵: Electron-style contextBridge isolated world 분리. 현재는 surface가 작고 fs/cache 격리로 대부분의 위험을 차단하고 있어 우선순위가 낮다.

frontend가 호출하는 fs.* cmd는 path 화이트리스트 + traversal 가드로 검증. fs.mdx 참고.

{ "fs": { "allowedRoots": ["~/Documents/myapp"] } }
보호동작
Default safeallowedRoots 미설정/빈 배열 → 모든 frontend fs.* 차단
Path traversal.. path component는 모든 mode에서 항상 차단 (["*"]도 우회 불가)
Prefix-extension 차단/foo/bar 허용 시 /foo/barX 통과 X (separator boundary 가드)
~ 사전 expandconfig load 시 1회 — hot path env lookup 0회
Backend 우회backend SDK 호출은 무제한 (사용자 자체 코드 신뢰)

fs.* 외에도 renderer가 file path를 넘기는 native cmd는 같은 경계로 검사한다.

cmdsink정책
print_to_pdf / capture_page / desktop_capturer_capture_thumbnail파일 쓰기fs.allowedRoots 설정 시 enforce
native_image_get_size / native_image_to_png / native_image_to_jpeg파일 읽기/인코딩fs.allowedRoots 설정 시 enforce
tray_create.iconPath이미지 파일 로드fs.allowedRoots 설정 시 enforce

이 경로들은 기존 릴리즈에서 무제한이었기 때문에 fs.allowedRoots가 미설정/빈 배열이면 레거시 동작을 유지한다. fs.allowedRoots를 설정한 앱에서는 fs.*와 동일한 traversal/boundary 검사를 적용한다.

shell.*와 dialog open/save의 defaultPath는 opt-in allowlist를 제공한다. 키가 없으면 기존 동작을 유지하고, 키가 있으면 빈 배열은 deny-all, ["*"]는 allow-all, 나머지는 path boundary 또는 URL glob 매치만 허용한다.

{
"shell": {
"allowedPaths": ["~/Documents/myapp"],
"allowedExternalUrls": ["https://*.example.com/*"]
},
"dialog": {
"allowedPaths": ["~/Documents/myapp"]
}
}

Backend SDK 호출은 fs sandbox와 동일하게 우회한다.

config.app.name 키로 OS 표준 user-data 디렉토리 아래 자동 격리. 한 시스템에 여러 Suji 앱이 있어도 cookie/localStorage/IndexedDB/Service Worker가 서로 영향 없음. cache.mdx 참고.

__core__ channel을 통해 들어오는 모든 IPC 메시지가 통과:

검증거부 시 응답
payload size ≤ 32KBerror: "payload_too_large"
cmd 필드 존재error: "missing_cmd"
cmd 영숫자/언더스코어만error: "invalid_cmd" (newline/quote 등 injection 차단)
알려진 cmdunknown 시 error: "unknown_cmd" (typo / version mismatch 진단)
cmd 정확 매치substring 매치 X

외부 untrusted 콘텐츠 (광고/위젯/임베드)를 iframe으로 띄울 때 origin 화이트리스트로 제한. production suji:// 응답에 붙는 CSP frame-src directive로 Chromium이 차단한다.

suji.json:

{
"security": {
"iframeAllowedOrigins": ["https://trusted.com", "https://api.example.com"]
}
}
설정동작
미설정 / []default block — 모든 iframe 차단 (frame-src 'none')
["https://trusted.com"]해당 origin만 허용 (frame-src 'self' https://trusted.com)
["*"]무제한 escape hatch (frame-src *) — 권장 X

사용자가 security.csp로 직접 CSP 명시 시 그것 우선 — iframe_allowed_origins은 무시. 그 경우 사용자가 직접 frame-src 명시 책임.

security.csp는 명시 문자열 override와 "disabled" escape hatch를 지원한다. 미설정이면 Suji가 default CSP를 만들고 iframeAllowedOriginsframe-src에 반영한다.

suji build --sandbox 또는 SUJI_SANDBOX로 App Sandbox entitlements를 자동 부착한다. main app과 CEF Helper(Browser/GPU/Renderer/Plugin)별 plist를 분리하고 helper에는 inherit entitlement를 적용한다. Security-scoped bookmarks API는 별도 sandbox.mdx에 정리되어 있다.

  • 진짜 isolated world: window.__suji__ surface는 freeze/hardening되어 있지만 Chromium isolated world/contextBridge와 동일한 별도 V8 context는 아직 아니다.
  • dev mode CSP: dev_url로 Vite 등 외부 서버를 직접 로드하는 동안에는 Suji가 응답 헤더를 주입하지 않는다. dev 서버나 HTML meta CSP로 별도 검증해야 한다.
  • network/webRequest: webRequest는 선언적 blocklist API로 제공한다. renderer 일반 fetch allowlist는 별도 HTTP plugin 정책으로 다룬다.
  • App Sandbox 실효 검증: 로컬 빌드와 entitlement 부착은 자동화되어 있지만, Mac App Store 심사 환경의 실제 sandbox 검증은 별도 배포 검증이 필요하다.