공급망
종속성 혼동이란 무엇인가요?
종속성 혼동(dependency confusion)은 빌드가 같은 이름의 비공개 패키지 대신 공격자의 공개 패키지를 설치하게 되는 공격입니다. 아무도 오타를 내지 않습니다. 공격자는 내부 패키지 이름을 알아내 높은 버전 번호로 공개 레지스트리에 게시하기만 하면 되고, 나머지는 잘못 구성된 빌드가 알아서 합니다.
업데이트
종속성 혼동 공격의 작동 방식
- 내부 이름을 찾습니다. 비공개 패키지 이름은 공개 코드, JavaScript 번들, 오류 메시지, 리포지토리에 남은 매니페스트를 통해 유출됩니다.
- 같은 이름을 공개적으로 게시합니다. 공격자는 그 이름을 npm, PyPI 또는 다른 공개 레지스트리에 매우 높은 버전 번호로 등록합니다.
- 빌드를 기다립니다. 내부 레지스트리와 공개 레지스트리를 모두 확인하도록 설정된 패키지 관리자는 후보 두 개를 찾고, 더 높은 버전을 선택합니다.
- 회사 내부에서 실행됩니다. 패키지의 설치 스크립트가 개발자 머신이나 CI 러너에서 실행되며, 그곳에 있는 모든 것에 접근합니다.
이 공격에 이름을 붙인 연구
2021년 2월, 보안 연구원 Alex Birsan이 이 기법을 공개했습니다. 그는 버그 바운티 프로그램을 통해 또는 허가를 받아 Apple, Microsoft, PayPal을 포함한 35개가 넘는 조직 내부에서 이 기법으로 코드를 실행했습니다. 그가 테스트한 세 언어 모두에서 통했으며, 공격자들은 공개 후 며칠 만에 이를 따라 하기 시작했습니다.
왜 발생하는가
근본 원인은 여러 레지스트리를 동등하게 취급하는 패키지 관리자입니다. 대표적인 예가 Python입니다. pip의
--extra-index-url은 PyPI 옆에 두 번째 소스를 추가하며, 같은 이름의 공개 패키지를 찾으면
pip가 그것을 설치할 수 있습니다.
# Unsafe for private packages: both indexes are candidates
pip install --extra-index-url https://pypi.internal.example/simple my-internal-lib
# Safer: one index you control, which proxies public packages
pip install --index-url https://pypi.internal.example/simple my-internal-lib
npm도 스코프가 없는 내부 이름에서 같은 문제가 있으며, 레지스트리에 연결된 스코프를 쓰면 이를 피할 수 있습니다.
# .npmrc: everything under @acme comes only from your registry
@acme:registry=https://npm.internal.example/
종속성 혼동을 예방하는 법
- 단일 신뢰 소스를 사용하세요. 공개 패키지를 프록시하는 하나의 내부 레지스트리로 빌드를 연결하고, 내부 이름이 절대 공개 레지스트리에서 해석되지 않도록 구성하세요.
-
스코프나 네임스페이스를 사용하세요. 공개 레지스트리에서
@acme같은 스코프를 소유하고, 이를 비공개 레지스트리에 매핑하세요. - 이름을 선점하세요. 내부 패키지 이름을 공개 레지스트리에 등록해 다른 누구도 등록할 수 없게 하세요.
-
고정하고 검증하세요. 잠금 파일을 커밋하고 pip의
--require-hashes같은 해시 검사를 사용해, 다른 패키지가 들어오면 설치가 실패하게 하세요. 잠금 파일이 하는 일과 하지 않는 일을 참조하세요. - 빌드가 연결할 수 있는 곳을 제한하세요. 빌드가 여러분의 레지스트리에만 접근할 수 있다면 공격자의 패키지를 아예 가져올 수 없습니다. GitHub Actions 이그레스 정책을 참조하세요.
- 유출되는 것을 확인하세요. 공개 코드와 클라이언트 번들에서 내부 패키지 이름을 검색하세요.
종속성 혼동 vs 타이포스쿼팅
타이포스쿼팅은 사람이 공개 패키지 이름을 잘못 입력하는 데 기댑니다. 종속성 혼동은 비공개 패키지의 정확한 이름을 사용하며, 빌드가 구성된 방식에 기댑니다. 둘 다 도달하는 곳은 같습니다. 공격자의 코드가 여러분의 빌드 안에서 실행되는 것입니다. 다른 공격과의 관계는 공급망 공격이란 무엇인가를 참조하세요.
영향을 받았는지 확인하는 법
공개 레지스트리에서 해석된 내부 패키지 이름이나, 내부 레지스트리에 존재하지 않는 버전을 찾아보세요. 잠금 파일만으로는 이를 확인하기 어렵습니다. 문제는 빌드 중에 패키지가 실제로 어디에서 왔느냐이기 때문입니다.
CRACI가 돕는 방법
CRACI는 패키지를 가져오는 지점에서 작동합니다. GitHub Actions 러너로서 레지스트리별 유형이 지정된 소스로 이그레스 정책을 적용하므로, 빌드가 허용한 레지스트리에만 접근하도록 제한할 수 있습니다. 또한 각 작업이 가져온 모든 패키지를 SBOM에 기록하므로 빌드에 무엇이 들어왔는지 확인할 수 있습니다. CRACI는 패키지가 악성인지 탐지하지는 않으므로, 레지스트리 구성을 첫 번째 방어선으로 유지하세요. 소프트웨어 공급망 보안 모범 사례를 참조하세요.
종속성 혼동: 자주 묻는 질문
종속성 혼동 공격이란 무엇인가요?
패키지 관리자가 두 레지스트리를 모두 확인하고 더 높은 버전을 선택하기 때문에, 비공개 패키지와 이름이 같은 공개 패키지가 실수로 설치되는 공격입니다.
종속성 혼동은 타이포스쿼팅과 어떻게 다른가요?
타이포스쿼팅은 누군가 공개 패키지 이름을 잘못 입력하는 데 기댑니다. 종속성 혼동은 비공개 패키지의 정확한 이름을 사용하며, 공개 레지스트리를 확인하는 빌드 구성에 기댑니다.
종속성 혼동은 어떻게 예방하나요?
단일 내부 레지스트리, 스코프가 지정된 패키지 이름, 해시 검사를 갖춘 잠금 파일, 그리고 빌드가 접근할 수 있는 레지스트리를 제한하는 규칙을 사용하세요. 내부 이름을 공개적으로 선점하는 것도 도움이 됩니다.
--extra-index-url은 안전한가요?
비공개 패키지에는 안전하지 않습니다. 두 인덱스를 모두 후보로 만들기 때문에 같은 이름의 공개 패키지가 설치될 수 있습니다. 직접 관리하는 하나의 인덱스와 함께 --index-url을 사용하세요.
종속성 혼동은 누가 발견했나요?
보안 연구원 Alex Birsan이 버그 바운티 프로그램을 통해 또는 허가를 받아 35개가 넘는 조직을 대상으로 이 기법을 사용한 뒤, 2021년 2월에 공개했습니다.