TECH 으로 돌아가기
TECH HACKER NEWS 오늘 8분 읽기 22 READS

샌드박싱의 기본기: 왜 프로세스와 액터 모델이 정답에 가까운가

소프트웨어 샌드박싱은 여전히 지도가 제대로 그려지지 않은 영역이다. 좋은 샌드박스를 구현하는 데 필요한 조각들은 여기저기 흩어져 있고, 선구자들조차 신참들을 안전한 항로로 안내할 통합된 '세계 지도'를 아직 완성하지 못했다. Emilua 프로젝트에서 샌드박싱 지원을 개발해 온 개발자가 정리한 이 글은, 오해를 줄이기 위해 다소 반복을 감수하면서 그 경험을 공유한다. 한국의 서버·보안 실무자에게도 새겨둘 만한 관점이 담겨 있다.

논의의 출발점은 정의다. 2009년 Hack In The Box 말레이시아에서 Julien Tinnes와 Chris Evans는 샌드박싱을 '머신에 대한 관리 권한 없이 프로세스의 권한을 제한하는 능력'으로 규정했다. 이 정의가 중요한 이유는, 시스템 관리자가 파일시스템 권한 같은 수단으로 세워둔 정책을 서드파티 프로그램이 함부로 바꿀 수 있다면 그 정책 자체가 무력화되기 때문이다. 게다가 애플리케이션은 저마다 고유한 가상 세계를 추상화하는데, 트위터 피드를 누가 볼 수 있는지를 UNIX 권한 모드로 정하지 않듯, 전통적인 파일 권한은 이런 정책을 표현하기에 적합하지 않다.

관리자 API에 기대는 낡은 방식의 함정

그럼에도 프로세스는 결국 OS 위에서 돌아가고, 파일 같은 커널 자원과 상호작용한다. 개발자에게 중요한 것은 바로 이 인터페이스다. 예컨대 파이어폭스가 DRM 플러그인을 실행할 때, 그 서드파티 코드가 사용자 홈 디렉터리 전체에 접근하도록 두는 것은 바람직하지 않다. 그런데 setuidgid 같은 전통적 도구는 여기서 도움이 되지 않는다. 애초에 개발자가 프로그래밍적으로 쓰라고 만든 인터페이스가 아니기 때문이다. 이 간극이 실제로 문제가 되는 OS들은 FreeBSD의 Capsicum, 리눅스의 Seccomp처럼 전통 UNIX를 넘어서는 확장 인터페이스를 제공한다.

좋은 인터페이스가 없던 시절, 개발자들은 슈퍼유저 전용 메커니즘을 남용해 샌드박스를 만들었다. 대표적인 것이 chroot 감옥을 설정하는 suid 헬퍼 바이너리다. 그러나 아무 프로그램에나 suid 바이너리 설치를 허용하는 순간 보안은 무너진다. suid는 사실상 시스템 전체에 대한 관리 권한을 일시적으로 끌어올리는 것과 같은데, 최소 권한 원칙에 따르면 권한은 오직 줄어들 뿐 결코 늘어나서는 안 된다.

리눅스 네임스페이스는 왜 샌드박싱 도구로 부적절한가

또 하나의 함정은 커널 공격 표면을 기하급수적으로 키우는 API 설계다. 도커 붐과 함께 대중화된 리눅스 네임스페이스가 그런 사례다. 중첩된 유저 네임스페이스 안에서 프로세스는 (그 네임스페이스 한정으로) 슈퍼유저가 되고, 원래 슈퍼유저에게만 열려 있던 커널 코드 경로가 모든 사용자에게 노출된다. 10년 넘게 축적된 커널 코드가 애초에 그런 전제로 작성되지 않았다는 점이 문제다. Andy Lutomirski는 CLONE_NEWUSER로 임의의 네트워크 네임스페이스에 대해 CAP_NET_ADMIN을 얻어 iptables까지 다룰 수 있게 되는 상황을 두고 '여기에 권한 상승 버그가 없다면 내 모자를 먹겠다'며 위험성을 경고했다.

결론적으로 유저 네임스페이스는 도커 같은 신뢰된 컨테이너화 도구에 한정해 허용하는 것이 맞고, 범용 샌드박싱 메커니즘으로는 나쁜 선택이다. 리눅스의 Landlock 같은 최신 인터페이스는 이런 재앙을 피하도록 공격 표면을 늘리지 않게 설계됐다. 글쓴이 역시 Emilua 초기 몇 년을 네임스페이스 연구에 쏟았으나 좌절 끝에 방향을 틀었고, 지금은 네임스페이스를 컨테이너화 도구 제작용으로만 남겨두고 실제 샌드박싱에는 다른 메커니즘을 쓴다. 이런 느슨한 정의와 구분하기 위해 Tinnes는 이 글이 다루는 개념을 '재량적 권한 축소(discretionary privilege dropping)'라 불렀는데, 이는 시스템 관리 정책을 대체하는 것이 아니라 함께 채택돼 상호 보완하는 관계다.

프로세스 경계와 액터 모델

그렇다면 실제 OS에서 어떻게 권한을 떨어뜨릴까. 오늘날 주류 OS에서 권한 경계는 프로세스 단위에 있다. 커널은 프로세스에 결합된 자격(credential)을 검사해 자원 획득 여부를 판단한다. 리눅스는 예외적으로 스레드 단위로 자격을 두지만, 스레드 수준에 뿌리를 둔 설계는 제대로 작동하지 않으며 그래서 glibc가 스레드 간 자격을 동기화하는 추가 작업을 한다. GNOME 개발자들이 스레드 수준으로 처리할 수 있다고 여겼다가 CVE-2023-43641로 틀렸음이 드러난 사례가 이를 보여준다. Adam Langley가 신뢰된 헬퍼 스레드를 어셈블리로 작성하는 이론적 대안을 제시하긴 했지만, 경제적으로 지나치게 비싸 현실성이 없다.

결국 지금 우리가 가진 것은 프로세스다. 프로그램을 별개의 프로세스로 구획화한 뒤에는 각 구획에 서로 다른 권한을 부여하고, 구획 간 통신을 처리해야 한다. Capsicum 연구진이 오래전 지적했듯 구획화된 애플리케이션 개발은 필연적으로 메시지 전달로 통신하는 분산 애플리케이션 개발이 된다. 여기서 액터 모델이 유용하다. 액터는 자기 내부 상태를 관리하고, 다른 액터에게 메시지를 보내며, 메시지에 다른 액터의 주소를 담을 수 있다. 또한 한 액터가 자기 자신과 병렬로 실행되지는 않지만 스레드 사이를 옮겨 다니는 것은 허용되는데, 이는 Boost.Asio의 strand와 같은 성질이다.

Emilua는 각 프로세스를 하나의 액터로 삼고, UNIX 도메인 소켓을 액터 메시징에 활용한다. 새 액터를 스폰할 때 소켓 상속을 설정하며 이 소켓이 곧 액터의 주소가 된다. UNIX 도메인 소켓으로는 파일 디스크립터를 전송할 수 있으므로, '모든 것은 파일 디스크립터'라는 UNIX 문화의 격언대로 /dev/random이나 GPU 통신 같은 장치 노드를 포함한 폭넓은 자원을 제한된 프로세스에 넘겨줄 수 있다. 핵심은 파일 디스크립터가 엉뚱한 액터에게 새지 않음을 증명하는 것이며, 이 문제를 풀어주는 잘 연구된 틀이 바로 능력 기반 보안(capability-based security)이다. 액터 모델과 능력 기반 보안을 함께 구현한 Pony 언어가 그 실증 사례다. 실무자 입장에서 얻을 교훈은 분명하다. 화려한 격리 기능보다, 프로세스 경계와 명시적 권한 위임이라는 견고한 토대 위에서 샌드박스를 설계해야 한다는 점이다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://blog.emilua.org/2025/01/12/software-sandboxing-basic...
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기
처리 중...