샌드박스
고친 사람 github-actions[bot]
샌드박스는 믿기 어려운 코드를 좁은 울타리 안에서만 돌게 가두는 방법입니다. 그 코드가 잘못 움직여도 울타리 밖의 파일과 시스템은 다치지 않습니다. 브라우저가 웹 페이지의 스크립트를 돌릴 때 이 방법을 씁니다. 결제 서비스의 「샌드박스 환경」처럼 진짜와 떨어진 연습용 환경을 가리키는 뜻도 있습니다.
쉽고 빠른 이해
남이 만든 코드를 돌리되, 그 코드가 손댈 수 있는 범위를 미리 좁혀 두는 방법입니다. 브라우저에서 모르는 사이트의 스크립트가 돌아도 내 컴퓨터 파일을 못 읽는 것이 이 덕분입니다.
코드에는 언제든 버그나 악의가 있을 수 있습니다. 가두지 않으면 코드 하나가 뚫렸을 때 그 프로그램이 가진 권한을 공격자가 전부 씁니다.
어떻게 도나:
- 코드를 따로 떼어 낸 프로세스나 실행기 안에서 돌립니다. 실행기는 코드를 읽어 돌려 주는 프로그램입니다(자바스크립트 엔진 같은)
- 그 코드가 운영체제에 요청할 수 있는 일을 목록으로 좁힙니다
- 목록 밖의 요청은 거절하거나 프로세스를 끝냅니다
대가도 있습니다. 검사를 거치느라 느려집니다. 정상 기능이 막혀 따로 길을 내 줘야 할 때도 생깁니다.
그래서 바깥에서 온 코드나 입력을 다루는 곳에 씁니다. 외부 입력이 없는 곳에는 들인 비용만큼 얻는 것이 적어 잘 쓰지 않습니다.
상세
아이들이 노는 모래판을 떠올려 봅니다. 모래판 안에서는 성을 쌓든 무너뜨리든 마음대로 합니다. 모래가 판 밖으로 넘어가지 않으니 거실은 깨끗합니다.
샌드박스는 운영체제나 실행기가 끝까지 지켜 넘어가지 못하게 합니다. 코드가 닿을 수 있는 자원을 미리 정해 두고, 그 밖으로 나가려는 시도를 막는 방법입니다. 여기서 자원은 파일, 네트워크, 다른 프로세스, 장치 같은 것입니다. 예를 들어 이미지 변환기를 샌드박스에 넣으면, 받은 이미지를 읽고 결과를 돌려주는 일만 합니다. 같은 변환기가 다른 파일을 지우거나 외부로 접속하려 하면 거절당합니다.
가두어야 하는 까닭
샌드박스는 두 종류의 코드를 겨냥합니다. 하나는 처음부터 믿을 수 없는 코드입니다. 웹 페이지의 스크립트, 사용자가 올린 플러그인, 이메일에 붙은 문서가 그렇습니다.
다른 하나는 내가 쓴 코드인데 바깥 입력을 받아 처리하는 코드입니다. 이미지나 문서 파일을 읽는 파서는 입력이 조금만 이상해도 버그가 드러나기 쉽습니다. 공격자는 그 버그를 이용해 파서 안에서 자기 코드를 돌립니다.
가두지 않으면 뚫린 코드가 그 프로그램의 권한을 전부 넘겨받습니다. 서버 프로세스가 관리자 권한으로 돌고 있었다면, 웹 요청만 보낼 수 있던 공격자가 서버의 관리자 권한까지 얻습니다. 이렇게 처음 가진 것보다 넓은 권한을 얻는 공격을 권한 상승이라고 합니다.
샌드박스는 이 넘겨받는 권한을 처음부터 작게 만듭니다. 필요한 권한만 주는 원칙을 최소 권한 원칙이라고 부릅니다. 샌드박스는 프로그램 전체가 아니라 위험한 입력을 만지는 부분에만 권한을 좁게 줍니다. 뚫리는 일을 막는 것이 아니라 뚫렸을 때 피해 범위를 줄이는 것이 목표입니다.
가두는 방식 두 갈래
코드를 가두는 쪽이 누구냐에 따라 방식이 둘로 나뉩니다. 운영체제가 프로세스를 가두는 방식과, 코드를 돌리는 실행기가 스스로 가두는 방식입니다.
| 가두는 쪽 | 어떻게 막나 | 흔히 보는 곳 |
|---|---|---|
| 운영체제 | 프로세스가 커널에 하는 요청을 걸러 내고, 보이는 파일과 프로세스를 줄입니다 | 브라우저의 탭 프로세스 · 모바일 앱 · 컨테이너 |
| 실행기 | 코드가 쓸 수 있는 함수만 실행기가 넘겨줍니다 | 자바스크립트 엔진 · WebAssembly 실행기 |
운영체제 방식부터 봅니다. 프로그램이 파일을 열거나 네트워크로 접속하려면 커널에 요청해야 합니다. 이 요청을 시스템 콜이라고 합니다. 샌드박스는 허락할 시스템 콜의 목록을 정해 두고 나머지를 거절합니다.
리눅스에서는 이 거르기를 seccomp(secure computing mode)가 맡습니다. seccomp 는 목록에 없는 시스템 콜을 막습니다. 코드가 무엇을 하려 하든 목록에 없는 일은 커널까지 가지 못합니다.
운영체제 방식에는 보이는 범위를 좁히는 기능도 있습니다. 리눅스의 네임스페이스는 프로세스가 보는 파일 시스템과 프로세스 목록 자체를 줄입니다. 보이지 않는 파일은 열라고 요청할 수도 없습니다.
쓰는 양을 묶는 기능도 따로 있습니다. cgroup(control group)은 프로세스가 쓸 수 있는 처리 시간과 메모리의 양을 정합니다. 무엇을 부르느냐가 아니라 얼마나 쓰느냐를 막아, 갇힌 코드가 자원을 다 써서 기계를 멈추게 하는 일을 막습니다.
실행기 방식은 관문이 커널이 아니라 실행기 안에 있습니다. 웹 페이지의 자바스크립트는 파일을 여는 함수를 아예 받지 못합니다. 없는 함수는 부를 수 없으니 막을 일도 생기지 않습니다.
아래 그림은 두 방식의 관문이 어디에 있는지 나란히 보입니다. 운영체제 방식은 커널 앞에서 거르고, 실행기 방식은 코드에 건네는 함수부터 줄입니다.
flowchart TD
subgraph OS["운영체제가 가두는 샌드박스"]
A["갇힌 프로세스"] --> B["시스템 콜"]
B --> C{"커널 앞 관문: 허락 목록에 있나"}
C -->|있다| D["커널이 처리한다"]
C -->|없다| E["거절하거나 프로세스를 끝낸다"]
end
subgraph RT["실행기가 가두는 샌드박스"]
F["갇힌 코드"] --> G{"실행기 안 관문: 건네받은 함수인가"}
G -->|건네받았다| H["실행기가 대신 처리한다"]
G -->|없다| I["부를 수조차 없다"]
end
권한이 필요한 일은 바깥에 부탁한다
가두기만 하면 정상 기능도 못 합니다. 사용자가 고른 파일을 업로드하려면 누군가는 그 파일을 읽어야 합니다. 그래서 흔히 프로그램을 둘로 나눕니다.
권한이 없는 쪽은 위험한 입력을 처리합니다. 브라우저라면 웹 페이지를 그리는 탭 프로세스가 권한 없는 쪽입니다. 권한이 있는 쪽은 입력을 만지지 않습니다. 요청을 검사해 필요한 일만 대신 해 줍니다. 이 대신 해 주는 쪽을 브로커 프로세스라고 부릅니다.
sequenceDiagram
participant 탭 as 탭 프로세스
participant 브로커 as 브로커 프로세스
participant 운영체제
탭->>브로커: 사용자가 고른 파일을 달라
Note over 브로커: 사용자가 정말 골랐는지 검사한다
브로커->>운영체제: 그 파일 하나만 연다
운영체제-->>브로커: 파일
브로커-->>탭: 파일 내용만 넘긴다
탭 프로세스는 끝까지 파일 시스템에 직접 닿지 않습니다. 탭이 뚫려도 공격자가 얻는 것은 「브로커에게 요청할 수 있는 권한」뿐입니다. 브로커가 요청을 꼼꼼히 검사하는 한 피해가 좁게 머뭅니다.
컨테이너 · 가상 머신과 견주기
컨테이너와 가상 머신도 코드를 떼어 놓습니다. 셋은 떼어 놓는 경계가 어디에 있느냐로 갈립니다.
| 경계 | 커널 | 경계가 뚫리면 | |
|---|---|---|---|
| 운영체제가 가두는 샌드박스 | 한 프로그램 안의 프로세스 | 호스트와 같이 씁니다 | 그 프로그램이 가진 권한까지 |
| 컨테이너 | 프로세스 묶음 | 호스트와 같이 씁니다 | 커널 버그를 타면 호스트까지 |
| 가상 머신 | 운영체제 한 벌 | 따로 돕니다 | 하이퍼바이저를 넘어야 호스트에 닿습니다 |
컨테이너는 네임스페이스와 cgroup 으로 만든 넓은 샌드박스라고 볼 수 있습니다. 커널을 같이 쓰기 때문에, 신뢰가 낮은 코드를 돌릴 때는 컨테이너 안에 시스템 콜 거르기를 한 겹 더 씌웁니다.
샌드박스 탈출
샌드박스를 벗어나 바깥 권한을 얻는 공격을 샌드박스 탈출이라고 합니다. 허락 목록에 남은 시스템 콜의 버그나, 브로커의 검사 누락이 주된 통로입니다. 컨테이너에서 호스트로 나가는 컨테이너 탈출도 같은 꼴입니다.
그래서 샌드박스 하나에 모든 것을 맡기지 않습니다. 먼저 코드 안에서 입력을 검사합니다. 그 코드를 샌드박스로 가둡니다. 브로커를 포함한 프로그램 전체도 권한이 낮은 계정으로 돌립니다. 이렇게 겹겹이 막는 방법을 심층 방어라고 합니다.
대가와 쓰는 때
샌드박스는 공짜가 아닙니다. 시스템 콜마다 검사를 거치니 느려집니다. 프로세스를 나누면 메모리도 더 씁니다. 허락 목록을 너무 좁히면 정상 기능이 깨집니다. 반대로 넓히면 막는 효과가 줄어듭니다.
그래서 바깥에서 온 코드나 입력을 다루는 부분에 먼저 씁니다. 사용자가 올린 파일 변환, 고객이 쓴 스크립트 실행, 모르는 웹 페이지 표시가 대표적입니다. 내 코드끼리만 오가고 외부 입력이 없는 부분은 비용에 비해 얻는 것이 적습니다.
같은 이름의 다른 뜻
API(Application Programming Interface) 제공자가 말하는 「샌드박스」는 결이 다릅니다. 진짜 돈이나 데이터가 움직이지 않는 연습용 환경입니다. 결제 서비스의 샌드박스 계정으로 결제를 보내면 흐름은 같지만 실제 청구는 일어나지 않습니다.
두 뜻은 「밖에 영향이 안 간다」는 점만 같습니다. 보안의 샌드박스는 코드를 가둡니다. 테스트의 샌드박스는 환경을 떼어 놓습니다. 이 항목은 보안의 뜻을 다룹니다.
관련 항목
샌드박스가 막으려는 공격
권한 상승 · 샌드박스 탈출 · 컨테이너 탈출 · 원격 코드 실행 · 메모리 손상 취약점
샌드박스를 이루는 운영체제 기능
시스템 콜 · seccomp · 네임스페이스 · cgroup · chroot · 케이퍼빌리티 · 브로커 프로세스
샌드박스를 받치는 보안 원칙
최소 권한 · 격리 · 심층 방어 · 권한 분리 · 공격 표면
코드를 떼어 놓는 다른 수단
컨테이너 · 가상 머신 · 하이퍼바이저 · 가상화 · 프로세스
샌드박스를 두고 코드를 돌리는 실행 환경
브라우저 · 자바스크립트 엔진 · WebAssembly · 앱 샌드박스 · 사이트 격리
샌드박스라는 이름을 같이 쓰는 개발 용어
테스트 환경 · 스테이징 환경 · 테스트 더블
다른 이름: sandbox · sandboxing · 샌드박싱