주의: 저명한 Rust 개발자를 겨냥한 표적 공격
요약
본 글은 Rust 생태계의 의존성 관리와 빌드 시스템(특히 `build.rs`)이 가진 심각한 공급망 공격 위험을 경고합니다. 간접 의존성의 변조만으로도 사용자 권한 접근 및 대규모 피해가 가능하며, 이는 단순히 언어만의 문제가 아닌 빌드 시스템 상호작용의 문제입니다.
핵심 포인트
- 간접 의존성 탈취는 대규모 공급망 공격에 매우 취약함.
- build.rs는 컴파일 중 사용자 권한으로 임의 코드를 실행할 수 있어 위험함.
- 문제의 핵심은 언어 자체가 아닌 빌드 시스템 간 상호작용에 있음.
- Nix, bubblewrap 등을 활용하여 개발 환경을 격리하는 것이 중요함.
유명한 사람은 아니지만, 평소에도 “코덱이 없다”는 메시지를 의심하고 모르는 상대의 연락을 스팸이나 사기 가능성이 있는 것으로 취급해 온 건 다행임.
이제 개발 환경 개편을 서둘러 Rust 도구 모음을 Docker 컨테이너 안에만 두려 함. rust-analyzer가 실수로 변조된 패키지를 아무 제약 없이 실행하지 못하게 하려는 목적임. 이미 가능한 한 많은 간접 의존성을 WASIp2로 제한한 플러그인으로 옮기고 있으며, API도 nanoprocess sandboxing 원칙에 맞춰 설계 중임.
최근 systemd 프로젝트에서 Rust 도입을 검토하면서, 주요 유지보수자 중 한 명인 Luca Boccassi가 짚어볼 만한 우려를 제기함. Cargo와 crates.io에 대한 과장을 걷어내면 핵심은 간접 의존성 하나만 탈취돼도 대규모 공급망 공격이 가능하다는 것임. 특히 build.rs는 컴파일 중 사용자 권한으로 임의 코드를 실행하므로 브라우저 쿠키나 SSH 키까지 접근할 수 있고, 최종 사용자뿐 아니라 개발자도 위험해짐. 나머지 토론은 양쪽의 감정싸움으로 흐르니 굳이 읽지 않아도 됨.
개발 환경을 더 안전하게 만들 방법을 고민하다 보면, 나조차 build.rs 파일을 너무 당연하게 받아들이고 있다는 사실이 섬뜩해짐. 깊숙이 묻힌 간접 의존성이 변조되면 파급력이 엄청날 수 있음. LSP 서버가 연결된 편집기로 프로젝트를 열기만 해도 초기에 cargo check가 실행돼 이런 코드가 작동할 수 있음.
Rust의 풍부한 라이브러리는 수많은 선행 작업에 기댈 수 있는 강점인 동시에, 그중 누구 하나의 악의나 부주의만으로도 큰 피해를 입을 수 있는 취약점임. 언어 도구에 내장된 빌드 스크립트도 마찬가지로 양날의 검임. Zig, Swift, JavaScript 등에도 유사한 기능이 있으므로 Rust만의 문제는 아님. //! 문서 주석을 호스팅하는 docs.rs는 강하게 제한된 컨테이너 안에서 빌드하는 방식으로 대응함. rubydoc.info도 최근 비슷한 메커니즘을 이용한 OpenAI 봇들의 대규모 공격을 겪음.
Makefile도 임의 코드를 실행할 수 있다는 사실이 현재 방식의 안전성을 입증해 주지는 않음. 의존성을 명령 하나로 추가하고, 이후에는 GitHub 자동화 봇이 갱신하게 둔 채 다음 사고가 날 때까지 신경 쓰지 않는 관행이 위험을 키운다고 봄.
내 작은 PostgreSQL 기반 웹 애플리케이션은 패키지 메타데이터를 제외한 Cargo.toml이 25줄인데, cargo tree에서 고유한 패키지 이름을 세면 239개나 나옴. 같은 이름이지만 시맨틱 버전 호환성이 없는 패키지는 별도로 세지도 않은 수치임. Cargo는 버전 범위가 겹치는 호환 의존성을 하나로 합침.
더 나은 해법을 제시할 만큼 언어 구현에 능숙하지는 않지만, 지금 상태가 이상적이지는 않음. go generate는 의존성을 순회하지 않아 피해 범위가 작지만, 주로 코드 생성에 쓰이는 기능이라 Makefile에 가까운 build.rs를 대체하기는 어려움. 의존성 업데이트에 대기 기간을 두는 방식도 그 모델로 해결할 수 없는 다른 문제를 만들기에, 확실한 개선이라기보다는 장단점을 맞바꾸는 정도로 봄.
당장은 Nix와 bubblewrap을 함께 사용함. 시간이 나면 systemd-nspawn도 살펴볼 예정이며, bubblewrap README의 설명과 달리 루트 권한 없이 실행하는 모드도 지원함.
의존성 분석 결과를 어딘가에 공개하면 좋겠음. 의존성 구조는 미묘할 수 있어 개수만으로는 판단하기 어려움. 하나의 프로젝트가 모듈화를 위해 크레이트를 10~20개로 나눠 배포하기도 함.
복잡하게 얽혀 있지만, 대부분은 프로그래밍 언어보다는 빌드 시스템 간 상호작용의 문제임. 언어나 컴파일러에도 영향이 있으나 핵심은 빌드 시스템에 있고, 해야 할 일이 많지만 쉬운 해결책은 없음.
특히 거의 모든 도구가 C 빌드 시스템을 다뤄야 하는데, 이 부분은 여전히 제대로 풀리지 않음. 그 요구가 없다면 build.rs도 필요하지 않을 수 있지만, 자유도를 낮춘 대체 방식은 대부분 C를 처리해야 하는 순간 한계에 부딪힘. 그런데 C 지원은 피할 수 없음.
빌드 중 임의 코드를 실행하는 것 자체는 잘못이라고 보지 않음. 개발자라면 대개 빌드 직후 그 프로그램을 실행할 생각일 테고, 배포용 바이너리만 만드는 배포판 유지보수자라면 빌드 시스템의 안전성에 기대기보다 애초에 샌드박스에서 빌드해야 함.
진짜 문제는 LSP가 임의 코드를 실행한다는 설계임. 끔찍한 선택이며, LSP 서버에서 이런 동작을 당연하게 받아들이는 듯한 분위기 때문에 LSP 생태계 전체에 정이 떨어짐.
오픈소스에 의존하는 소프트웨어, 즉 사실상 거의 모든 소프트웨어에는 공격 경로가 될 수 있는 사람들의 연결망이 존재함. 의존성 그래프에 있는 패키지 중 하나라도 배포 권한을 가진 사람은 모두 공격 대상이 될 수 있어 정말 무서움.
지금으로서는 의존성 업데이트 대기 기간을 두는 것이 최선의 방어책 같음. 새 버전이 나온 뒤 며칠 기다리면서, 그사이에 다른 누군가 공급망 위험을 발견해 주기를 기대하는 방식임.
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기