TECH 으로 돌아가기
TECH HACKER NEWS 오늘 7분 읽기 26 READS

샌드박스로 신뢰할 수 없는 입력을 격리하는 C 웹 라이브러리, kcgi

웹 애플리케이션을 C나 C++로 직접 작성하는 일은 오늘날 흔치 않다. 대부분의 실무자는 고수준 언어와 프레임워크를 쓰고, C는 성능이나 시스템 제약이 명확한 영역으로 밀려났다. 그럼에도 CGI와 FastCGI라는 오래된 인터페이스는 여전히 살아 있고, 특히 OpenBSD처럼 보안을 중시하는 환경에서는 작고 감사 가능한 코드를 선호하는 전통이 강하다. kcgi는 바로 그 지점을 겨냥한 오픈소스 라이브러리다. 스스로를 "최소한의, 안전한, 감사 가능한" 보안 지향 C/C++ 웹 라이브러리라고 소개한다.

kcgi가 다루는 기능 자체는 CGI 라이브러리로서 그리 특이하지 않다. 쿼리 문자열, 쿠키, 페이지 본문, multipart 등 모든 HTTP 입력을 검증과 함께 처리하고, 자동 압축, 인증, 설정 가능한 출력 캐싱, 요청 디버깅, JSON·XML 같은 포맷 작성기를 제공한다. 그러나 kcgi가 다른 라이브러리와 구분되는 핵심은 기능 목록이 아니라 신뢰할 수 없는 입력을 처리하는 방식에 있다. 바로 샌드박싱과 프로세스 분리다.

신뢰할 수 없는 입력 경로를 프로세스로 분리하다

kcgi의 CGI 처리 대부분은 HTTP 요청을 완전히 파싱하는 khttp_parse 함수에 담겨 있고, 애플리케이션은 다른 모든 함수보다 이 함수를 먼저 호출해야 한다. FastCGI에서는 컨텍스트를 초기화하는 khttp_fcgi_init과 새 요청을 받는 khttp_fcgi_parse로 역할이 나뉜다. 어느 경우든 요청은 khttp_free로 해제해야 한다. 여기까지는 전형적인 생명주기지만, 그 내부 구조가 핵심이다.

kcgi는 네트워크에서 들어온 신뢰할 수 없는 데이터의 파싱과 검증을 샌드박스화된 자식 프로세스 안에 가둔다. 샌드박스는 프로세스가 접근할 수 있는 환경을 제한하기 때문에, 파싱 과정이나 서드파티 라이브러리를 통한 검증에서 악용 가능한 오류가 발생하더라도 시스템 환경까지 손을 뻗지 못한다. 파싱된 데이터는 소켓을 통해 부모 프로세스로 전달된다. 즉 개별 HTTP 요청을 다루는 파서·검증기와, 새 연결을 받아 넘기는 연결 위임자가 서로 다른 샌드박싱 규칙 아래 분리되어 돌아간다. 이 방식은 OpenSSH의 권한 분리 모델을 따르며, 운영체제마다 별도의 처리를 요구한다.

실무자가 놓치기 쉬운 전제들

이 설계는 애플리케이션 개발자에게 한 가지 중요한 제약을 부과한다. 검증이 샌드박스 안에서 일어나기 때문에, 검증 루틴이 파일을 열거나 네트워크 연결을 맺는 식으로 환경에 접근해서는 안 된다. 일부 샌드박스 facility는 그런 접근을 시도하는 자식 프로세스를 즉시 종료시킬 수 있기 때문이다. 환경 접근이 꼭 필요한 검증이라면 파싱 검증 단계가 끝난 뒤로 미뤄야 한다. 이는 기존 CGI 코드를 그대로 옮겨오려는 개발자가 반드시 염두에 둬야 할 부분이다.

배포 환경에 대한 전제도 분명하다. OpenBSD 기본 서버처럼 잘 구성된 웹 서버는 chroot 안에서 동작하는 경우가 많은데, 이럴 때는 바이너리를 정적으로 링크하거나 필요한 공유 라이브러리를 shadow root 안으로 끌어와야 한다. FastCGI 애플리케이션은 웹 서버가 직접 띄울 수도 있고(아파치에서 흔한 방식), 외부에서 소켓과 kfcgi 래퍼로 띄울 수도 있다. 후자는 OpenBSD httpd의 표준 방식이며 래퍼가 취하는 보안 조치 때문에 권장된다. 빌드 측면에서 하드 의존성은 BSD make(리눅스에서는 bmake)와 zlib뿐이고, 회귀 테스트를 돌릴 때만 libcurl이 추가로 필요하다. 리눅스(musl·glibc), 각종 BSD, Solaris, OmniOS, 그리고 Mojave 이상의 macOS까지 폭넓게 지원한다.

검증을 위한 테스트와 퍼징, 그리고 성능이라는 대가

보안 라이브러리답게 자체 검증 장치도 갖췄다. make regress로 실행되는 자동화 테스트 프레임워크는 로컬 포트에서 대기하며 HTTP 문서를 최소한의 CGI 요청으로 번역해 kcgi 클라이언트에 전달하는 미니 웹서버처럼 동작한다. 바인딩 포트가 고정되어 있어 회귀 스위트를 돌릴 때 접근 포트를 조정해야 할 수 있다는 점은 실무적으로 기억해둘 만하다. GitHub Actions가 이 프레임워크로 모든 커밋을 지원 시스템 전반에서(컴파일러 새니타이저 포함) 검증한다. 여기에 더해 American fuzzy lop을 위한 별도 퍼징 프레임워크도 제공되어 multipart, plain, urlencoded 입력 경로를 각각 퍼징할 수 있다.

물론 이 모든 안전성에는 비용이 따른다. kcgi는 설계상 세 가지 지점에서 오버헤드를 감수한다. 신뢰할 수 없는 데이터를 처리할 자식 프로세스를 띄우는 비용, 샌드박스 프레임워크를 가동하는 비용, 그리고 파싱된 쌍을 부모 컨텍스트로 되돌리는 전송 비용이다. CGI로 실행할 때 성능은 운영체제가 프로세스를 생성하고 회수하는 능력에 묶이고, FastCGI에서는 병목이 데이터 전송 쪽으로 옮겨간다. 결국 kcgi는 범용 고성능 웹 스택을 대체하려는 물건이 아니다. 공격 표면을 좁히고 신뢰 경계를 프로세스 수준에서 명시적으로 긋는 것이 성능보다 우선인 환경, 그리고 코드 한 줄까지 감사하려는 팀에게 설득력 있는 선택지다. 안전성과 성능 사이의 이 교환을 어디서 받아들일지가 도입 판단의 핵심이 된다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://kristaps.bsd.lv/kcgi/
SHARE
NEXT · CHOOSE

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

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

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