개발새발 로그

Dependabot님을 통해서 의존성에 숨어있는 잠재적 위협을 해결해보자! 본문

React

Dependabot님을 통해서 의존성에 숨어있는 잠재적 위협을 해결해보자!

이즈흐 2024. 5. 1. 19:17

Dependabot을 알게 되어서 이것저것 공부하다가
내 프로젝트에는 어떤 취약점이 있을까 궁금해졌다.

실제로 내가 진행 중인 개인프로젝트에도 취약점이 존재했고,
이를 살펴보면서 해결해보려 한다!

 

🤔Dependabot?

Dependabot은 깃허브에서 제공하는 강력한 기능 중 하나로

의존성에 문제가 있다면 이에 대해 문제를 알려주고 가능하다면 해결할 수 있는 풀 리퀘스트까지 열어준다.

 

의존성에 대한 문제에 알기 위해서는  버전의존성에 대해서 알아야 한다.

📖버전

우리는 버전에 대해서 얘기할 때 개발자들 사이에서 암묵적으로 지켜지고 있는 유의적 버전(semantic versioning)에 대해서 알아야 한다.

유의적 버전 체계는 개발자 커뮤니티에서 널리 받아들여지고 있으며, 다양한 소프트웨어 프로젝트와 라이브러리에서 사용되고 있습니다.
이는 소프트웨어의 개발과 관리를 보다 체계적으로 하고, 의존성 관리에 있어서 많은 이점을 제공합니다.

 

버전은 주, 부, 수로 구성되어 있고, 아래와 같이 정의한다.

 

1. 기존 버전과 호환되지 않게 API가 바뀌면 "주 버전"을 올리고,

2. 기존 버전과 호환되면서 새로운 기능을 추가할 때는 "부 버전"을 올리고,

3. 기존 버전과 호환되면서 버그를 수정한 것이라면 "수 버전"을 올린다.

 

이 주, 부, 수로 알 수 있는 점은

주 버전이 바뀐다면 이전 버전과 호환이 되지 않는다는 것이고,

부 버전이 바뀐다면 기존 기능은 존재한 채 새로운 기능이 추가됐다는 것이고,

수 버전이 바뀐다면 기존 버전에서 존재하던 버그를 수정한 것이다.

 

그니까 버전의 업데이트가 있을 때 주, 부, 수를 확인하고, 무엇이 변화되었는지 예측할 수 있는 것이다.

 

그리고 npm은 이러한 버전에 대해 규칙을 정의했다.

1. react@16.0.0 : 버전 앞에 아무러 특수기호가 없다면 정확히 해당 버전에 대해서만 의존하고 있다는 뜻이다.

2. react@^16.0.0 : 16.0.0과 호환되는 버전을 의미한다. 이때 호환된다는 뜻은 0보다 높은 부 버전에 대해서는 호환된다는 가정하에 상위 버전을 설치할 수 있다는 것을 뜻한다. 즉, 여기서 가능한 버전은 16.0.0 부터 17.0.0 미만의 모든 버전이다. 단 주 버전이 0인경우에는 부 버전이 올라가도 API에 변경이 있을 수 있으므로 수 버전까지만 수용한다.

3. react@~16.0.0 : 패치 버전(수 버전)에 대해서만 호환되는 버전을 의미한다. 즉, 16.0.0부터 16.1.0미만의 모든 버전이다.

 

한 가지 염두에 둬야할 점은 유의적 버전은 어디까지나 개발자들간의 약속일 뿐, 정말로 해당 API의 버전이 이 유의적 버전에 맞춰 구현돼 있는지는 알 수 없다는 것이다.
npm은 이에 대해 보증해 주지 않으며, 어디까지나 개발자 간의 암묵적인 약속이다.

 

📖의존성

우리가 얘기할 의존성은 package.json에 있는 의존성이다.

package.json에는 dependenciesdevDependencies로 구성되어 있다.

peerDependencies도 있지만 이는 주로 라이브러리에서 사용된다.

 

dependencies

package.json에서 npm install을 실행하면 설치되는 의존성

해당 프로젝트를 실행하는 데 꼭 필요한 패키지가 여기에 선언된다.

 

devpendencies

package.json에서 npm install을 실행하면 설치되는 의존성

해당 프로젝트를 실행하는 데는 필요하지 않지만 개발 단계에서 필요한 패키지들을 여기에 선언한다.

 

peerDependencies

주로 서비스보다는 라이브러리와 패키지에서 자주 쓰이는 단위다.

직접적으로 해당 패키지를 require하거나 import 하지는 않지만 호환성으로 인해 필요한 경우를 의미한다.

 

예를 들어 만약 재사용 가능한 훅을 제공하는 패키지를 만든다고 가정해보면

이 경우 실제 React를 import하는 일은 경우에 따라 없을 수도 있지만

사용하려면 리액트 16.8.6 버전 이상이 필요하다. 

단순히 use커스텀 훅을 제공한다고 해서 쓸모있는 것이 아니라,

리액트 훅을 제공하는 버전을 설치한 서비스에서 사용해야만 올바르게 사용할 수 있을 것이다.

이 때 peerDependencies에 선언하면 된다.

{
	peerDependencies : {
    	"react" : ">=16.8",
        "react-dom" : ">=16.8"
    }
}

 


 

😮깃허브에서 내 프로젝트의 취약점 확인하기

그럼 이제 내 깃허브 저장소에서 Dependbot을 확인해보자.

취약점이 5개..

위처럼 5개의 취약점을 알려주고 있다.

태그로 보이는 ModerateLow가 눈에 띈다.

 

이는 깃허브의 Dependabot이 취약점을 4단계로 분류한 것이다.

4단계는 아래와 같다.

Critical -> High -> Moderate -> Low

 

그리고 이 취약점 들은 프로젝트를 npm install 했을 때도 볼 수 있다.

 

그럼 첫 번째 tough-cookie Prototype Pollution vulnerability를 들어가보자

위 내용을 요약하자면 

1. package-lock.json에서 취약점을 발견했다.

2. 보안 취약점의 심각도는 Moderate다.

3. 아래 자세한 정보를 확인할 수 있는데 내용은 아래와 같다.

4.1.3 이전 버전의 tough-cookie 패키지는 쿠키를 부적절하게 처리하여 프로토타입 오염에 취약하며,
이는 rejectPublicSuffixes=false모드에서 CookieJar를 사용할 때 쿠키를 잘못 처리하기 때문입니다.
이 문제는 객체가 초기화되는 방식에서 발생합니다.

 

🤔그럼 tough-cookie는 도대체 무엇일까?

나는 개인 프로젝트를 하면서 tough-cookie를 설치한 적이 없다.

근데 왜 tough-cookie에서 문제를 알려주는 것일까?

 

대부분의 의존성은 package-lock.json에 숨어 있는 경우가 많다. 
패키지가 어디 설치되어있는지 확인하려면 아래와 같은 명령어를 입력해서 확인하면 된다.

request에서 사용하고 있음을 알려주고 있다!

그럼 node_modules에 있는 request를 직접 확인해보자

이처럼 tough-cookie를 의존하고 있는 것을 확인할 수 있다.

 

🤔그럼 어떻게 취약점을 해결해야 할까?

아래 버튼을 클릭해서도 해결할 수 있다고 한다.

Dependabot이 제안하는 해결방법으로 해결하는 것이다.

하지만 이 방법은 무작정 시도하고 머지해서는 안된다.

보통 버전 업데이트의 문제인데 
유의적 버전에 따라 만약 주 버전을 올리게 된다면 실제 라이브러리를 사용하는데 많은 변경이 있을 수 있기 때문이다.

그러므로 무작정 제안하는 풀 리퀘스트를 머지해서는 안된다.

 

🤔그러면 어떻게 해야할까?

일단 어떤 문제인지 확인해야한다.

request@2.88.2에는 Tough-cookie@~2.5.0이 필요합니다. 터프 쿠키에 사용할 수 있는 패치 버전이 없습니다.

라고 하고 있다.

사실 나는 Dependabot이 제안해주는 해결 방법 버튼을 클릭해서 위와 같은 내용이 나왔다.

원래는 다른 내용이었지만 내용은 비슷하다.

 

어쨌거나 다시 돌아와서 나는 request의 버전을 업데이트하는 것인가? 추측했었다.

그래서 일단 npm에 request를 확인해봤다.

그런데 웬일인가 Deprecated 되어있었다.

나는 이런 것도 모르고 사용하고 있었다.

아마 과거에 proxy서버를 구성하던 도중에 아무 생각없이 했던 행동이 이렇게 된 것 같았다.

 

그래서 나는 위 패키지를 삭제하고 사용하던 부분은 axios로 교체해주었다.

npm uninstall request

그랬더니 아래와 같이 vulnerabilities가 줄어든 것을 볼 수 있었다.

그러면 이를 push하고 main에 머지를 해보겠다.

5개의 취약점에서 3개로 줄어든 모습

기존에 알려주고 있었던 취약점이 5개였는데 3개로 줄어들었다!
간단한 해결방법을 통해서 취약점을 없애버렸다..

 

 

🛠️나머지 오류들도 해결해보자 - package.json에 overrides 사용방법

위 3개의 오류는 똑같이 undici에 대해서 생겨나는 문제에 대해서 이야기 하고 있다.

 

자세히 보면 공통적으로 보여주는 부분이 있다.

버전을 업데이트하라고 명시하고 있다.

아래 자세한 정보에 해결방법이 나와있는데 이는 일단 무시하도록 하겠다.

 

그래서 명시한대로 버전이 문제인 것을 파악했고, 지금 현재 버전이 어떻게 되는지 확인해야 한다.

5.26.5 버전이다.

vercel 패키지에 의존되어 있던 패키지였다.현재 5.26.5버전에서 5.28.4버전으로 업데이트해야한다.주, 부, 수 중에 부 버전을 업데이트 해야한다.

 

현재 npm에서 undici는 6.15.0까지 업데이트 되어있다.

하지만 주 버전을 업데이트하기에는 기능상 문제가 생길 수 있다.

 

그래서 아래와 같이 패키지 내부의 버전을 강제로 올릴 것이다.

이 코드는 package.json에 작성하면된다.

  "overrides": {
    "undici": "^5.28.4"
  },

이것은 내부 의존성에서 사용하고 있는 undici의 버전을 강제로 ^5.28.4로 덮어 쓰라는 의미다.

이를 통해 undici의 버전이 5.28.4로 올라가게 되고, 의존성 문제도 해결될 것이다.

 

package.json에 위 코드를 추가하고 npm install을 수행하면 아래와 같은 변화를 볼 수 있다.

이렇게 해결하고 main에 머지를 해보았다.

알려지고 있는 의존성 취약점이 없는 모습

undici와 관련된 의존성 취약점이 모두 사라지게 되었다!

 

이렇게 의존성 취약점 문제를 해결하는 방법을 알아봤다.

 

📘마무리하며..

의존성에 대한 취약점을 알아보면서 중요한 점을 정리해보려고 한다.

 

💭먼저 의존성 관련 이슈를 방지하려면 어떻게 해야할까?

의존성 관련 이슈를 방지하는 가장 좋은 방법은 의존성을 최소한으로 유지하는 것이라고 한다.

dependencies와 node_modules의 크기가 커질수록 위협에 노출될 확률 또한 높아지는 것이다.

가능한 한 내재화할 수 있는 모듈은 내재화하고, 의존성을 최소한으로 유지하는 것이 좋다고 한다.

 

그리고 가능한 널리 알려져 있고 많은 사람들이 사용하는, 그리고 활발하게 유지보수되는 패키지를 사용해야 한다.

여기서 가장 중요한 것은 "활발하게 유지보수되느냐"다.

아무리 사용자가 많다 하더라도 유지보수하는 주체가 없다면 점차 의존성 문제에 당면할 가능성이 크다.

패키지를 선택할 때는 얼마나 많은 사용자가 존재하고, 얼마나 활발하게 유지보수되는지 살펴봐야 한다.

 

💭 그리고 Dependabot이 경고하는 문제는 계속 관심을 가져야한다.

그리고 Dependabot은 이슈를 찾는 용도로만 사용하고, Dependabot이 제안하는 해결 방법은 무작정 사용하지 말아야 한다.

 

728x90
반응형
LIST