의존성 혼동 공격 – 작동 원리 및 예방 방법

지원 종료(EOL)된 PHP용 php2shell 패치(CVE-2026-109618 및 CVE-2026-109619)가 출시되었습니다. 수요가 많아 평소보다 영업팀의 답변이 늦어지고 있으므로, 패치된 패키지를 가장 빠르게 확보하려면 온라인에서 Runtimes용 ELS를 구매하시기 바랍니다 (새 탭에서 열림). 패치 FAQ 읽어보기

이 블로그 글의 AI 요약으로 바로 가기

  • Perplexity를 사용하여 이 게시물을 요약해 주세요
  • Gemini로 이 게시물을 요약해 주세요

현대 소프트웨어 개발에서는 엄격한 공급망 설치 모델에서 하이브리드 모델로 전환되었습니다. 이제 종속성은 비공개 패키지와 공개 패키지라는 두 가지 경로를 통해 확보할 수 있습니다. 이 글에서는 종속성 혼동 현상과 이것이 심층 전이적 종속성 공격과 어떤 관련이 있는지에 대해 중점적으로 다룰 것입니다.

하이브리드 공급망 모델

 

Today, we have two different ways of installing packages via various tools like npm, pnpm, etc. The concept of a single way to install packages is a thing of the past. Years ago, we would just open our terminal up and type npm install <package>. It would get pulled from the npm registry, and that would be that. Private packages, or scoped packages, can be delivered from the npm registry and are secure by default. You can also host scoped packages in internal registries like Nexus, Artifactory, and Verdaccio.

범위가 지정된 패키지는 다음과 같습니다. @myorg/mypackage. “@” 기호로 시작하여 조직 이름을 따르고, “/”와 패키지 이름으로 끝납니다. 이는 npm 레지스트리 내에 호스팅되는 보안이 적용된 비공개 패키지입니다. 또한 이를 통해 조직 내에서 패키지를 그룹화할 수 있습니다. 오픈 소스인 공개 패키지도 여전히 설치할 수 있습니다. 기능을 구축하기 위해 공개 패키지를 설치하게 될 가능성이 매우 높습니다. 따라서 공개 패키지와 비공개 패키지가 혼합되어 사용될 것입니다.

공개된 오픈소스 패키지를 사용하거나 CI/CD 구성이 부적절할 때 취약점이 서서히 침투해 들어옵니다. 패키지 관리 도구는 편의성을 위해 설계되었습니다. 하지만 그 편의성에는 대가가 따릅니다. 이러한 패키지 관리 도구는 소스의 출처보다 더 높은 버전 번호를 우선시합니다. 그러면 문제는 눈덩이처럼 불어나기 시작합니다.

 

의존성 혼동 공격이란 무엇인가?

 

의존성 혼동 공격은 현대적인 패키지 관리자가 여러 소스 간에 의존성을 해결하는 방식을 노립니다. 비공개 및 공개 레지스트리가 모두 사용되는 환경에서는 이러한 해결 과정에서 예상치 못한 위험이 발생할 수 있습니다.

겉보기에는 평범한 종속성 설치처럼 보이지만, 특히 내부 패키지가 제대로 격리되거나 범위가 제한되지 않은 경우 악성 코드의 침투 경로가 될 수 있습니다.

의존성 혼동은 어떻게 작용하는가

 

의존성 혼동 공격(또는 네임스페이스 혼동)은 패키지 관리자가 여러 레지스트리 간에 의존성을 해결하는 방식의 결함을 악용합니다. 이 공격은 빌드 시스템이 비공개(내부) 레지스트리와 공개 레지스트리(예: npmjs.com)에서 모두 가져오도록 설정되었을 때 발생합니다.

공격자가 비공개이고 범위 지정되지 않은 패키지의 이름을 파악한 뒤, 정확히 동일한 이름을 가진 악성 패키지를 더 높은 버전 번호로 공개 레지스트리에 등록하면, 패키지 관리자는 이 공개된 악성 버전을 “가장 적합한 항목”으로 판단하여 아무런 경고 없이 공급망에 주입하게 됩니다. 이는 패키지 관리자가 이름이 일치하는 경우 더 높은 버전을 우선적으로 처리하기 때문입니다.

실제로 패키지 관리자는 semver 범위를 충족하는 가장 높은 버전을 우선순위로 삼습니다. 만약 여러분의 package.json 에서 ^1.2.0 버전을 internal-tool버전을 요구하며, 공격자는 v99.0.0을 npmjs.com에 게시하면, 관리자는 공개 버전이 정당한 업데이트라고 판단하여 이를 가져옵니다.

또 다른 일반적인 경우는 조직에서 범위 지정 패키지를 사용하지 않을 때 발생합니다. 해당 패키지는 auth-package로 로 공개 레지스트리에 게시되었지만, 퍼블릭 레지스트리에도 동일한 이름의 패키지가 존재하는 경우입니다. 레지스트리는 .npmrc 파일에 명시된 경우를 제외하고는 레지스트리는 버전 번호가 가장 높은 패키지를 가져옵니다.

전치 의존성 분석

 

전달 의존성은 개발자인 당신이 의도적으로 설치한 것은 아니지만, 프로젝트의 직접 의존성을 설치할 때 함께 따라 들어오는 패키지를 말합니다. 전달 의존성은 의존성 트리 내 여러 계층 깊숙이 숨어 있을 수 있어, 보안 결함의 원인을 정확히 파악하기 어렵게 만듭니다.

“대부분의 사이버 보안 사고는 패치를 적용하지 않은 것과 관련이 있었습니다. 사고의 60.4%는 패치가 제공되었음에도 불구하고 적용되지 않은 상태에서 발생했습니다.”

 

소프트웨어 공급망에서 발생하는 전이적 의존성 보안 결함을 방지하려면, 항상 최신 동향을 파악하고 패치가 출시되는 대로 적용하는 것이 매우 중요합니다. 전이적 의존성이 어떤 형태를 띠는지 보여주는 예시는 다음과 같습니다.

rxjs 는 Angular v20의 직접적인 종속성이지만, 만약 node_modules 폴더를 자세히 살펴보면 rxjs 폴더를 찾아보면, 그 안의 package.json 파일에서 tslib 패키지가 있습니다. 이 패키지는 사용자가 직접 설치한 것이 아니라 rxjs 패키지를 통해 Angular v20 프로젝트 내에 함께 포함된 것입니다.

의존성 혼동 공격의 구조

 

프로젝트에 추가하려는 기본적인 사용자 인터페이스 구성 요소, 즉 간단한 버튼을 예로 들어보겠습니다. 이 버튼은 조직의 디자인 시스템에 포함된 컴포넌트 라이브러리에서 직접 가져온 것입니다.

별다른 건 없습니다. 배경, 텍스트가 들어간 폰트, 그리고 둥근 모서리. 디자인 시스템에 포함된 요소 그대로가 바로 이 버튼입니다. 코드는 React로 작성되었으며 다음과 같습니다:

 

export const DemoButton = () => {

  return (

    <button style={{ backgroundColor: 'blue', color: 'white' }}>

      Secure Scoped Action

    </button>

  );

};

 

npm을 통해 설치하고, 프로젝트에 가져오기만 하면 끝입니다. 이제 UI에 작동하는 버튼이 생겼습니다. 하지만 한편으로는 또 다른 멋진 버튼 이 존재합니다. 이 버튼은 비공개 레지스트리에 범위가 지정되지 않았거나, 범위가 지정되었더라도 개발자가 공개용 버전을 설치하는 경우가 있습니다. 공개용 버전은 악성 버튼으로, 홈페이지에 경고 메시지를 표시하거나, 백그라운드에서 인증 정보를 가로채거나, 즉시 또는 일정 시간이 지난 후 사이트의 색상을 반전시키는 등의 행동을 유발합니다.

이 버튼의 코드는 다음과 같습니다. 혹시 아직 확인하지 못하셨을 수도 있습니다:



import React, { useEffect } from 'react';



export const DemoButton = () => {

  useEffect(() => {

    document.body.style.filter = 'invert(100%)';

    alert("Hacked via Unscoped Dependency!");

  }, []);




  return <button>Click for Update</button>;

};

 

대부분의 경우, 이 버튼은 직접적인 종속성으로 포함되지 않을 것입니다. 예제 코드에서는 여러 구성 요소로 이루어진 완전한 디자인 시스템이 구축되어 있지 않기 때문에 이를 직접적인 종속성으로 표시하고 있습니다. 검증되지 않은 UI 라이브러리나 디자인 시스템의 종속성으로 작용하는 패키지를 설치할 경우, 악성 버튼이 간접적으로 유입될 수 있습니다.

이러한 종속성이 직접적으로 도입되든 간접적으로 도입되든, 이들은 애플리케이션의 신뢰 경계의 일부가 되며, 방치할 경우 잠재적인 공격 표면이 될 수 있습니다.

설치 전후 공격

 

공격자들은 사전 및 postinstall 스크립트를 사용하여 개발자가 코드를 실행할 시간조차 갖기 전에 악성 페이로드를 실행합니다. preinstall 스크립트는 패키지 파일이 폴더에 배치되기 전에 환경 검사를 수행하여 악성 행위를 실행할지 여부를 결정합니다. 만약 스크립트가 보안이 강화되었거나 모니터링되는 환경을 감지하면, 탐지를 피하기 위해 은밀하게 본부에 신호를 보내고 잠복 상태를 유지합니다.

설치 후 postinstall 스크립트는 문이 잠금 해제될 때까지 대기한 후, 그 후 다양한 작업이 수행될 수 있습니다. postinstall 스크립트는 비밀 정보를 훔치거나, E.T.의 표현을 빌리자면 '집에 전화'하여 서버에 문이 활짝 열려 있어 누구든 들어올 수 있음을 알릴 수 있습니다. 이를 뱀파이어 전설에 비유해 볼 수 있습니다. 일단 뱀파이어를 집 안으로 들여보내면, 그 순간부터 뱀파이어의 활개 치는 시간이 시작되는 셈입니다.

스크립트가 node_modules에 들어가면 이미 늦은 것입니다. 스크립트는 즉시 실행됩니다. 스크립트는 다음과 같은 명령을 실행합니다. curl 나 wget 와 같은 명령어를 실행하여 .env 파일, SSH 키 또는 AWS 토큰을 공격자의 사이트로 전송할 수 있습니다. 공격자가 이러한 인증 정보를 확보한 후에는 키나 토큰을 삭제하고 새로 생성하는 것 외에는 별다른 방법이 없습니다.

의존성 혼동 해결

 

이러한 문제를 미리 예방하고, 보안 패치가 적용된 안전한 프로젝트로 전환하기 위한 몇 가지 방법이 있습니다. 

범위가 지정된 패키지

의존성 혼동 공격을 피하는 가장 확실한 방법은 패키지 관리자에 조직을 등록하고 범위 지정 패키지를 사용하는 것입니다. @myorg/mypackage. 이를 통해 조직 내의 자체 프로젝트에서 사용할 수 있는 안전하고 비공개인 패키지를 제공할 수 있습니다.

Proper .npmrc 올바른.npmrc 구성

npmrc 파일을 생성할 때 .npmrc 파일을 생성할 때는 특정 범위를 비공개 레지스트리에 엄격하게 매핑해야 합니다. 이를 엄격하게 매핑함으로써 해당 항목이 공개 레지스트리로 '유출'되는 것을 방지할 수 있습니다. .npmrc 파일을 설정하는 예시는 다음과 같으며, 이는 프로젝트 내 문제를 해결하는 가장 효과적인 방법입니다:

 

# 공개 패키지의 기본 레지스트리 설정

registry=https://registry.npmjs.org/




# 모든 @org 범위 패키지가 비공개 레지스트리를 통해서만 해결되도록 강제 설정

@org:registry=https://npm.pkg.github.com/

always-auth=true

사용 –ignore-scripts

Another clever way to get away from the preinstall and postinstall scripts is to use –ignore-scripts when installing packages. Using that flag looks like this: npm install <new-package> –ignore-scripts. This isn’t just something that is used once; you want to bake this into your automation and workflows as well.

락파일 무결성

귀하의 package-lock.json 또는 yarn.lock 파일은 단순히 버전을 고정하는 것 이상의 역할을 합니다. 이 파일에는 해시, 즉 암호학적 해시가 저장되어 있습니다. 해시가 변경되어 빌드가 실패한다면, 이는 공격자가 레지스트리에서 해당 패키지를 다른 것으로 교체했음을 의미합니다. npm ci 를 사용하면 락 파일을 강제 적용합니다. npm install을 사용하면 이 보호 기능을 우회할 수 있습니다.

스택 고정하기

이러한 모든 사항을 고려하여, 엄격한 보안 점검 목록을 철저히 준수하시기 바랍니다. 이 내용들은 그 중 일부에 불과합니다.

 

  1. 범위 지정 패키지를 사용하고, 레지스트리에 조직을 등록했는지 확인하세요
  2. 다음 파일을 설정하세요 .npmrc 파일을 구성하여 패키지를 비공개 레지스트리에 엄격하게 매핑하십시오
  3. 사용 –ignore-scripts 를 비활성화하려면 CI/CD 파이프라인에서 preinstall 및 postinstall 스크립트를 비활성화하려면

 

공급망 보안은 론코(Ronco) 로티세리 오븐처럼 ‘한 번 설정해 두고 잊어버릴’ 수 있는 일이 아닙니다. 프로젝트가 진행됨에 따라 지속적인 주의가 필요합니다. 이는 프로젝트가 진행되는 동안 초기 단계부터 장래에 이르기까지 꾸준히 관리해야 할 사항입니다.

레지스트리를 철저히 관리하고, 적용 범위를 확인하며, 종속성 트리의 깊은 곳에서 일어나는 일들을 면밀히 모니터링하세요. 가장 큰 위험 요소가 항상 눈에 띄는 것은 아니지만, 대개는 이미 여러분의 스택 안에 숨어 있습니다. ‘초대받지 않은 뱀파이어’라는 비유와 마찬가지로, 영화 《로스트 보이즈》의 다음 대사를 여러분께 전하며 글을 마칩니다. “산타 칼라에서 살면서 도저히 참을 수 없었던 게 하나 있다면, 바로 그 빌어먹을 뱀파이어들이었어.” 여기서 산타 칼라는 소프트웨어를, 뱀파이어는 보안 결함을 의미합니다. 한발 앞서 대응하여 그들을 들여보내지 마십시오.

 

요약
의존성 혼동 공격 – 작동 원리 및 예방 방법
기사 이름
의존성 혼동 공격 – 작동 원리 및 예방 방법
설명
의존성 혼동 공격이 어떻게 작동하는지, 이 공격이 npm과 전이적 의존성을 어떻게 악용하는지, 그리고 소프트웨어 공급망을 어떻게 보호할 수 있는지 알아보세요.
작성자
게시자 이름
TuxCare
게시자 로고

4,500명 이상의 Linux 및 오픈 소스 전문가와 함께하세요!

한 달에 두 번. 스팸이 없습니다.

목차
필요한 오픈 소스 보안에 대한 답변 얻기

4,500명 이상의 Linux 및 오픈 소스 전문가와 함께하세요!

한 달에 두 번. 스팸이 없습니다.