Skip to content

Security Model ko

Hermes Agent edited this page Oct 1, 2026 · 1 revision

보안 모델

English | 中文 | 日本語 | 한국어 | Español | Português | Русский

browsa는 임의의 웹 콘텐츠를 모델에게 먹이고, 모델 출력을 확장 페이지 안에 렌더하고, 사용자 대신 쿠키 실린 요청을 CDN에 보낸다 — 보안 경계는 모두 그 세 체인 위에 놓인다. 원칙: "원본 아티팩트를 보존"하는 쪽으로 fail open한다, 잘못된 데이터를 조용히 만들어내는 일은 결코 없다.

MV3 CSP와 벤더 패치

  • 확장 페이지 CSP: script-src 'self' 'wasm-unsafe-eval' — new Function/eval 없음; WASM 인스턴스화는 허용.
  • Mol*/RDKit embind 접착제는 eval로 클래스 팩토리를 만든다 → build.mjs가 복사 시점에 벤더를 패치한다(손작성 인보커; 즉실행 init은 지연화). 패치 정규식은 안정적인 문자열에 고정되고 molstar 업그레이드가 매칭을 멈추면 빌드를 THROW한다.
  • 검증 환경 교훈: dev-preview는 기본 CSP가 eval을 허용하는 평범한 http 페이지다 — CSP 위반을 잡을 수 없다. 벤더 교체 후에는 실제 확장 안에서 실행하라(launchPersistentContext --load-extension, xvfb) canvas/svg가 존재하고 CSP/EvalError 콘솔 출력이 0임을 단언하라.

DOMPurify (모델 출력은 적대적 입력이다)

  • ALLOWED_URI_REGEXP를 sanitize() 옵션으로 넘기지 마라 — DOMPurify는 그것을 자체 안전 목록에 없는 모든 허용 속성의 "값"에 적용해 <ol start="N">의 맨 숫자를 떼어낸다(실제 버그: 리스트 번호 붕괴). URI 검사는 명시적 URI_ATTRS 집합으로 스콥된 uponSanitizeAttribute 훅을 통과한다.
  • DOMPurify의 data: 허용목록은 data:image/svg+xml(스크립트를 품을 수 있다)을 덮지 않는다 — 모듈 로드 때 한 번 등록되는 훅이 이것을 막는다.
  • mermaid는 securityLevel:'loose'(노드 라벨 안 KaTeX)가 필요하므로, 그 SVG는 반드시 벤더 sanitizeMermaidSvg(살균 + 퇴화 렌더 거절)를 통과해야 한다 — load-bearing 보안이다.

최소-충분 권한 집합

  • <all_urls> host_permissions: 확장 컨텍스트 페치는 CORS 면제다(Ark / AlphaFold / RCSB / raw.githubusercontent가 이것에 의존), DNR이 CDN 요청 헤더를 주입하고, 스크립트가 어떤 탭에도 주입된다. 백엔드 프록시가 없으므로 이것은 필요다.
  • activeTab 제거됨(사이드 패널에서는 절대 동작하지 않는다; 주입은 명시적 tabId를 통과한다).
  • unlimitedStorage: 키프레임/페이지 이미지 데이터 URL은 히스토리에 산다(첫 요청 측 압축까지) — 기본 10MB 쿼터에 실제로 한 번 도달한 적이 있다.
  • 확장 개인 키 백업은 저장소 밖에 산다(/root/workspace/browsa-keys/, 절대 커밋되지 않음).

DNR 헤더 레시피 (lib/sidepanel/media-headers.js)

ASR 다운로드는 CDN이 받아주는 Referer/Origin/쿠키가 필요하다. 세션-스콥 DNR 규칙은 CDN 도메인(bilivideo / googlevideo) + initiatorDomains: [이 확장의 ID]로 필터하고, chrome-extension Origin을 사이트 자신의 것으로 재작성한다. 두 플랫폼 모두 필요하다("YouTube는 헤더가 필요 없다, POT가 URL에 있다"는 가정은 틀렸다 — 라이브 테스트에서 모든 새 스트림이 403했다).

MAIN 월드 추출: 주입 방어

페이지는 LLM 독자를 겨냥한 지시를 숨길 수 있다. 세 층:

  1. <template> 요소와 HTML 주석은 Readability가 파스하기 전에 제거된다;
  2. CSS 숨김 요소(display/visibility/opacity/font-size:0/제로 크기 박스)는 LIVE 문서에서 표시되고, 클론에서 제거된 뒤, 표시가 해제된다(페이지는 결코 변형된 채 남지 않는다); 비지 않은 textContent를 가진 요소만 검사된다(비용 상한);
  3. 출력 텍스트는 제로폭 문자, 제어 문자, 짝이 없는 UTF-16 서러게이트를 떼어낸다(짝 인식 정규식 — 무차별 제거는 정당한 이모지를 죽인다).

chrome.scripting.executeScript는 정확히 하나의 함수를 직렬화한다 — 헬퍼는 주입되는 함수 안에 중첩되어야 한다(ADR-0009; countImages는 모듈 수준 형제였고 실제 Chrome에서 ReferenceError를 던졌는데, 본문을 이어붙인 테스트는 아무것도 보지 못했다).

추출 전 페이지 자동화(preExtractCleanup) 안전 게이트

  • 쿠키 동의 클릭: 컨테이너 게이트 + 위험 단어 거부권(구매/삭제/지불/비우기/초기화 … 두 언어 모두).
  • 더보기 확장: 내비게이션 가능한 <a href>는 절대 클릭하지 않는다(실제 사건: 브랜드 링크가 첨부 도중 탭을 내비게이션했다); 크롬 영역 제외(nav/aside/[role=…], GitHub 메가메뉴와 labuladong 사건 후 강화); 클릭으로 열린 대화상자는 취소 라벨 컨트롤로 닫고 그 패스를 멈춘다.
  • lazyload용 스크롤은 SIGNAL 게이트다(lazy/가상화 신호가 존재할 때만); 페인트 억제는 <html>에 opacity:0을 쓴다(절대 visibility가 아니다 — 페이지 자체의 리빌 안무가 끼일 수 있다); scrollY는 언제나 복원된다.
  • GitHub /blob/ URL은 페이지 자동화를 전부 우회하는 raw 빠른 경로를 탄다(열린 메뉴가 클론 DOM을 오염 — 실제 사건).

세 가지 확장-오리진 형태 (게이트웨이 오리진 허용목록 전에 읽어라)

설치 형태 확장 ID
Chrome Web Store kghjmmajnpbkljankbbjbmnhfdocaeho (CWS 고정)
저장소 디렉터리 로드(manifest가 key 소지) apoodheofdhglelbnmggeokbhampbmgn (key에서 유도)
릴리즈 zip 풀기 경로 유도, 기계마다 다름 (패키징이 key를 떼어낸다)

chrome://extensions에 보이는 ID가 유일한 지상 진실이다. 로컬 게이트웨이는 ws://를 쓴다(wss는 거절); 게이트웨이 설정은 시작 때 한 번 읽는다.


원본 출처: AGENTS.md sanitizer-gotchas, preExtractCleanup, DNR, extension-ID 절; ADR-0008 / 0009. 권위판: Security-Model(영어) / Security-Model-zh(중국어) — AI 초벌 번역 스냅숏, 동기화 2026-10-01.

Clone this wiki locally