주요 내용
- Commons Lang은 엔터프라이즈 자바 스택 전반에 걸쳐 사용되고 있으므로, 패치가 적용되지 않은 하나의 사본만으로도 전체 시스템에 문제가 발생하게 됩니다.
- 아파치는 백포트 브랜치를 사용하는 대신 후속 릴리스를 통해 수정 사항을 제공하므로, 구버전인 3.x 버전에는 더 이상 수정 사항이 적용되지 않습니다.
- 서둘러 진행한 업그레이드는 다음과 같은 이유로 실패하기 마련입니다.
commons-lang3보통 전이적으로 발생하며, 매개 변수와 관련된 예기치 못한 문제가 실행 시에 나타납니다. - TuxCare의 ELS는 구버전 Commons Lang에 대한 패치 적용 빌드를 제공하므로, 사용자는 향후 계획을 세우는 동안에도 계속해서 지원을 받을 수 있습니다.
Commons Lang은 아무도 회의를 잡지 않는 의존성입니다. 이 라이브러리는 JDK에 포함되지 않은 헬퍼 함수들을 제공해 주며, StringUtils 그리고 ClassUtils 그리고 NumberUtils 그리고 빌더 클래스들을 처리한 뒤, 사용자가 소유한 모든 서비스의 배경으로 사라집니다. 런타임에 아무것도 불러오지 않으며, 거의 오류가 발생하지 않고, 2002년 Commons Lang 1.0이 출시된 이래로 줄곧 조용히 그 역할을 수행해 왔습니다. MvnRepository는 현재 이 라이브러리를 Core Utilities 카테고리의 최상위에 배치하고 있으며, 이를 사용하는 것으로 등록된 아티팩트가 약 50,000개에 달합니다. 이는 여러분의 빌드 파일 어딘가에 이 라이브러리가 거의 확실하게 포함되어 있다는 것을 정중하게 표현한 말입니다.
바로 그 점이 오래된 사본을 짜증 나게 만드는 이유입니다. 오직 귀하의 청구 서비스에서만 사용하는 라이브러리의 CVE는 해당 서비스 하나에만 영향을 미칩니다. 반면, commons-lang3 코드가 이를 호출하는 곳이라면 어디든 실행되며, 많은 엔터프라이즈 자바 환경에서는 그 위치가 너무 많아 아무도 정확한 목록을 파악하지 못하고 있는 실정입니다.
바로 이 부분이 팀들을 당황하게 만드는 부분입니다. 아파치(Apache)는 개별 커먼즈 랭(Commons Lang) 릴리스에 대한 지원 종료 일정을 공개하지 않습니다. 지원 종료 공지나, 현재 사용 중인 브랜치의 개발이 중단되었다는 안내도 없습니다. 또한 이전 릴리스에 대한 유지보수 브랜치도 제공되지 않기 때문에, 새로운 버전에 수정 사항이 반영되면 일반적으로 해당 수정 사항을 기존 릴리스로 되돌려주는 유지보수 흐름이 없습니다.
Apache Commons Lang이 실제로 릴리스를 어떻게 처리하는지
스캐너 때문에 말다툼을 벌이게 되기 전에 알아두면 좋은 점입니다.
먼저 분할된 구조부터 살펴보겠습니다. 아파치는 2011년에 Commons Lang 3.0을 출시하면서 의도적으로 패키지 네임스페이스를 분리했습니다. 2.x 계열은 다음 위치에 있습니다. org.apache.commons.lang 그리고 3.x는 다음 위치에 있습니다. org.apache.commons.lang3. 이는 두 버전이 서로 충돌하지 않고 동일한 클래스패스에 공존할 수 있도록 의도된 것이었으며, 많은 대규모 코드베이스에서 여전히 두 버전을 모두 포함하고 있는 이유이기도 합니다. 라이프사이클 측면에서 볼 때, 2.x는 모든 중요한 측면에서 이미 종료되었습니다. 마지막 릴리스는 2011년의 2.6이었으며, 개발은 3.x 계열로 옮겨졌고 그 이후로는 새로운 버전이 출시되지 않았습니다. 여전히 2.x를 사용 중인 사용자들은 2011년부터 업스트림 수정 사항 없이 시스템을 운영해 온 셈입니다.
3.x 계열은 대부분의 사람들이 생각하는 것과는 다르게 작동합니다. 아파치는 JDK와 달리 병행되는 장기 지원(LTS) 브랜치를 유지하지 않습니다. 수정 사항은 다음 릴리스에 반영되며, 공개된 릴리스 이력에는 3.12.x 유지보수 스트림이나 3.17.x 백포트 브랜치가 나타나지 않습니다. 따라서 아파치가 3.x 릴리스에 대해 공식적으로 지원 종료(EOL)를 선언한 적은 없지만, 실질적인 결과는 마찬가지입니다. 수정 사항이 더 새로운 버전에 반영된다면, 사용자의 버전에는 적용되지 않습니다.
감사관에게 보고서를 작성할 때는 이러한 구분을 정확히 하는 것이 중요합니다. “이 버전에 대한 업스트림 백포트 릴리스는 없습니다”라는 표현은 정확하고 타당합니다. 반면, “특정 날짜를 기점으로 공식적으로 EOL(지원 종료)되었습니다”라는 표현은 정확하지 않습니다. 아파치 측에서 그런 내용을 발표한 적이 없기 때문입니다.
2026년 9월 현재 최신 버전은 2025년 11월 12일에 출시된 3.20.0입니다. Java 최소 지원 버전도 알아두면 좋습니다. 3.9 버전부터 이 라이브러리는 Java 8을 요구하므로, 3.9 이후의 모든 릴리스에는 Java 8 이상이 필요하며, 프로젝트의 CI는 현재 지원되는 LTS 릴리스, 즉 8, 11, 17, 21 및 25에서 실행됩니다.
| 버전 정보 | 패키지 네임스페이스 | 현재 상황 | 최신 출시작 |
|---|---|---|---|
| Commons Lang 2.x | org.apache.commons.lang |
개발이 종료되었으며, 2011년 이후로 수정 사항이 없습니다. | 2.6 (2011) |
| Commons Lang 3.x, 이전 버전 | org.apache.commons.lang3 |
아직 생산 버전에서 배포 중이지만, 백포트 브랜치는 없음 | 해당 없음, 폐지됨 |
| Commons Lang 3.x, 최신 버전 | org.apache.commons.lang3 |
수정 사항이 실제로 적용되는 곳 | 3.20.0 (2025년 11월) |
‘Commons Lang EOL’이 실제로 무엇을 의미하는가
수정 사항이 더 이상 전달되지 않습니다
2025년 7월에 공개된 CVE-2025-48924는 딱히 눈에 띄는 점이 없다는 점에서 오히려 좋은 예시입니다. ClassUtils.getClass() 중첩된 클래스 이름을 재귀적으로 처리하므로, 입력 길이가 충분히 길 경우 스택 오버플로가 발생하여 StackOverflowError. 이는 예외(Exception)가 아닌 오류(Error)이기 때문에, 애플리케이션은 대개 이를 처리하지 못하며, 이로 인해 애플리케이션이 중단될 수 있습니다. 아파치(Apache)는 이 취약점에 낮은 등급을 부여했고, CISA의 ADP는 5.3점을 매겼으며, OSS-Fuzz가 이를 발견했습니다. 이 취약점이 뉴스 헤드라인을 장식할 일은 결코 없었을 것입니다.
그 형태가 흥미로운 부분입니다. 이 권고안은 commons-lang 2.0부터 2.6까지를 다루며, 모든 commons-lang3 3.0부터 3.18.0(3.18.0 제외)까지의 릴리스에는 업스트림 수정 사항이 반영되어 있습니다. 업스트림 패치가 적용된 3.17.0 버전은 존재하지 않습니다. 프레임워크나 변경 동결 정책으로 인해 해당 버전을 고수해야 하거나, 40개 서비스에 걸쳐 유틸리티 라이브러리의 회귀 테스트를 수행할 예산이 없는 경우, 해당 문제는 스캐너 출력 결과에 무기한으로 남아 있게 됩니다.
트라이아징을 진행할 때 명심해야 할 점은, 새로 공개된 Commons Lang CVE가 사용자가 현재 실행 중인 버전에 자동으로 영향을 미치는 것은 아니라는 사실입니다. 많은 경우 영향을 미치지 않을 것입니다. EOL로 인해 사라지는 것은, 버전을 변경하지 않고도 영향을 미치는 CVE를 수정할 수 있는 능력입니다.
규정 준수가 까다로워지는 순간
PCI DSS v4.0.1 요구사항 6.3.3에 따르면, 자체적인 요구사항 6.3.1 프로세스를 통해 등급이 매겨진 중대한 취약점에 대한 패치는 패치 출시일로부터 1개월 이내에 적용되어야 합니다. 그 외의 모든 사항은 해당 위험이 귀사의 환경에 얼마나 심각한지 자체적으로 평가하여 결정한 일정에 따라 처리됩니다. PCI DSS v4.0에서는 이 1개월 적용 기간을 중대하거나 보안 수준이 높은 패치에도 동일하게 적용했으나, v4.0.1에서는 이를 다시 중대한 취약점으로만 제한했습니다.
다른 규정들은 그다지 구체적인 지침을 제시하지 않으며, 이를 과장해서 설명하는 것은 기술에 정통한 독자의 관심을 잃게 만드는 확실한 방법입니다. FedRAMP는 심각도에 따라 시정 기간을 정합니다. HIPAA 보안 규칙은 구체적인 일수를 명시하지 않은 채 위험 분석을 수행하고 알려진 취약점을 관리할 것을 요구합니다. SOC 2는 기준 기반이므로, 감사인은 귀사가 문서화한 패치 프로세스가 실제로 따르고 있는 절차와 일치하는지 여부를 중요하게 여깁니다. 이 규정들 중 어느 것도 시정 방안이 없는 구성 요소를 방어하기 쉽게 만들어주지는 않습니다.
유틸리티 라이브러리를 사용할 때 특히 골치 아픈 문제는 범위 설정입니다. 취약점이 있는 애플리케이션 서버는 한 곳에 위치해 있으며 하나의 취약점 결과를 생성합니다. 취약점이 있는 commons-lang3 이를 포함하는 아티팩트 하나당 한 번씩 나타나기 때문에, 패치가 적용되지 않은 단일 버전이 서로 소통한 적이 없는 여러 팀이 함께 참여해야 하는 시정 작업으로 번지게 됩니다. 보완 통제는 시간을 벌어줄 수 있으며 대개 올바른 선택이지만, 이것이 충분한지 여부는 단순히 체크박스를 선택하는 문제가 아니라 평가자와의 논의를 통해 결정해야 할 사항입니다.
Apache Commons Lang 지원 종료(EOL) 이후의 선택지
최신 릴리스로 업그레이드하기
가능한 팀의 경우, 최신 릴리스로 전환하는 것이 장기적으로 올바른 해결책입니다. 아파치는 3.x 버전 내 호환성을 상당히 엄격하게 유지하고 있으며, lang3 네임스페이스는 전체 버전 라인에 걸쳐 안정적으로 유지되어 왔고, 프로젝트 측에서는 릴리스마다 바이너리 호환성 보고서를 공개하고 있습니다. 개별 릴리스마다 여전히 사용 중단되는 기능이 있고 동작이 조정되므로, 이를 단순히 무비판적으로 적용하기보다는 업그레이드로 간주해야 합니다.
문제는 대개 한 줄만 수정하면 끝나는 경우가 거의 없다는 점입니다. commons-lang3 일반적으로 이 파일은 다른 요소(지속성 계층, 프레임워크, 또는 다른 Apache Commons 구성 요소 등)에 의해 프로젝트 구조에 추가된 것이기 때문에, 최종적으로 사용하게 되는 버전은 Maven의 중재(mediation)가 선택한 버전입니다. 프로젝트에 자체 버전을 명시하여 직접 선언하는 경우 pom.xml 해당 경쟁에서 승리합니다. 버전을 명시하지 않고 상위 POM이나 가져온 BOM에서 제공하도록 하면, 관리되는 버전이 대신 결정합니다. 다른 사람의 아티팩트 내에 번들로 포함된 음영 처리된 사본은 중재 범위를 완전히 벗어나며, 중재 과정의 어떤 단계에서도 영향을 받지 않습니다. 바로 그 때문에 mvn dependency:tree -Dverbose 마지막 명령어가 아니라 가장 먼저 실행되는 명령어입니다.
다른 유틸리티 라이브러리로 교체하기
일부 팀은 이러한 기회를 통해 해당 의존성을 전면적으로 재검토하기도 합니다. Guava는 Commons Lang과 상당 부분 중복되는 기능을 다루고 있으며, Spring은 자체 유틸리티 클래스를 제공하고 있고, Java 8 이상의 핵심 API는 원래 Commons Lang이 제공하려 했던 기능 중 상당 부분을 흡수했습니다.
이는 타당한 아키텍처적 결정이지만, 그에 따른 장단점을 솔직하게 인정해야 합니다. Guava 역시 자체적인 CVE 이력을 가진 타사 의존성이며, Apache Commons에 비해 변경 빈도에 대해 더 유연한 입장을 취하고 있습니다. 왜냐하면 표시된 모든 항목은 @Beta 어떤 릴리스에서든 변경되거나 사라질 수 있습니다. 이는 하나의 공급망 표면을 제거하는 것이 아니라 다른 것으로 교체하는 것입니다. 마이그레이션은 또한 모든 호출 지점을 검토하고, 이를 재작성하며, 대체된 코드에서 동작을 검증하는 과정을 의미합니다. 이는 스프린트가 아닌 마이그레이션 프로젝트이므로, 다음 달로 예정된 시정 조치 마감 기한을 맞추기에는 부적절한 해결책입니다.
현재 버전을 유지하되, 어쨌든 패치를 적용하세요
세 번째 방법은 해당 버전을 그대로 유지한 채 업스트림 이외의 다른 곳에서 보안 수정 패치를 받는 것입니다. 이것이 바로 TuxCare의 ‘Endless Lifecycle Support’가 제공하는 서비스이며, 세 가지 방법 중 유일하게 패치 상태를 업그레이드 일정과 분리해 주는 방식입니다.
커먼스 랭크 업그레이드를 서두르면 왜 역효과가 나는가
압박이 가해지면—보통 이는 결정이라기보다는 스캐너가 무언가를 발견하는 형태로 나타나곤 합니다—본능적으로 버전을 올리고 출시하고 싶어집니다. 그러면 몇 가지 문제가 발생하기 마련입니다.
버전 조정(version mediation)이 첫 번째 단계입니다. 여러 종속성이 서로 다른 버전을 선언하면 commons-lang3 버전의 경우, Maven은 코드가 테스트된 버전이 아닌 그래프에서 가장 가까운 선언을 선택하며, 동일한 깊이에서 선언 순서에 따라 우선순위를 결정합니다. 이 경우 빌드는 성공 상태를 유지할 수 있지만, NoSuchMethodError, a NoClassDefFoundError 혹은 테스트에서 놓친 코드 경로에서 나중에 다른 호환성 문제가 발생할 수도 있습니다.
평소처럼 대응하면 상황이 더 악화될 수 있습니다. 팀들은 <exclusions> Maven이나 Gradle의 종속성 제약 조건을 통해 원하는 버전을 강제로 지정하면, 당장의 오류는 해결되지만 다음 업그레이드 시 아무도 건드리고 싶어 하지 않는 종속성 구조가 남게 됩니다. 시간적 압박 속에서 여러 서비스에 걸쳐 이런 방식을 적용하다 보면, 일시적인 문제를 해결하는 대신 유지보수 문제를 떠안게 되는 셈입니다.
행동 드리프트는 눈에 잘 띄지 않는 위험 요소입니다. Commons Lang의 문자열, 반사 및 숫자 관련 헬퍼 함수들은 수많은 릴리스를 거치며 개선되어 왔으며, 극단적인 사례에 의존하거나 특정 상황에서 null 처리를 하는 코드는 StringUtils 예를 들어, 메서드는 시그니처를 변경하지 않고도 동작을 바꿀 수 있습니다. 컴파일 시에는 아무런 오류도 발생하지 않습니다. 단지 실제 실행 시에는 약간 다른 결과를 반환할 뿐입니다.
이 모든 것이 필연적인 것은 아닙니다. 이는 작업이 의존성 그래프가 아닌 규정 준수 일정에 맞춰 정해진 기간 내에 강제로 압축될 때 발생하는 현상입니다. 이러한 전환 과정을 매끄럽게 헤쳐 나가는 팀들은 매번 똑같은 세 가지를 실천합니다. 바로 트리를 제대로 검토하고, 코드가 실제로 의존하는 동작을 테스트하며, 이 두 가지가 진행되는 동안에도 일관된 보안 태세를 유지하는 것입니다.
Apache Commons Lang용 ELS가 제공하는 기능
TuxCare는 지난 15년 동안 오픈소스 소프트웨어에 대한 패치를 제공해 왔으며, 전체 포트폴리오에 걸쳐 42만 개 이상의 패치를 배포했습니다. Commons Lang 서비스는 사용자들이 실제로 사용 중인 버전을 지적하고 있습니다. 현재 문서에는 기존 네임스페이스에 Commons Lang 2.4와 2.6이 나열되어 있으며, 그 외에도 commons-lang3 3.4, 3.8.1, 3.10, 3.11, 3.12.0, 3.14.0, 3.15.0 및 3.17.0.
기계적인 측면에서는 별다른 특징이 없는데, 사실 그게 바로 핵심입니다. 자격 증명은 Maven의 settings.xml, TuxCare 저장소는 다음 위치에 있습니다. pom.xml, 또는 이 둘에 해당하는 Gradle 구문을 사용하고, 의존성을 유지 관리되는 빌드로 지정하면 2.6은 다음과 같이 됩니다. 2.6-tuxcare.1. 호출 대상은 변하지 않고, 빌드 도구도 변하지 않으며, CI는 여느 때와 마찬가지로 아티팩트를 계속 해결합니다. 빌드 과정에서 공급업체에 직접 접속하지 않도록 하려면, 자체 리포지토리 관리자를 통해 전체 과정을 프록시 처리할 수 있습니다.
처리 기간과 관련하여, TuxCare는 CVSS 7.0 이상의 ‘높음’ 및 ‘매우 높음’ 등급 취약점에 대해서는 공개 후 14일, ‘중간’ 등급 취약점에 대해서는 60일, ‘낮음’ 등급 취약점에 대해서는 90일을 목표로 하고 있습니다. 이 기간은 취약점이 공개된 시점부터 시작됩니다. PCI DSS는 패치 출시 시점부터 기간을 계산하므로, 14일 목표 기간은 1개월의 여유 기간 내에 조치를 취할 수 있도록 설정된 것입니다. 요구 사항 6.3.3은 ‘매우 높음’ 등급 취약점에 적용됩니다.
의존성 커버리지에 대해 한 마디 덧붙이자면, 이 개념을 과대평가하기 쉽기 때문입니다. commons-lang3 설계상 런타임 종속성이 전혀 없기 때문에, 그 밑에 숨겨진 트리가 존재하지 않습니다. 오히려 그 반대입니다. commons-lang3 대개는 다른 사람의 트리 내에 위치한 전이적 의존성이기 때문에, 바로 그 때문에 간과되기 마련입니다. ELS for Libraries는 추가 비용 없이 지원되는 구성 요소의 직접적 및 전이적 의존성을 모두 다루며, 이는 Struts나 Tomcat과 같은 경우 매우 중요합니다. 특히 Commons Lang의 경우, 다른 모든 것이 의존하고 있는 이 라이브러리에 마침내 유지보수가 이루어지는 빌드가 뒷받침된다는 점이 큰 장점입니다.
나머지는 여러분이 원할 만한 기본 구성 요소들입니다. 패키지에는 분리된 OpenPGP 서명이 포함되어 있으며 TuxCare가 관리하는 레지스트리에서 제공되므로, 진위성과 무결성은 단순히 가정되는 것이 아니라 검증 가능합니다. Commons Lang을 포함한 Java 라이브러리를 위한 CycloneDX VEX 피드가 제공되는데, 이를 통해 스캐너와 감사 담당자는 단순히 패키지를 언급하는 CVE가 아닌, 특정 빌드에 실제로 적용되는 CVE가 무엇인지 파악할 수 있습니다. SBOM은 SPDX 및 CycloneDX 형식으로 제공되며, Java 피드는 패키지와 함께 Nexus에 게시됩니다. TuxCare 지원 포털을 통해 연중무휴 24시간 지원이 제공되며, 여기에는 문제 분류, 패치 요청 및 빌드 시스템과 관련된 간혹 발생하는 문제 해결 등이 포함됩니다.
현재 지원되는 Apache Commons Lang 버전의 목록은 TuxCare의 문서를 참조하십시오. 어떤 Apache Commons Lang CVE가 이미 수정되었는지 확인하려면 실시간 CVE 추적기를 살펴보십시오.
Commons Lang의 지원 종료로 인해 애플리케이션이 위험에 처하지 않도록 하세요
업스트림 유지보수를 받지 못하게 된다고 해서 그 자체로 문제가 발생하는 것은 아닙니다. 사실 그게 바로 문제이며, 스캐너나 감사관이 이를 로드맵에 포함시키기 전까지는 로드맵에 포함되지 않는 이유이기도 합니다. 애플리케이션은 계속 실행되고, 테스트도 계속 통과되며, 유일하게 달라진 점은 해당 버전에 대한 업스트림 수정 사항이 더 이상 제공되지 않는다는 것뿐입니다.
현재 릴리스로 업그레이드하는 것이 여전히 올바른 목표입니다. 이를 제대로 수행하려면 종속성 검토 범위를 설정하고, 코드가 실제로 의존하는 동작을 테스트하며, 다른 사람들도 일정을 확보하고 있는 릴리스 일정에 해당 작업을 맞춰야 합니다. 이것이 바로 계획이며, 마감일이 다른 사람에게 달려 있을 때는 계획을 세우는 데 필요한 시간을 확보하기 어렵습니다.
연장 지원이 바로 그 시간을 벌어주는 것입니다. 이는 업그레이드를 대체하는 것이 아니며, 누구도 그렇지 않은 척해서는 안 됩니다. 연장 지원은 지원 대상인 Commons Lang 버전에 패치를 지속적으로 적용해 주고, 평가관에게 제시할 수 있는 타당한 근거를 제공하며, 엔지니어들이 긴급한 라이브러리 마이그레이션 대신 분기별 로드맵에 집중할 수 있도록 해줍니다.
현재 Apache Commons Lang의 지원 종료(EOL)에 대해 고려 중이시라면, 지원이 종료된 일부 Commons Lang 및 commons-lang3 버전에 대한 확장 지원을 제공하는 TuxCare의 ‘Apache Commons Lang용 ELS’를 확인해 보시기 바랍니다.
