Capabilities 및 보안

이 페이지

샌드박스 플러그인은 기본적으로 격리됩니다. 자체 KV 및 스토리지 읽기와 쓰기를 넘어서는 모든 작업을 수행하려면 플러그인이 디스크립터에서 capability를 선언해야 합니다. 샌드박스 브리지는 이러한 선언을 기반으로 호스트가 제공하는 모든 API를 제어합니다 — content:read를 선언하지 않은 플러그인은 ctx.content를 얻지 못하고, network:request를 선언하지 않은 플러그인은 ctx.http를 얻지 못합니다.

이 페이지는 각 capability가 무엇을 부여하는지, 샌드박스가 어떻게 이를 강제하는지, 그리고 무엇이 강제될 수 없는지를 다룹니다.

Capabilities 선언

Capabilities는 디스크립터(astro.config.mjs에서 가져오는 파일)에 id, version, entrypoint와 함께 존재합니다:

export function helloPlugin(): PluginDescriptor {
	return {
		id: "plugin-hello",
		version: "0.1.0",
		format: "standard",
		entrypoint: "@my-org/plugin-hello/sandbox",

		capabilities: ["content:read", "network:request"],
		allowedHosts: ["api.example.com"],
	};
}

플러그인이 실제로 필요로 하는 것만 선언하세요. Capability 선언은 마켓플레이스가 동의 대화 상자에서 사이트 운영자에게 표시하는 것이기도 합니다 — 추가 capabilities는 설치 시 마찰이 되고 감사 시 보안 플래그가 됩니다.

Capability 참조

Capability액세스 권한 부여
content:readctx.content.get(), ctx.content.list()
content:writectx.content.create(), ctx.content.update(), ctx.content.delete() (content:read 포함)
media:readctx.media.get(), ctx.media.list()
media:writectx.media.getUploadUrl(), ctx.media.upload(), ctx.media.delete() (media:read 포함)
network:requestctx.http.fetch()allowedHosts로 제한
network:request:unrestrictedctx.http.fetch() 호스트 제한 없음 (사용자 구성 URL 전용)
users:readctx.users.get(), ctx.users.getByEmail(), ctx.users.list()
email:sendctx.email.send() (구성된 이메일 제공자 플러그인 필요)
hooks.email-transport:register독점 email:deliver 훅 등록 허용 (전송 제공자)
hooks.email-events:registeremail:beforeSend / email:afterSend 훅 등록 허용
hooks.page-fragments:registerpage:fragments 훅 등록 허용 (네이티브 플러그인만)

알아두어야 할 몇 가지 사항:

  • 함의. content:write는 자동으로 content:read를 포함합니다; media:writemedia:read를 포함합니다; network:request:unrestrictednetwork:request를 포함합니다. 둘 다 나열할 필요가 없습니다.
  • network:request:unrestricted는 사용자 구성 URL을 위해 존재합니다. 운영자가 대상 URL을 입력하는 웹훅 플러그인은 매니페스트에 없는 호스트에 도달해야 합니다. 항상 알려진 API를 호출하는 플러그인은 network:request + allowedHosts를 사용해야 합니다.
  • email:send는 capability뿐만 아니라 구성에 의해 제어됩니다. 플러그인은 email:send를 선언할 수 있지만 다른 플러그인이 email:deliver 전송을 등록한 경우에만 ctx.email이 채워집니다.

네트워크 호스트 허용 목록

network:request가 있는 플러그인은 allowedHosts에 나열된 호스트만 가져올 수 있습니다. 하위 도메인에 와일드카드가 지원됩니다:

capabilities: ["network:request"],
allowedHosts: [
	"api.example.com",          // 정확한 호스트
	"*.cdn.example.com",        // cdn.example.com의 모든 하위 도메인
],

브리지는 요청을 전달하기 전에 요청 URL의 호스트를 허용 목록과 비교합니다. 선언되지 않은 호스트에 대한 요청은 샌드박스를 벗어나지 않고 플러그인 내에서 예외를 발생시킵니다.

network:request:unrestricted는 허용 목록 확인을 완전히 건너뜁니다. 이는 운영자가 런타임에 대상 URL을 구성하는 플러그인(웹훅 발신자, 일반 HTTP 전달자)을 대상으로 합니다. 대상이 플러그인 설계의 일부인 플러그인에는 사용하지 마세요 — 대신 명시적 호스트와 함께 network:request를 선언하여 동의 대화 상자가 운영자에게 플러그인이 정확히 어디로 호출할지 알려주도록 하세요.

샌드박스가 강제하는 것

샌드박스 러너가 활성화되면 런타임은 다음을 강제합니다:

  1. Capability 게이팅. PluginContext 팩토리는 해당 capability가 선언된 경우에만 ctx.content, ctx.media, ctx.http, ctx.users, ctx.email을 채웁니다. 선언되지 않은 capability에서 메서드를 호출하는 것은 불가능합니다 — 거기에 객체가 없습니다.

  2. 스토리지 및 KV 범위 지정. 모든 스토리지 및 KV 작업은 플러그인의 id로 범위가 지정됩니다. 플러그인은 다른 플러그인의 KV 또는 해당 스토리지 컬렉션을 읽을 수 없으며 디스크립터에 선언한 스토리지 컬렉션에만 액세스할 수 있습니다.

  3. 네트워크 격리. 직접 fetch() 및 기타 네트워크 프리미티브는 러너에 의해 차단됩니다. 네트워크에 도달하는 유일한 방법은 브리지의 호스트 유효성 검사를 통과하는 ctx.http.fetch()입니다.

  4. 호스트 바인딩 없음. 샌드박스 플러그인은 환경 변수, 파일 시스템 또는 플랫폼 바인딩을 볼 수 없습니다 — 호스트 워커가 가지고 있더라도 마찬가지입니다. 플러그인 런타임은 브리지와 선언된 capabilities만 있는 깨끗한 격리입니다.

  5. 리소스 제한. 러너는 호출당 CPU, 하위 요청, 벽시계 시간 및 메모리 제한을 강제할 수 있습니다. 정확한 제한은 사용하는 러너에 따라 다릅니다; Cloudflare 러너는 플랫폼의 Worker Loader 제한을 사용합니다(호출당 50ms CPU, 10개 하위 요청, 30초 벽시계 시간, ~128MB 메모리). 러너의 제한을 초과하는 훅은 중단됩니다; EmDash 훅 타임아웃(훅 구성의 timeout)은 그 위에 더 엄격한 상한선을 강제합니다.

샌드박스가 강제하지 않는 것

capability 시스템이 다룰 수 없는 몇 가지 사항:

  • 부여된 capability 내의 동작. content:write가 있는 플러그인은 자신의 콘텐츠뿐만 아니라 모든 콘텐츠를 편집할 수 있습니다. Capabilities는 대략적입니다 — “이 플러그인은 콘텐츠를 쓸 수 있다”고 말하지 “이 플러그인은 생성한 콘텐츠만 쓸 수 있다”고 말하지 않습니다. 감사 시 검토는 플러그인이 부여 내에서 실제로 수행하는 작업에 대한 유일한 확인입니다.
  • Node.js에 대한 운영자 신뢰. 구성된 샌드박스 러너가 사용 불가능하다고 보고하면(Cloudflare Worker Loader 없음, Node 측 러너가 설치되지 않음 등) sandboxed: [] 플러그인은 시작 시 건너뜁니다. 이를 plugins: []로 이동하여 프로세스 내에서 실행할 수 있습니다 — 그러나 그렇게 하면 V8 격리가 없고 리소스 제한이 없으며 플러그인이 직접 fetch()를 호출하거나 환경 변수를 읽을 수 있습니다. 이를 네이티브 수준 신뢰로 취급하세요.
  • 측면 채널. 타이밍, 로그 출력 및 저장된 데이터는 모두 호스트 환경에 대한 적절한 액세스 권한이 있는 사람에게 표시됩니다. 샌드박스를 이를 실행하는 운영자에 대한 기밀 경계로 사용하지 마세요.

Capability 동의

운영자가 마켓플레이스에서 샌드박스 플러그인을 설치하면 EmDash는 선언된 capabilities를 나열하는 동의 대화 상자를 표시합니다. capabilities를 추가하는 업데이트 — 예를 들어 이전에는 콘텐츠만 읽었던 플러그인이 이제 네트워크 요청을 하려고 하는 경우 — capability diff로 표시되며 새 버전이 적용되기 전에 새로운 승인이 필요합니다.

이것이 “나중에 사용할 수 있다”고 해도 추가 capabilities를 선언하는 것이 중요한 이유입니다. 모든 설치 및 업데이트에서 마찰로 나타나며 보안 감사는 명백히 필요한 것보다 더 많이 요청하는 플러그인에 플래그를 지정합니다. 플러그인이 사용하는 것을 정확히 나열하고 플러그인이 실제로 사용하기 시작할 때 실제 버전에서 새로운 capabilities를 추가하세요.

번들 시 유효성 검사

emdash plugin bundleemdash plugin publish는 추가 검사를 수행합니다:

  • 선언된 모든 capability는 알려진 집합에 있어야 합니다(오타는 빌드를 실패시킵니다).
  • allowedHosts를 채우지 않고 network:request를 선언하는 플러그인은 경고를 트리거합니다 — 호스트를 선언하거나 network:request:unrestricted로 전환하고 이유를 문서화하세요.
  • 사용 중단된 capability 이름은 bundle/validate 중에 경고를 트리거하고 publish에서 하드 실패합니다.
  • 번들된 backend.js는 Node.js 내장 모듈(fs, path, child_process 등)을 가져올 수 없습니다 — 샌드박스 런타임은 이를 제공하지 않습니다.

전체 검사 목록은 번들링 및 게시를 참조하세요.