본문 바로가기
C.W.K.
Stream
Lesson 01 of 05 · published

Permission 모델 개관 — 세 카테고리, 하나의 신뢰 계약

~11 min · permissions, manifest, mv3, trust

Level 0Extension 입덕
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"선언하는 모든 permission이 user가 읽는 install prompt의 한 문장. Lesson 1이 모델 — 세 카테고리, 두 grant 순간, manifest 그대로에서 경고 텍스트 계산하는 하나의 Chrome."

세 카테고리

  • API permissionpermissions 배열에 선언. 각자 chrome.* namespace 해금: "storage"chrome.storage.* 활성화, "scripting"chrome.scripting.* 활성화, "contextMenus"가 menu 활성화 등.
  • Host permissionhost_permissions 배열에 선언. 각자 URL match pattern (https://*.github.com/*, <all_urls>). 그 origin에 script inject, DOM read, request intercept 권한 부여.
  • 선택 권한optional_permissionsoptional_host_permissions에 적어 둬. 생김새는 위의 둘과 같은데, 설치할 때는 user 한테 아무것도 안 물어봐. 나중에 코드가 chrome.permissions.request로 그때그때 달라고 하는 거야.

MV3가 host permission을 API permission에서 분리한 이유는 Chrome이 더 깔끔한 install 경고 표시할 수 있게. User가 "This extension can: read your data on github.com AND use storage AND use tabs" 가 아닌 "This extension can: read your data on github.com" 봄 — 구별된 줄, 구별된 시각 무게.

두 grant 순간

  • 설치 시점: permissionshost_permissions에 적은 건 user가 설치하는 순간 다 열려. 설치 창이 적어 둔 걸 전부 요약해서 보여 주고, user가 Add를 누르면 지울 때까지 계속 갖고 있게 돼.
  • Runtime: optional_permissionsoptional_host_permissions의 모든 것은 user-gesture handler에서 chrome.permissions.request 호출할 때만 grant. 작은 dialog 나타남. user가 Allow 나 Deny click. Deny 면 API가 unavailable 유지. Allow 면 session 나머지 동작하고 browser restart 너머 persist.

Chrome이 install 경고 계산하는 법

각 permission이 몇 가지 경고 카테고리 중 하나로 매핑. Permission 결합이 경고 collapse (예: 'tabs' 가 일부 약한 permission 흡수) 하거나 무서운 거 추가 가능 (<all_urls> 가진 어떤 것이든 'Read and change all your data on all websites' 아래로). 정확한 매핑은 Chrome Developers docs에 있지만, rule of thumb:

  • storage, activeTab, contextMenus, sidePanel은 자체 경고 없는 API permission이야. commands, omnibox, actionpermissions 배열에 넣는 문자열이 아니라 별도 manifest key고.
  • tabs — moderate, "Read your browsing history."
  • scripting, <all_urls> 보다 좁은 host permission — "Read and change your data on specific site."
  • host_permissions 나 content_scripts.matches의 <all_urls> — 시끄러운 "Read and change all your data on all websites."
  • downloads, notifications, identity, history, geolocation, cookies — 각자 자체 dedicated 경고 줄 추가.

최소 권한 원칙

Chrome Web Store 심사도, 좀 아는 user도 extension을 설치 경고로 판단해. 한 줄 덜어낼 때마다 거절당할 이유가 하나씩 줄어드는 거야. ClipDeck이 진짜로 필요한 건 이것들이고:

  • storage — 없으면 안 되는 거야. 늘 적어 두고, 무서운 경고도 안 붙어.
  • tabs — SW에서 tab.url을 읽으려면 필요해. 경고가 조금 무섭게 붙긴 하는데, 방문 카운터랑 사이트별 clip 필터를 하려면 있어야 해.
  • scriptingactiveTab — toolbar를 눌렀을 때 코드를 밀어 넣으려면 필요해. activeTab 덕분에 필요할 때만 도는 이 경로에서는 host_permissions: [<all_urls>]를 건너뛸 수 있어.
  • sidePanel, contextMenus — 조용.
  • matches: [<all_urls>]로 선언된 content script — ClipDeck 한테 'all websites' 경고 주는 것. Track 6 Lesson 3이 이걸 좁히는 거 논의.
  • Export에 쓰는 downloadsoptional_permissions에 두고 user가 Export를 누를 때만 요청해야 해.
세 카테고리: API, host, optional. 두 grant 순간: install 이나 on-demand. Install 경고가 trust 예산 — 신중히 써.
MV2 배열을 그대로 써도 되는 게 함정이야. Chrome은 API 권한과 host 권한이 한 배열에 뒤섞여 있던 옛 MV2 모양도 아직 받아 줘. 올라가긴 해. 대신 경고 요약이 읽기 어려워지고, Chrome Web Store 심사자는 이걸 수상한 냄새로 봐. 그러니 항상 MV3 모양으로 갈라 둬 — API는 permissions, 주소는 host_permissions, lazy 버전엔 optional_*.

Code

manifest.json — 네 배열 explicit 한 MV3 shape·json
{
  "manifest_version": 3,
  "name": "ClipDeck",
  "version": "0.9.0",
  "permissions": [
    "storage",
    "tabs",
    "scripting",
    "activeTab",
    "sidePanel",
    "contextMenus"
  ],
  "host_permissions": [],
  "optional_permissions": [
    "downloads"
  ],
  "optional_host_permissions": [
    "https://*/*",
    "http://*/*"
  ],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "exclude_matches": ["https://accounts.google.com/*", "https://*.bank.com/*"],
      "js": ["content.js"],
      "run_at": "document_idle"
    }
  ]
}
background.js — 선언 vs grant permission의 진단 dump·javascript
// background.js — 선언된 / grant 된 permission 검사
async function dumpPermissions() {
  const declared = chrome.runtime.getManifest().permissions ?? [];
  const declaredHosts = chrome.runtime.getManifest().host_permissions ?? [];
  const optional = chrome.runtime.getManifest().optional_permissions ?? [];
  const optionalHosts = chrome.runtime.getManifest().optional_host_permissions ?? [];

  const granted = await chrome.permissions.getAll();
  console.group("[ClipDeck SW] permissions");
  console.log("declared API:", declared);
  console.log("declared hosts:", declaredHosts);
  console.log("optional API:", optional);
  console.log("optional hosts:", optionalHosts);
  console.log("granted now (API):", granted.permissions);
  console.log("granted now (hosts):", granted.origins);
  console.groupEnd();
}

chrome.runtime.onInstalled.addListener(dumpPermissions);

External links

Exercise

clipdeck/manifest.json을 첫 번째 code block 매칭하도록 update (version 0.9.0, permissions / host_permissions / optional_permissions / optional_host_permissions로 split 된 MV3-shape). chrome://extensions에서 extension reload. ClipDeck → Details click. 'Permissions' section 봄 — API permission 들이 list 보여야 하고, Site access section이 content-script match pattern 따로 표시해야 함. 다음 두 번째 code block (dumpPermissions) 추가하고 reload. SW DevTools console 열기 — 네 줄 봄: 선언된 API permission, 선언된 host (optional_host_permissions로 옮겼으니 빈 배열), optional set, 그리고 이 순간 실제로 grant 된 것. optional_host_permissions가 선언됐지만 아직 grant 안 됨. Lesson 5에서 요청.
Hint
Console이 optional_host_permissions undefined 보이면, Chrome이 105 보다 옛 거 (field 도착 시점). upgrade 하든 host를 직접 선언으로 fallback. 배열 split 후 install 경고가 나빠지면 (가끔 Chrome이 재계산해서 그럼), 보통 host를 content_scripts에서 host_permissions로 옮기는 게 더 visible 하게 한 것 — 정말 persistent grant 필요한지, activeTab + on-demand injection이 cover 하는지 확인. 진단 dump는 일회성 도구. Chrome Web Store에 ship 전 제거.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.