WebAssembly를 이용, Firefox의 보안성 개선하기


이 글은 Nathan Froyd이 쓴 Securing Firefox with WebAssembly의 한국어 번역본입니다.


개인의 안전(Security)과 프라이버시(Privacy) 보호는 Mozilla의 핵심 목표입니다. 그래서 우리는 온라인 상의 사용자들을 보다 안전하게 보호하려고 끊임 없이 노력합니다. 복잡하면서 동시에 고도로 최적화된 Firefox 같은 시스템에 있어, 메모리 안전성(memory safety)은 보안 측면에서 가장 어려운 도전 중 하나입니다. Firefox 는 대부분 C 와 C++ 로 개발됐습니다. 이 랭귀지는 안전하게 쓰기 어렵기로 악명이 높습니다. 사소한 실수 하나가 프로그램을 망가뜨릴 수 있기 때문입니다. 우리는 메모리 위협요소(memory hazards)를 발견하고 제거하기 위해 열심히 노력했습니다. 그 뿐 아니라 우리는 깊은 수준에서 이런 위협요소를 차단하기 위해 Firefox 코드베이스를 고도화하고 있습니다. 그 과정에서, 우리는 두 가지 접근방식을 취해왔습니다.

새로운 접근

우리가 Firefox 에 샌드박스의 비중과 Rust 랭귀지의 비중을 지속적으로 그리고 적극적으로 늘리고 있긴 하지만, 두 방식 모두 나름의 한계가 있습니다. 프로세스 레벨 샌드박스 방식은 기존의 규모가 큰 컴포넌트들을 다룰 때는 효과가 좋지만, 시스템 자원을 많이 소모합니다. 그래서 아껴서 신중하게 적용해야 합니다. Rust 랭귀지 방식은 시스템 자원은 적게 소모하지만 C++ 로 작성된 수백만 라인의 기존 코드를 새로 써야 합니다. 노력이 많이 드는 방식입니다.

그라파이트 폰트 처리 라이브러리(Graphite font shaping library) 를 생각해 봅시다. 이 라이브러리는 복잡한 폰트를 그리기 위해 Firefox가 사용하는 라이브러리입니다. 이 라이브러리는 독립된 프로세스 안에 넣기에는 그 크기가 너무 작습니다. 그러나 메모리 위협요소(memory hazard)를 해결하지 않으면, 사이트 마다 프로세스를 격리한다고 해도, 유해한 폰트를 사용하는 페이지가 훼손되는 것은 막을 수 없습니다. 그리고 이렇게 특수한 경우에만 사용되는 코드를 새로 작성하는 것은 제한된 인적 자원을 고려할 때 합리적이지 못합니다.

그래서 오늘, 우리는 우리 무기고에 세 번째 무기를 추가하려고 합니다. 캘리포니아, 샌디에고, 텍사스 오스틴, 스탠포드 대학교의 연구진이 개발한 새로운 샌드박스 기술인 RLBox 입니다 이것을 이용하면 우리는 쉽고 빠르게 기존의 Firefox 컴포넌트가 웹어셈블리(WebAssembly) 샌드박스 안에서 실행되도록 만들 수 있습니다. Shravan Narayan, Deian Stefan, Tal Garfinkel, Hovav Shacham의 쉼 없는 노력 덕분에 우리는 이 기술을 우리의 코드베이스에 통합시킬 수 있었습니다. 그리고 우리는 그라파이트(Graphite) 모듈을 샌드박스로 격리시키는 데 이 기술을 적용했습니다.

이 격리 기술은 Firefox 74를 통해서 리눅스 사용자들에게 전달될 것입니다. 그리고, 맥 사용자들에게는 Firefox 75를 통해서, 윈도우즈 사용자들에게는 그 이후 가능한 빠른 시일 안에 전달될 것입니다. UCSDUT Austin의 언론보도와 공동 연구 논문을 통해 자세한 내용을 보실 수 있습니다. 이제 이 기술을 어떻게 Firefox에 통합했는지 설명하겠습니다.

wasm 샌드박스 만들기

wasm 샌드박스 아이디어의 핵심은 C/C++ 코드를 wasm 코드로 컴파일할 수 있다는 것, 그리고 이렇게 컴파일된 wasm 코드를 최종 실행되는 컴퓨터의 머신 코드(machine code)로 컴파일할 수 있다는 것입니다. 이 과정은 브라우저에서 C/C++ 어플리케이션을 실행시키는 과정과 유사합니다. 딱 하나, wasm 코드를 네이티브 코드로 변환하는 과정을 미리, 그러니까 Firefox 를 빌드할 때 함께 처리한다는 것만 다릅니다. 이 두 개의 과정에 매우 많은 소프트웨어들이 동원됩니다. 그리고 우리는 샌드박스 변환 작업이 좀 더 간단하고 에러 없이 처리될 수 있도록 세번째 단계를 추가했습니다.

첫번째로, C/C++ 코드를 wasm 코드로 컴파일 할 수 있어야 합니다. WebAssembly 가 이룩한 성과들 중에 Clang 과 LLVM 을 지원하는 wasm 백엔드(backend)가 있습니다. 하지만 컴파일러만 있다고 모든 문제가 해결되는 것은 아닙니다. C/C++ 표준 라이브러리도 쓸 수 있어야 합니다. 이는 wasi-sdk 를 통해 지원됩니다. 이 재료들을 이용하면 우리는 C/C++ 코드를 wasm 코드로 변환할 수 있습니다.

두번째로, 우리는 wasm 코드를 네이티브 오브젝트 파일로 변환할 수 있어야 합니다. 우리가 처음 wasm 샌드박스를 구현하려고 했을 때 “이 절차가 왜 필요한가요? 그냥 wasm 코드를 배포하고 사용자가 Firefox 가 시작할 때마다 그때그때(on-the-fly) 머신 코드로 컴파일하면 되잖아요?” 라는 질문을 자주 들었습니다. 그렇게 할 수도 있었습니다. 하지만 그러면 새로운 샌드박스가 생성될 때마다 wasm 코드를 새로 컴파일해야 합니다. 분리된 프로세스에 모든 출발점이 존재하는 상황에서 샌드박스마다 컴파일하는 방식은 불필요한 중복입니다. 우리는 컴파일된 네이티브 코드를 여러개의 프로세스가 공유하는 방식을 선택했습니다. 그 결과 메모리를 크게 절약할 수 있었습니다. 또 샌드박스 시작 시간을 단축할 수 있었습니다. 이는 작은 크기의 샌드박스에게 있어 중요합니다. 예를 들어 폰트 처리와 이미지 처리에 관계된 코드를 샌드박스로 만드는 경우 말이죠.

Cranelift 와 친구들을 이용한 AOT 컴파일 (Ahead-of-time compilation)

이 접근방식을 위해 wasm 에서 네이티브 코드를 빌드하는 컴파일러를 새로 만들 필요가 없습니다!  우리는 이런 AOT 컴파일(ahead-of-time compilation) 작업을 동일한 컴파일러 백엔드를 이용해서 처리할 수 있습니다. Firefox 의 JavaScript 엔진이 wasm 컴포넌트를 처리할 때 사용하는 바로 그 컴파일러 백엔드, 즉 Cranelift 와 Bytecode Alliance의 Lucet compiler and  runtime 말입니다.  이렇게 코드를 공유하면 코드 개선의 효과가 JavaScript 엔진에도 또 wasm 샌드박스 컴파일러에도 적용됩니다. 현재는 엔지니어링 상의 이유로 이들 2개의 코드가 각기 다른 버전의 Cranelift를 사용하고 있습니다. 그러나 우리의 샌드박스 기술이 더 성숙해지면, 우리는 정확히 동일한 코드를 사용하게 될 것입니다.

이제 wasm 코드를 네이티브 코드로 변환했으니, 우리는 샌드박스에 담긴 코드를 C++ 코드에서 호출할 수 있어야 합니다. 샌드박스에 담긴 코드가 분리된 별도의 가상머신(virtual machine)에서 실행된다면, 이를 위해 실행시간에 함수 이름을 검색하는 일과 가상머신의 상태를 관리하는 일이 필요할 것입니다. 이런 환경에서 샌드박스에 담긴 코드는 어쨌든 wasm 보안 모델(security model)을 준수하는 네이티브 코드입니다. 그래서, 샌드박스에 담긴 함수는 일반적인 네이티브 코드를 호출하는 것과 동일한 메카니즘을 통해 호출될 것입니다. 우리는 어떤 머신모델이 사용되는지 주의를 기울여야 합니다. wasm 코드는 32-bit 포인터를 사용합니다. 반면, x86-64 Linux 같은 우리의 디폴트 타겟 플랫폼은 64-bit 포인터를 사용합니다. 여기서 극복해야 난관이 하나 더 있습니다. 그래서 우리는 변환 과정에 마지막 단계를 추가했습니다.

샌드박스가 올바르게 동작하도록 만들기

일반적인 네이트브 코드를 수행하는 것과 동일한 메카니즘으로 샌드박스에 담긴 코드를 호출하는 것은 쉬운 일입니다. 하지만 그렇게만 생각한다면 중요한 사실을 놓치는 것입니다. 우리는 샌드박스에 담긴 어떤 것도 신뢰할 수 없습니다. 누군가 악의적으로 샌드박스를 오염시켰을 수 있습니다.

예를 들어, 샌드박스에 담긴 다음 코드를 생각해봅시다.

/* 0 에서 16 사이의 값을 리턴.  */

int return_the_value();

우리는 샌드박스에 담긴 코드가 약속대로 동작한다고 보장할 수 없습니다.  우리는 함수의 리턴 값이 약속된 범위 안에 있도록 보장할 방법이 필요합니다.

비슷하게, 샌드박스에 담긴 어떤 함수가 포인터를 리턴하는 경우를 생각해 봅시다.

extern const char* do_the_thing();

우리는 리턴되는 포인터가 실제로 샌드박스가 관리하는 메모리 영역을 가리킨다고 보장할 수 없습니다. 누군가 악의적으로 샌드박스 밖의 어딘가를 가리키는 포인터를 리턴하게 만들었을 수 있습니다.  그래서, 우리는 리턴되는 포인터를 사용하기 전에 그 값이 적절한지 확인해야 합니다.

게다가 소스코드를 읽어서는 분명하게 알 수 없는 또다른 런타임 규칙들이 있습니다. 예를 들어, 앞서 함수에서 리턴되는 포인터가 샌드박스가 동적으로 할당한 메모리를 가리킨다고 합시다. 그런 경우, 그 포인터는 호스트 어플리케이션이 아니라 샌드박스에 의해 해지되어야 합니다. 우리는 개발자가 어플리케이션 변수와 샌드박스 변수를 철저하게 구별해줄 것을 바랍니다. 하지만 경험에 의하면 이것은 절대 기대할 수 없는 바램입니다.

오염된 데이터

앞서 2개의 예제는 일반적인 원칙을 드러냅니다. 샌드박스에서 리턴되는 데이터에는 샌드박스에서 리턴되는 값임을 식별할 수 있는 표시가 있어야 합니다. 이런 식별 장치가 있으면, 우리는 데이터가 적절한 방식으로 처리되리라 확신할 수 있습니다.

우리는 샌드박스와 관련된 데이터에 “오염됨 (tainted)” 이라고 라벨을 붙입니다.  오염된 데이터는 자유롭게 조작할 수 있으며 (e.g. 포인터 연산, 필드 데이터 접근), 이런 조작은 또 다른 오염된 데이터를 만듭니다.  하지만 오염된 데이터를 오염되지 않은 데이터로 바꿀 때는, 그런 행위가 가능한 한 명시적이기를 바랍니다. 오염(taintedness)이라는 개념은 샌드박스에서 리턴된 메모리를 다룰 때만 유용한 것이 아닙니다.  오염 개념은 샌드박스에서 리턴된 데이터에 추가적인 검증이 필요하다는 것을 표시할 때도 유용합니다. 예를 들어 어떤 외부 배열을 가리키는 인덱스 같은 것 말입니다.

우리는 그래서 샌드박스 안에 담긴 모든 함수가 오염된 데이터를 리턴하는 것으로 정의했습니다. 그리고 그 함수들의 인자도 역시 오염된 데이터입니다. 왜냐하면 그 함수들이 조작하는 데이터는 어떻게든 반드시 샌드박스에 속해야 하기 때문입니다. 일단 함수 호출 규격을 이렇게 만들면, 우리는 컴파일러를 데이터의 오염 여부를 체크하는 도구로 사용할 수 있습니다. 오염된 데이터가 오염되지 않은 데이터를 요구하는 영역에서 사용될 경우, 또는 그 반대의 경우 컴파일러는 에러를 발생시킬 것입니다. 이런 영역이 바로 오염된 데이터가 전파되거나 그리고/또는 검증되어야 하는 영역입니다. RLBox가 오염된 데이터(tainted data)에 관한 잡다한 모든 것을 처리합니다. 그리고 라이브러리 인터페이스를 샌드박스 인터페이스로 손쉽게 변환시키는 기능들을 제공합니다.

다음 계획

wasm 샌드박스의 핵심 인프라를 이용해서, 우리는 wasm 샌드박스가 Firefox 코드베이스에 미치는 긍정적 효과를 확대하는 데 집중할 수 있었습니다. 그래서 wasm 샌드박스를 우리가 지원하는 모든 플랫폼으로 확장했고, 또 wasm 샌드박스를 보다 많은 컴포넌트에 적용했습니다. 이 기술이 가벼울 뿐더러 사용하기 쉽기 때문에, 우리는 몇 달 이내에 Firefox 의 더 많은 부분에 샌드박스를 적용할 수 있을 것 같습니다. 우리는 Firefox 와 함께 번들되는 써드파티 라이브러리들에 먼저 노력을 집중하고 있습니다. 이런 라이브러리들은 일반적으로 잘 정의된 진입점 (entry points)을 갖고 있으며 나머지 시스템과 과도하게 메모리를 공유하지 않습니다. 미래에는 Firefox 의 핵심 코드에도 이 기술을 적용할 것입니다.

일러두기

UCSD, UT Austin, Stanford 의 리서치 파트너들의 업적에 깊이 감사 드립니다. 그들의 노력이 이 작업의 추진력이 돼주었습니다. 아울러 Bytecode Alliance 의 파트너들에게 – 특히 Fastly 의 엔지니어링 팀에게 – 특별한 감사를 전합니다. 그들은 Lucet 을 개발했고 이번 프로젝트가 성공할 수 있도록 Lucet 의 기능을 확장해주었습니다.

작성자: ingeeKim

"누구에게나 평등하고 자유로운 웹"에 공감하는 직장인.

ingeeKim가 작성한 문서들…


댓글이 없습니다.

댓글 쓰기