주요 내용
- Lodash는 활동이 중단된 상태였을 뿐, 지원이 종료된 것은 아니었으며, 이 라이브러리의 부활로 인해 오랫동안 고정된 버전에 대한 새로운 CVE가 발생했습니다.
- 하나의 에스테이트에는 여러 버전의 Lodash가 포함될 수 있으며, 그중 대부분은 전이적으로 불러옵니다.
- 취약점이 있는 코드를 찾는 것은 쉽지만, 어떤 코드 경로를 통해 그 지점에 도달하는지 증명하는 것이야말로 진짜 어려운 일입니다.
누구도 기획 회의에 앉아서 Lodash를 도입하기로 결정하지는 않습니다. 그냥 자연스럽게 들어오게 됩니다. 선임 개발자가 헬퍼 함수 하나를 가져오고, 빌드 도구가 이를 의존성으로 지정하고, 세 단계 아래에 있는 차트 라이브러리가 이를 필요로 하게 되면 _.merge, 그러다 보니 어느새 여러분이 출시하는 모든 앱 패키지에 포함되어 함께 배포되곤 합니다. 눈에 띄지 않는 인프라이며, 누구도 의도적으로 선택한 프레임워크는 아닙니다.
자바스크립트 파일이 실행되는 곳이라면 어디든 퍼져 있습니다. 프론트엔드 코드, 백엔드 서비스, 빌드 도구, CLI, 자동화 스크립트는 물론, 2019년에 한 팀이 작성한 뒤 그 누구도 열어보지 않은 채 방치된 내부 패키지까지 말이죠. Sonatype의 2024년 보고서에 따르면, npm만 해도 그 해에 약 4.5조 건의 패키지 요청이 발생한 것으로 추산되며, Lodash는 그 엄청난 양의 요청 속에서 가장 널리 사용되는 패키지 중 하나입니다.
그러다 릴리스 노트나 대부분의 회고 회의에서도 다루지 않았던 전환점이 찾아왔습니다. 2021년 2월에 버전 4.17.21이 출시된 이후, 수년 동안 후속 버전이 나오지 않았습니다. 팀들은 이러한 침묵을 안정성으로 해석하고, 해당 버전을 고정시킨 뒤 다음 단계로 넘어갔습니다.
그 해석은 틀렸다. 주의보가 없었다는 사실이 결코 안전의 증거가 될 수는 없었다. 그것은 실제 노출 상황에 대해서는 거의 아무것도 말해주지 않았으며, 재개된 프로젝트가 누적된 보안 문제를 해결하기 시작하자 그 사실이 분명해졌다.
Lodash는 여전히 유지보수되고 있나요?
네, 오랜 공백 끝에 다시 돌아왔습니다. Lodash는 수년 동안 사실상 활동이 중단된 상태였으나, 2025년에 다시 활발한 관리 체제로 돌아왔습니다. 2026년에 이어진 보안 권고 사항들 때문에 오래된 핀들이 다시 활성화되기 시작한 것입니다.
Lodash는 2012년 Underscore의 포크로 시작되었으며, 개발자는 Dojo와 jQuery 재단을 거쳐 이 프로젝트가 OpenJS 재단에 안착하게 되었고, 현재 이곳에서 ‘기능 완성(Feature-Complete)’ 성숙 단계로 전환되고 있습니다. 최근의 역사는 타임라인으로 보면 더 이해하기 쉽습니다.
| 날짜 | 무슨 일이 있었나요? |
|---|---|
| 2021년 2월 | 2021년 4월 17일, 다음의 명령어 삽입 취약점에 대한 수정 사항이 배포되었습니다. _.template 그리고 ReDoS 문제가 발생했고, 그 후 몇 년 동안은 별다른 소식이 없었습니다 |
| 2025 | OpenJS, Sovereign Tech Agency의 자금 지원, 거버넌스 개편 및 새로운 기술 운영 위원회 출범을 발표했습니다. |
| 2026년 1월 | 4.17.23 버전에서 프로토타입 오염 문제를 수정했습니다. _.unset 그리고 _.omit (CVE-2025-13465), 4.0.0부터 4.17.22까지의 버전에 영향을 미침 |
| 2026년 3월 | 4.18.0 패치는 a를 수정합니다. _.template imports 키를 통한 코드 삽입(CVE-2026-4800) 및 1월의 _.unset 그리고 _.omit 수정 (CVE-2026-2950) |
| 2026년 4월 | 4.18.1이 최신 릴리스로 출시되었으며, 4.18.0에서 발생했던 모듈식 빌드 관련 회귀 현상이 수정되었습니다. |
이번 재구축에는 실질적인 운영 인프라가 마련되었습니다. 여기에는 사고 대응 계획, 공개된 위협 모델, GitHub 보안 권고, CodeQL 스캔, 종속성 업데이트를 위한 Renovate, OpenSSF 스코어카드, 그리고 재구축된 CI가 포함됩니다.
알고 계셨나요? 2023년 4월 17일자 보안 권고문은 OpenJS CVE 번호 부여 기관을 통해 제출되었습니다. 이는 수년 만에 나온 첫 번째 Lodash 보안 릴리스였으며, 이러한 조율된 공개 방식은 보안 프로세스가 제대로 작동하고 있음을 보여주는 특징이지, 방치되어 있는 상태가 아님을 의미합니다.
구버전 코드를 실행하는 모든 사용자에게 중요한 부분은 바로 이 부분입니다. 이러한 지속적인 관리 덕분에 현재 릴리스 라인은 유지보수되고 보안이 보장됩니다. 그렇다고 해서 누군가가 새로운 수정 사항을 3.10.1 버전으로 역포트하고 있다는 뜻은 아닙니다.
재개된 프로젝트가 고정된 버전에 미치는 영향
이 메커니즘은 간단하면서도 용서할 여지가 없습니다. 새로운 권고 사항이 발표되면, 누구도 여러분의 코드를 수정하지 않았음에도 새로 확인된 취약 범위에 변경되지 않은 핀이 포함될 수 있습니다. 버전 번호는 변하지 않았지만, 알려진 위험 수준은 달라졌습니다.
다음 세 가지 공개 내용을 통해 이 점이 명확히 드러납니다.
[ ]의 시제품으로 인한 오염 _.unset 그리고 _.omit 4.17.23 버전에서 패치되었습니다. CVE-2025-13465는 4.0.0부터 4.17.22까지를 대상으로 하므로, 5년 전 버전인 4.17.21도 이 범위에 정확히 포함됩니다.
해당 수정 사항을 우회하는 취약점이 CVE-2026-2950으로 등록되었습니다. 4.17.23 패치는 문자열 경로 세그먼트는 보호했으나 배열로 감싸인 세그먼트는 보호하지 않았기 때문에, 동일한 삭제 공격이 여전히 가능했습니다. Lodash 4.17.23 및 그 이전 버전이 이 취약점의 영향을 받았으며, 4.18.0 버전에서 패치되었습니다.
코드 삽입 _.template 수입된 키 이름을 통해, CVE-2026-4800은 이전의 CVE-2021-23337에 대한 불완전한 수정에서 비롯되었습니다. 원래 패치는 변수 option에 대한 유효성 검사는 수행했으나 imports 키 이름에 대한 유효성 검사는 수행하지 않았으며, 두 경로 모두 동일한 Function() sink. 이 문제는 4.0.0부터 4.17.23까지의 버전에 영향을 미치며, 4.18 계열에서는 패치되었습니다.
여기서 버전 선택은 자동으로 이루어지지 않습니다. 4.18.0 릴리스에는 모듈식 빌드 관련 회귀 문제가 포함되어 있었으며, 현재 npm은 이 버전을 “잘못된 릴리스입니다. 대신 [email protected]를 사용해 주세요.”라는 알림과 함께 사용 중단 예정으로 표시하고 있습니다. 이로 인해 사용자들은 위의 세 가지 CVE 모두의 영향을 받는 버전으로 바로 되돌아가게 됩니다. 실제로 사용해야 할 버전은 4.18.1입니다. 일부 단일 함수 lodash.* 패키지들도 새 줄에서 다시 생성되었습니다. lodash.unset 현재는 4.18.0 버전을 추적하고 있는 반면, 다음과 같은 다른 것들은 lodash.merge 2019년 버전인 4.6.2로 고정되어 있으므로, 해당 빌드를 직접 묶어 사용하는 구버전 프로젝트는 각 빌드를 개별적으로 확인해야 합니다.
주의: 2024년에 “lodash 4.17.21”에 대해 스캐너 결과가 녹색으로 표시되었다고 해도, 이 결과가 계속 녹색으로 유지되는 것은 아닙니다. 2026년에 발표된 보안 권고 사항은 해당 빌드에 소급 적용되며, 재스캔을 수행하기 전까지는 lockfile에서 이를 확인할 수 있는 방법이 없습니다.
2018년 당시의 Angular 관리자 콘솔을 상상해 보세요. 번들에 lodash 3.10.1이 포함되어 있고, lodash.merge 두 개의 방치된 차트 라이브러리에 이끌려 들어왔습니다. 스캐너 보고서에는 잘못된 내용이 전혀 없으며, 조치를 취해야 할 사항도 없습니다. 정확함과 유용함 사이의 그 간극에서야말로 진정한 작업이 시작됩니다.
프로토타입 오염은 팀들이 유틸리티 라이브러리를 바라보는 방식을 바꿔놓았다
텍스트를 대문자로 변환하는 순수한 문자열 헬퍼 함수는 동일한 보안 경계를 넘지 않습니다. 반면, 객체를 병합하거나 경로에 값을 할당하는 함수는 경계를 넘으며, 바로 그 차이가 모든 것을 설명합니다. 객체 병합과 경로 할당은 나중에 코드가 신뢰하게 될 구조에 데이터를 기록하게 되는데, 이것이 바로 경계선입니다. _.capitalize 절대 가까이 가지 않는다.
이 오류 유형은 그리 특이한 것이 아닙니다. 다음과 같이 조작된 키를 입력하면 __proto__ 또는 constructor.prototype 취약한 병합 또는 경로 설정자로 변환하면, 일반 객체가 상속받는 공유 프로토타입에 접근할 수 있으며, 이는 다음부터 시작됩니다. Object.prototype. 가장 경미한 경우에는 오염된 객체로 인해 하류 코드가 멈춰버리면서 서비스 거부 현상이 발생합니다. 가장 심각한 경우에는 템플릿 엔진이나 Function() 호출 시, 프로토타입 오염이 코드 실행의 발판이 됩니다. 이러한 특정 Lodash 보안 권고 사항에 대한 한 가지 주의 사항은 다음과 같습니다: _.unset 그리고 _.omit 이 결함으로 인해 공유 프로토타입의 속성을 덮어쓰는 대신 삭제할 수 있게 되므로, 실제적인 영향은 전형적인 ‘오염 → RCE’ 연쇄 공격보다는 시스템 오작동 및 서비스 거부로 나타납니다.
하지만 정확성이 중요합니다. 영향을 받는 함수에 대한 모든 호출이 악용될 수 있는 것은 아닙니다. 결정적인 요소는 공격자가 병합된 객체나 키 경로에 영향을 미칠 수 있는지 여부입니다. 두 가지 모두 개발자가 완전히 제어하고 있으며 신뢰할 수 없는 입력으로는 접근할 수 없다면, 노출 위험은 낮습니다. 요청 본문, 쿼리 문자열 또는 파싱된 파일을 통해 둘 중 하나라도 영향을 받을 수 있다면, 이를 배제할 때까지는 해당 경로를 잠재적으로 악용될 수 있는 것으로 간주해야 합니다. 바로 이러한 구분이, 모든 것이 위험에 처해 있다고만 나열하는 단순한 CVE 목록과 실질적인 해결 방안을 구분 짓는 기준입니다.
의존성 그래프야말로 진정한 문제 해결 과제입니다.
취약점이 있는 Lodash 사본을 찾는 데는 한 번의 스캔이면 충분합니다. 하지만 이에 대해 어떻게 대처할지 결정하는 데는 훨씬 더 오랜 시간이 걸립니다. 2022년 Snyk 오픈소스 보안 현황 보고서에 따르면, 개발 중인 평균적인 애플리케이션은 69개의 종속성에서 49개의 취약점을 보유하고 있는 것으로 나타났으며, Lodash와 같은 유틸리티 라이브러리는 바로 여러 계층 깊숙이 자리 잡은 유형에 해당합니다.
작업 자체는 충분히 구체적입니다. 먼저 각 Lodash 버전을 도입한 상위 종속성을 파악한 다음, 중복 제거가 정말로 안전한지, 아니면 두 사용처가 실제로 서로 다른 메이저 버전을 필요로 하는지 확인하세요. 오버라이드나 해결 방식이 지속 가능한지, 아니면 단지 해결을 미룬 빚에 불과한지 판단하세요. 번들된 사본과 서버 측 사본은 서로 독립적으로 해결되므로 별도로 테스트하세요. 그런 다음 npm이 전혀 감지할 수 없는 하드코딩되거나 수동으로 복사된 빌드를 찾아내세요.
Lodash는 자체적으로 런타임 종속성이 전혀 없으므로, Lodash 아래의 트리는 비어 있습니다. 문제는 정반대입니다. Lodash는 수천 개의 다른 트리에서 잎 노드로 존재하며, 바로 그곳에서 복사본이 급증합니다. 지원되는 버전의 경우, TuxCare ELS는 취약점이 있는 직접적 및 전이적 복사본을 패치된 빌드로 대체할 수 있는데, 이는 스캐너가 하나의 저장소에서 여섯 가지 서로 다른 버전을 탐지해 냈을 때 이 작업의 핵심 목적입니다.
현장 이야기: 팀에서는 “Lodash를 업그레이드”하고 티켓을 종결했음에도 불구하고 다음 스캔에서 여전히 실패할 수 있습니다. 직접 업그레이드 시 하위 패키지가 고정해 둔 버전을 재정의하지 않기 때문에, 최상위 종속성을 업그레이드하면서도 4.17.15 버전의 전이적 복사본 세 개는 그대로 남겨두었기 때문입니다.
이 부분에 대해서는 솔직하게 언급할 필요가 있습니다. 선택적 체이닝, 스프레드 구문, 그리고 최신 배열 메서드 덕분에 많은 Lodash 헬퍼 함수들이 더 이상 필요하지 않게 되었습니다. 심층 병합(deep merge)의 의미론, cloneDeep 예외적인 경우, debounce 행동, 그리고 fp 모듈은 사정이 다릅니다.
그러면 결정의 문제가 제기됩니다. Lodash가 몇 군데의 간단한 호출 위치 뒤에 위치해 있다면, 이를 제거하는 것이 대개 더 깔끔한 선택입니다. 만약 _.merge 또는 _.cloneDeep 재무 또는 보고 로직에 영향을 미치는 경우, 해당 제거 작업을 별도의 테스트 계획이 필요한 기능 변경으로 간주해야 합니다. 이러한 변경은 CI(지속적 통합)를 통과한 후에도 월말 보고서에 오류를 일으킬 수 있기 때문입니다.
네 가지 개선 방안
단 하나의 해결책은 없으며, 그렇지 않은 척하는 것이 바로 문제 해결을 지연시키는 원인입니다. 현실적인 대안으로는 네 가지 경로가 있으며, 대부분의 부동산 관리 기관은 그중 하나 이상을 활용하고 있습니다.
| 경로 | 다음과 같은 경우에 가장 좋습니다 | 비용 |
|---|---|---|
| Lodash를 직접 업그레이드하기 | 해당 직접 종속성을 소유하고 있으며, 테스트가 호출 위치를 모두 다루고 있습니다. | 로우, 특별한 이유가 있어서 고정된 게 아니라면 |
| 함수를 네이티브 JS로 대체하기 | 소수의 간단한 호출 위치(연쇄 호출, 스프레드, 배열 메서드) | 보통 수준이며, 병합/복제 로직의 경우 더 높음 |
| 상위 패키지를 업그레이드합니다 | Lodash는 관리되는 종속성을 통해 간접적으로 포함됩니다. | 부모 클래스의 호환성 깨는 변경 사항에 따라 다릅니다. |
| 기존 릴리스에 패치를 적용하고 보류하기 | 그래프에 특정 구버전을 고정적으로 포함시켜야 합니다. | 현재 진행 중이지만, 강제 이주는 없다 |
바로 그 마지막 단계에서 벤더 패치의 진가가 발휘됩니다. TuxCare ELS for Libraries는 Lodash 1.3.1, 2.4.2, 3.2.0, 3.10.1, 4.5.0, 4.17.4, 4.17.5, 4.17.11, 4.17.15, 4.17.19, 4.17.21 및 4.18.1 버전을 지원하므로, 업그레이드할 수 없는 버전에서도 업스트림 수정 사항의 보안 혜택을 계속 누릴 수 있습니다.
이 메커니즘은 그다지 화려하지 않은데, 바로 그게 핵심입니다. 루트 디렉터리에 npm 별칭을 추가하면 됩니다. package.json ~와 함께 overrides 따라서 전이적 복사본은 패치된 빌드로 해결되거나, 지원 대상 의존성이 여러 개 포함된 저장소에 TuxCare Patcher를 실행하면 됩니다. 이것이 바로 전이적 복사본 문제에 대한 직접적인 해결책입니다.
알아두면 좋은 팁: 선언 overrides 루트 디렉터리에서 package.json, npm은 설치된 종속성 내부에서 선언된 재정의 항목을 무시하기 때문입니다. 별칭이 지정된 패치된 빌드 경로를 지정한 다음, npm이 종속성 트리 전체에서 취약한 패키지 이름과 일치하는 항목을 유지 관리되는 대체 패키지로 해결하도록 해야 합니다.
TuxCare가 구버전 Lodash의 보안을 어떻게 보장하는지
10년 전의 유틸리티 라이브러리에 수정 사항을 역이식하는 것은 단순히 복사-붙여넣기 작업이 아니며, 이를 그런 식으로 취급하면 애플리케이션이 의존하는 극한 사례들이 오히려 깨지게 됩니다. 이 과정은 네 단계로 진행됩니다.
첫째, 공개된 버전 범위가 모든 애플리케이션에서 악용 가능성을 나타낸다고 가정하기보다는, 새로 공개된 각 정보가 실제로 해당 범위에 포함되는지 분석해야 합니다. 둘째, 관련 없는 동작 변경을 끌어들일 필요 없이 보안 의도만 역이식해야 합니다. 애플리케이션은 Lodash의 극단적인 경우(edge-case) 동작에 의존하고 있는데, 선의에서 비롯된 리팩토링이 이러한 동작을 은연중에 변경할 수 있기 때문에, 이 점에 대한 절제가 그 어느 곳보다도 중요합니다.
셋째, 공통 함수와 구체적으로 문제가 발생한 경로를 모두 회귀 테스트해야 하므로, 프로토타입 오염 문제를 해결하더라도 다음은 변경되지 않습니다. _.merge 정당한 중첩 객체에 대한 반환값을 제공합니다. 넷째, 빌드된 모든 패키지에 서명을 하고 각 릴리스와 함께 SBOM 및 VEX 문서를 공개하여, 감사 담당자가 단순한 약속이 아닌 검증 가능한 출처 정보를 얻을 수 있도록 하십시오.
이는 업스트림 혁신이 아닌 관리 업무이며, 이러한 구분은 의도된 것입니다. TuxCare는 업스트림의 우선순위가 다른 곳으로 옮겨간 후에도 운영 코드를 안전하고 안정적으로 유지하는 데 특화되어 있으며, 수년간 운영될 것으로 예상되는 시스템을 위해 예측 가능하고 호환성을 최우선으로 하는 패치 작업을 수행합니다.
Lodash 리셋 비용은 누가 부담했는지, 그리고 이 모델이 왜 중요한가
Lodash는 이름을 붙일 만한 패턴의 가장 대표적인 사례입니다. OpenJS 재단의 지원을 받아 소버린 테크 에이전시(Sovereign Tech Agency)가 관리한 자금으로, 수백만 개의 시스템이 의존하는 라이브러리의 거버넌스 및 보안 작업에 비용이 지원되었으며, OpenJS의 관련 기사는 전문을 읽어볼 가치가 있습니다.
그 자금이 보장하지 못하는 것은 3.10.1 버전의 무기한 유지보수입니다. 이 자금은 현재 프로젝트를 강화했을 뿐, 자원봉사자들이 2015년 출시된 버전에 수정 사항을 역이식하도록 강제하지는 않았습니다. 그렇지 않은 척하는 것이야말로 유지보수자들이 소진되는 원인입니다.
생태계 지속가능성 프로그램은 이러한 격차를 상업적으로 메우며, 수익의 일부는 참여 프로젝트에 환원됩니다. TuxCare는 OpenJS 재단의 골드 회원사이자 이 프로그램의 제공업체 중 하나로, Node.js 지원 종료 페이지에 HeroDevs 및 NodeSource와 함께 등재되어 있습니다.
이 점은 분명히 밝혀둘 필요가 있습니다. 그래야 모두가 정직하게 행동할 수 있기 때문입니다. 4.17.21용 벤더 패치는 업스트림 릴리스가 아닌 벤더 패치이며, 훌륭한 툴링은 그 출처를 숨기지 않고 명확하게 드러냅니다. 또한, 파일을 전혀 이동할 수 없는 경우, ELS for Lodash는 해당 버전을 실행해야 하는 기간 동안 npm을 통해 실제로 실행 중인 버전에 수정 사항을 전달합니다.
자주 묻는 질문
Lodash는 더 이상 지원되지 않는 건가요?
아닙니다. 수년 동안 개발이 중단된 상태였지만, OpenJS를 통해 다시 활발히 유지보수되고 있으며, 자금 지원을 받은 거버넌스 재구축이 이루어졌고 현재 4.18 계열의 버전이 출시되어 있습니다.
4.17.21 버전은 여전히 안전한가요?
재개 이후 공개된 취약점과는 무관합니다. 이 취약점은 CVE-2025-13465 및 CVE-2026-2950(프로토타입 오염) 등 세 건의 보안 권고 사항에 해당하는 범위에 속합니다. _.unset 그리고 _.omit(첫 번째 수정 사항을 우회하는 방법 포함) 및 CVE-2026-4800 (_.template (코드 삽입). 4.18.1 버전으로 업그레이드하면 이 세 가지 문제가 모두 해결되며, 각 문제에 대한 수정 사항이 포함된 유지 관리 빌드를 적용해도 마찬가지로 해결됩니다.
4.18.1 버전을 사용해야 하나요?
적극적으로 유지보수되고 있는 프로젝트의 경우, 그렇습니다. 4.18.1이 현재 릴리스 버전이며, 4.18.0에 포함된 모듈식 빌드 회귀 문제를 수정했습니다. 그래프에 특정 구버전이 고정되어 있는 경우, TuxCare의 ‘Endless Lifecycle Support’를 통해 제공되는 패치 및 고정 릴리스를 대안으로 사용할 수 있습니다.
도대체 Lodash가 여전히 필요한가요?
대개 그렇지 않습니다. 네이티브 구문이 많은 간단한 헬퍼 함수들을 대체했고, structuredClone 일반적인 심층 복제 사례를 다루지만, 클래스 프로토타입과 속성 메타데이터는 제외하며, 복제할 수 없는 값(함수 포함)에 대해서는 예외를 발생시킵니다. 심층 병합은, debounce, 그리고 fp 이 모듈에는 여전히 깔끔한 한 줄짜리 대체 코드가 없습니다.
티켓을 닫기 전에
“Lodash 업데이트”는 접근 가능한 모든 사본이 파악될 때까지는 시정 조치 계획이 아닙니다. TuxCare CVE 트래커에서 해당되는 모든 버전에 대한 최신 권고 사항을 확인할 수 있습니다.
