Realm Class

2022년 Node.js 에는 Realm Class가 추가되었다. Realm은 주로 실행 환경을 정의하고 코드 간의 격리와 분리를 유지하기 위한 목적으로 사용된다. 기존 Node.js VM 모듈이 있으나 Node.js 자체 기능이고 JavaScript Object Graph가 노출될 가능성이 있다. 그에비해 새롭게 도입된 Realm은 보안측면에서 강화된 JavaScript 실행환경을 제공한다.

ECMAScript Realm

정의와 구성요소

ECMAScript realm은 ECMAScript 스펙상의 개념이다. 이 용어는 코드 실행 컨텍스트, 실행 환경 및 연관된 객체의 집합을 나타내는데, 간단히 말해서 코드가 실행되고 변수, 함수, 객체 등이 존재하는 공간이라고 볼 수 있다. 다음은 Realm의 종류이다.

  • Principal Realm: 각각의 ECMAScript 실행 환경에서 하나씩만 존재하는 특수한 Realm. 이 Realm은 주로 코드 실행에 사용되며, 전역 객체와 스코프를 포함하는 주 실행 컨텍스트를 정의한다.

  • Synthetic Realm: JavaScript 코드에서 명시적으로 생성되는 가상의 Realm. 주로 코드 분리 및 격리를 위해 사용된다. 각 Synthetic Realm은 자체의 전역 객체와 스코프를 갖고 있다. 모듈 시스템과 관련이 있어, 모듈 간의 격리를 유지하고 코드의 독립성을 증가시키는 데 사용된다.

ECMAScript realm은 구성 중 일부는 다음과 같다.

  • Global Object: 각 realm은 전역 객체를 가지고 있으며, 이 객체는 해당 realm에 속한 모든 전역 변수와 함수를 포함함. 브라우저 환경에서는 window, Node.js에서는 global이 전역 객체에 해당한다.
  • Global Scope: 전역 객체의 스코프에 속하는 변수와 함수는 전역 스코프에 존재하며 realm 전체에서 접근 가능한 범위를 의미한다.
  • Intrinsics: 각 realm은 특정한 빌트인 객체와 함수를 갖는다. ObjectArray와 같은 객체 생성자들이 여기에 속한다.
  • Declarative Environment Records: 변수 선언 등과 같은 정보를 저장하는 객체.
  • Object Environment Records: 전역 객체 등과 같은 객체에 대한 참조를 유지하는 객체

구성요소에 관한 더 상세한 내용은 아래 링크를 참조한다.

Realm Class

Node.js 에서 Realm 클래스는 특정 ECMAScript 영역과 연관된 JavaScript 객체 및 함수 집합을 위한 컨테이너이다. 각 ECMAScript 영역에는 Global Object와 Intrinsics 집합이 있다. ECMAScript 영역에는 [[HostDefined]] 필드가 있으며, 이 필드는 Node.js 영역 객체를 나타낸다.

모든 Realm 인스턴스는 Context에서 생성된다. Node.js 내에서 Realm 은 본 개념과 같이 principal realm 과 synthetic realm으로 생성될 수 있다. principal realm는 Node::Environment의 main Context에서 생성된다. synthetic realm은 ShadowRealm의 Context (JavaScript API에 의해 생성됨) 에 대해 생성된다. vm.Context에는 Realm instance를 생성하지 않는다.

ShadowRealm을 이용한 Isolation

Node.js 가 도입한 내용은 Synthetic Realm을 이용한 객체 참조 (Object Graph) 의 격리를 보장하는 것이다. 아래의 예를 들면 (1) 에서 vm에서 생성된 함수가 외부의 객체를 접근할 수 있는데 이러한 참조를 Synthetic Realm을 통해 격리시킬 수 있다.

1
2
3
4
let ctx = vm.createContext();
let fn = vm.runInContext('...', ctx);

fn({}); // <= (1) vm에서 생성된 함수가 외부 `Object`에 액세스하여 메서드에 접근할수 있다.

ShadowRealm

Synthetic Realm을 생성하기 위해 Node.js는 TC39 3단계 제안상태인 ShadowRealm 기능을 사용한다. ShadowRealm은 서로 다른 자바스크립트 실행 컨텍스트 간의 객체 그래프 격리를 보장하고, 새로운 빌트인을 도입하여 별개의 글로벌 환경을 제공한다. 또한 ShadowRealm은 사용하는 모듈 그래프가 외부 영역으로부터 격리되도록 보장한다. ShadowRealm은 모든 주요 영역 (main thread 환경 / worker thread 환경), 다른 ShadowRealm 및 vm.Context에서 생성할 수 있다. VM과 비교하면 ShadowRealm은 URL, TextEncoders 등과 같이 Node.js 에서 공통인 API가 내장되어 있다. 따라서 더 쉽게 일반적인 JavaScript 코드를 실행할 수 있다.

Node.js 에서 Realm을 계층적으로 구조한 디자인을 정리하면 다음과 같다.

  • Environment (Main thread 나 Worker threads, ECMAScript agent를 나타낸다)

    • worker_contexts: worker 가 객체를 처리한다. handle objects.
    • principal_realm: principal realm (v8::Context) 과 관련된 데이터
    • synthetic_realms: Synthetic realms.
    • time origin.
  • Realm (realm record, principal realm, ShadowRealm, vm.Context):

    • context: 실행 context.
    • principal realm을 나타내는 플래그
    • module map.
    • Per-context persistent handles.
    • … principal realm과 synthetic realm 간 공통인 데이터

각 Realm은 여러 개의 synthetic realm 을 생성할 수 있다. Realm의 도입으로 Node::Environment는 이벤트 루프, Inspector 등 여러 Realm에서 공유할 수 있는 기타 여러 가지를 소유하는 Thread/Execution environment/ECMAScript 에이전트의 레코드로 해석할 수 있다.

Bootstrapping 순서

node::Environment Principal realm 설정

  1. InitializeContext
    • internal/per_context/primordials
    • internal/per_context/domexception
    • internal/per_context/messageport
  2. Environment::RunBootstrapping
    • internal/bootstrap/loaders
    • internal/bootstrap/node
      • node global extensions and the process object 설정
    • internal/bootstrap/browser
      • web API global extensions 설정
    • internal/bootstrap/switches/{is_main_thread, is_not_main_thread}
    • internal/bootstrap/switches/{does_own_process_state, does_not_own_process_state}
  3. LoadEnvironment
    • internal/main/*
      • internal/bootstrap/pre_execution

SyntheticEnvironment의 경우, (1) 및 (2) 단계는 node::Environment의 principal realm 와 유사하다. (2)에서 글로벌을 internal/bootstrap/switches로 초기화하는 스크립트를 node::Environment의 principal realm과 Synthetic Realm에 대해 분할해야 한다. (Synthetic Realm에서 특정 글로벌을 노출하지 않는 것과 같은 방식)

global 설정

  1. global: internal/bootstrap/node
    • setupGlobalProxy
  2. process: internal/bootstrap/node
  3. Buffer: internal/bootstrap/node
  4. Web interfaces: internal/bootstrap/browser

References

Share