본문으로 건너뛰기
>_ITDITD웹 보안 플랫폼

보안 가이드

의존성 CVE를 제대로 고치기: 스캔, 수정, 격리, 그리고 계속 감시

소규모 환경에서 의존성 취약점(CVE)을 실제로 고치기 위한 실전 플레이북. 완료의 4부 정의 — 스캔, 수정, 격리/인계, 모니터링 — 위에 세운다. 현장 교훈도 함께: 경보 피로를 이기는 변화 탐지, HTTP 200을 믿지 말 것, 수정이 사라지지 않게(로컬→push→배포).

게시 2026-06-11 업데이트 2026-06-11 6분 읽기

대상: 작은 팀과 함께 여러 웹 앱(여러 프레임워크 혼재)을 운영하며, 의존성 취약점(CVE)을 제대로, 한 번에 고치고 싶은 사람. 공격 절차는 없다 — 오직 고치고, 수정이 유지되게 하고, 계속 감시하는 실천이다. 이런 일을 자주 촉발하는 사고 유형은 방치된 공개 RCE가 부정 과금으로 청구된 사례를 보라.

본 사이트의 견해: 소규모 팀은 두 규율로 안전하다

소규모 팀에서 통하는 것은 화려한 도구가 아니라 — 두 규율이다: 1) 자동 변화 탐지(새 취약점에만 경보)와 2) 로컬→push→배포(프로덕션은 받기만, 절대 편집 안 함). 본 사이트도 같은 방식으로 돈다: 모든 배포 전 의존성 감사(pnpm audit) 더하기 매일 cron, 그리고 push-to-deploy로 프로덕션에 전달. 보안을 "한 번의 수정"이 아니라 "계속 발화하는 시스템"으로 다루는 것이 가장 값싸게 오래가는 길이다.

먼저 완료의 4부 "정의"를 고정하라

성급하게 "끝났다"고 부르는 것을 막는 가드레일. 미리 정하라: 넷이 모두 맞아떨어지기 전까지는 미완이다.

1. 스캔

상태를 숫자로 만든다

2. 수정

dev 아닌 크리티컬/높음을 근본 수정

3. 격리/인계

고칠 수 없는 것을 명시화

4. 모니터링

매일 변화 탐지를 넣는다

취약점 작업 완료 = 네 가지의 한 세트. 하나라도 빠지면 '고쳤다' 아래에 구멍이 남는다.

1. 스캔: 상태를 숫자로 만들어라

먼저, 무엇이 어디 있는지를 기계로 보라. 수동 임의 점검은 늘 빠진다.

1

락파일을 자동 발견하고 스캔

composer.lock / package-lock.json / pnpm-lock.yaml 을 찾아, 매일 오픈소스 스캐너(osv-scanner 또는 pnpm audit)로 돌린다. 락파일만 읽으므로 부하는 미미하다.
2

심각도가 하나의 숫자가 아님을 알라

같은 결함이 CVSS에서는 높음(예: 7.5)이지만 벤더의 검증된 등급에서는 보통일 수 있다. CVSS는 기계가 산정하므로 과대 계상 경향이 있다. 어느 기준이 임계값을 정하는지 결정하고, 팀 전체가 어느 것을 쓰는지 알게 하라(→ CVSS란 무엇인가). 실제 악용 여부(KEV)와 교차해 우선순위를 매겨라.
3

무시가 아니라 기록된 이유와 함께 노이즈 제외

테스트/빌드 전용 dev 의존성은 프로덕션 번들에 없는 경우가 많아 실제 위험이 없을 수 있다. 스캐너의 그룹화로 dev를 알림에서 빼되, 왜 제외했는지 기록하라. 나중에 "이거 아직 안 고친 거 아냐?"에 답할 수 있게 하라.

2. 수정: 증상 가리기가 아니라 근본 수정

응급 처치(증상 가리기)와 근본 수정은 다른 작업이다. 둘 다 해야 끝난다.

증상 가리기만(봉쇄)

  • 리버스 프록시에서 "수상해 보이는" 요청만 차단
  • 증상이 멈췄으니 "처리됨"으로 친다
  • 취약한 의존성은 그대로RCE는 살아 있다

근본 수정(올바름)

  • 취약한 의존성을 패치 버전으로 업그레이드
  • 업그레이드 후 로그에서 시그니처가 사라졌는지 확인
  • 응급 처치와 근본 수정을 별개 작업으로 보고, 둘 다 한다

메이저 업그레이드는 프로덕션 전에 빌드 검증

패치와 달리, 메이저 버전 점프(프레임워크 14→15, UI 키트 4→6 등)는 호환성 깨짐을 동반한다. 무턱대고 올려 프로덕션 빌드를 깨는 것이 최악이다. 수정한 소스를 프로덕션과 동일한 런타임(같은 Node/PHP 버전 컨테이너)에 넣고, push 전에 빌드/타입 체크를 완전히 통과시켜라. 오류를 하나씩 해소하라(sync→async 코드모드, 여러 줄 CSS 클래스 문자열, 폐기된 전역 타입 네임스페이스 등). 기존 컨테이너가 계속 돌고 있다면, 실패는 무중단으로 롤백된다.

완료에는 수정의 지속성이 포함된다

완벽한 수정도 다음 배포가 지우면 가치가 0이다. 가장 흔한 함정이다.

1

프로덕션 작업 트리에서 커밋하지 마라

프로덕션에서의 직접 커밋은 중앙 저장소와 히스토리가 갈라지고, 다음 배포(pull / checkout -f)가 그것을 덮어써 수정이 사라진다. 프로덕션은 "배포 결과가 도착하는 곳"이지 편집하는 곳이 아니다.
2

한 방향으로 표준화: 로컬→push→배포

로컬에서 편집 → 커밋 → push → (프로덕션) pull / 자동 배포. 프로덕션에서 검증해야 했던 변경조차 로컬로 되감아 다시 push해야 한다(→ 프로덕션을 받기 전용으로).
3

HTTP 200을 '정상'으로 믿지 마라

공유 호스팅에서는 치명적 오류도 200을 반환할 수 있다. 실제 콘텐츠로 검증하라 — 기대한 제목/텍스트가 렌더링되는가(curl | grep), 로그에 새 오류가 있는가, 그리고 홈페이지뿐 아니라 동적 경로(DB 기반, 매개변수화)를 걸어보라. 캐시가 변경을 지연시킬 수 있으니 기다리거나 먼저 비워라.

3. 격리/인계: 고칠 수 없는 것을 명시화하라

지금 당장 모든 것을 고칠 수는 없다. 요령은 "고칠 수 없음 / 내 영역 아님 / 미사용"을 결코 모호하게 두지 않는 것이다.

1

방치/EOL 코드: 삭제 전에 격리

더는 서비스하지 않는 옛 소스(EOL 프레임워크 등) — 곧장 삭제하지 말고 이름을 바꿔 격리하고, 언제/왜/어디서 서비스됐는지 표시를 남겨라. 스캔에서도 빼라. "삭제"는 되돌릴 수 없지만 "격리"는 되돌릴 수 있고 흔적을 남긴다.
2

먼저 정말 참조되지 않는지 확인

리버스 프록시 설정, 빌드 구성, 마운트를 추적해 아무것도 참조하지 않음을 확인한 뒤 격리하라 — 실수로 다시 서비스될 위험을 끊어라.
3

고칠 수 없는 것은 명시적으로 인계

손이 닿지 않는 것은 방치하지 말고 "누가 / 무엇을"을 명시해 인계하라. 인벤토리의 가치는 미수정이 보이지 않는 방치로 변하지 않게 하는 데 있다.

4. 모니터링: 매일 변화 탐지가 있어야 비로소 완료

이제 마지막으로 발화 장치를 넣는다. 그것이 없으면, 한 수정이 재발할 때 알려주지 않는다.

매일 모든 것이 아니라 새로운 것에만 경보

스캔 결과를 이전 실행(상태 파일)과 비교(diff)해 새 크리티컬/높음이 나타날 때만 알려라. 매일 같은 내용은 무시된다(경보 피로). 매일 cron으로 하나의 이메일(신규 / 해소 / 현재)로 요약하라. 공유 호스팅도 스캐너 바이너리 + cron 하나는 문제없이 처리한다(부하는 밀리초 단위; 안심을 위해 nice를 더하라).

diff
새 것에만 알림 = 피로 없음
매일
cron으로 계속 돌린다
이메일 1통
신규 / 해소 / 현재 요약
넷 모두
모니터링이 켜져야 비로소 완료

인벤토리가 함께 드러내는 "시크릿"과 "키"

CVE를 제대로 고치다 보면 의존성 외의 구멍도 함께 드러나는 경향이 있다. 두 고전 — 공개 디렉터리에 남은 시크릿 파일(웹루트에 놓인 옛 토큰; 공유 템플릿에서 왔다면 모든 사이트가 같은 구멍을 가짐)과 침해될 수 있는 환경에 넘겨진 루트 키. 둘 다 "한 번 유출되면 전부"라는 패턴을 만드니, 같은 점검에서 함께 확인하라(각각 별도의 깊은 글이 마땅하다). 시크릿 기본은 시크릿을 안전하게 저장하기기준선 체크리스트를 보라.

다음으로 읽기

FAQ

Q취약점 작업은 언제 실제로 '완료'되나요?
A

네 가지가 맞아떨어질 때뿐입니다: 1) 스캔해 상태를 숫자로 만들고, 2) dev가 아닌 크리티컬/높음을 수정하고, 3) 고칠 수 없는 것/방치 코드를 명시적으로 격리하거나 인계하고, 4) 매일 변화 탐지(신규·재발을 잡는 모니터링)를 갖춥니다. 4가 갖춰지기 전까지는 완료가 아닙니다 — 의존성은 내일 다시 취약해집니다.

Q매일 스캔하면 경보 피로가 생기지 않나요?
A

매일 모든 것을 경보하면 금세 무시됩니다. 이전 결과(상태 파일)와 비교(diff)해 새로운 크리티컬/높음이 나타날 때만 알리세요 — 변화 탐지입니다. 매일 같은 것을 보내지 않는 것이 모니터링을 실제로 오래가게 합니다.

Q수정이 왜 가끔 다시 취약 상태로 되돌아가나요?
A

프로덕션 작업 트리에서 직접 커밋하면, 다음 배포(pull이나 checkout -f)가 덮어써 수정을 지웁니다. 한 방향 — 로컬→push→배포 — 으로 표준화하고 프로덕션을 '받기만' 하게 하세요. 수정을 지우는 작업 흐름 위의 완벽한 수정은 가치가 0입니다.