주요 내용
- 지원되지 않는 Werkzeug는 업스트림 수정 작업이 중단됨을 의미하며, 이로 인해 새로운 취약점이 발견될 때 영향을 받는 애플리케이션들은 업스트림 패치를 받을 수 없게 됩니다.
- 서둘러 업그레이드를 진행하면 생산 환경의 버그, 종속성 문제, 기술적 부채 및 예상치 못한 비용이 발생할 수 있습니다.
- TuxCare ELS는 사용자가 업그레이드에 적합한 시기를 계획하는 동안 구버전의 Werkzeug에 대한 패치를 지속적으로 적용해 줍니다.
Werkzeug을 기반으로 탄탄한 시스템을 구축했습니다. 라우팅도 잘 작동하고, 요청 처리도 깔끔하며, WSGI 레이어는 해야 할 일을 정확히 수행합니다. 그런데 의존성 스캐너에 경고가 뜬다거나, 보안팀에서 컨테이너 내에서 어떤 버전의 Werkzeug이 실행 중인지 물어보더니, 그 답이 2023년에 출시된 2.2.3 버전이라는 사실이 밝혀집니다.
이제 어떻게 하지?
이 문제는 겉보기에 비해 꽤 흔한데, 그 주된 이유는 거의 아무도 의도적으로 Werkzeug 버전을 선택하지 않기 때문입니다. Flask가 이를 불러오고, pip가 이를 해결하며, 뭔가 문제가 발생해 확인이 필요해질 때까지는 별다른 문제 없이 그대로 남아 있습니다. 그러니 Werkzeug 브랜치가 더 이상 업데이트되지 않을 때 실제로 어떤 일이 일어나는지, 그리고 이에 대해 현실적으로 어떤 조치를 취할 수 있는지 함께 살펴보겠습니다.
Werkzeug의 릴리스 방식은 실제로 어떻게 이루어지는가
Werkzeug는 Flask, Jinja, Click과 함께 Pallets 프로젝트에서 관리하고 있습니다. Pallets는 Python이나 Node.js처럼 각 브랜치별로 고정된 유지보수 기간이 명시된 장기 지원 일정을 공개하지 않습니다. 로드맵 어딘가에 Werkzeug 2.2의 지원 종료일이 공식적으로 발표된 바도 없습니다. 지원 여부는 유지보수 담당자가 실제로 배포하는 내용을 통해 추측해야 합니다.
전체적인 흐름은 따라가기 어렵지 않습니다. Werkzeug 2.x는 Python 2와 3.5 지원을 중단하고 코드베이스를 타입 어노테이션 방식으로 전환했습니다. Werkzeug 3.x는 이 흐름을 이어가며 더 이상 사용되지 않는 API를 제거하고 지원되는 Python 최소 버전을 높였으며, 현재는 Python 3.9 이상이 필요합니다. 현재 활발히 개발 중인 버전은 3.1.x이며, 2026년 4월 2일에 출시된 3.1.8이 2026년 8월 기준 최신 안정 버전입니다.
여기서 사람들이 헷갈리는 부분이 있습니다. 새로운 주요 릴리스가 이전 브랜치를 즉시 무효화한다는 직관적인 생각은 신뢰할 수 없습니다. Werkzeug 3.0.0은 2023년 9월 말에 출시되었지만, 2.3.8 버전은 2023년 11월 8일에 보안 수정 사항을 포함해 여전히 출시되었습니다. 백포트는 실제로 발생합니다. 다만 예측할 수 없고, 보장되지 않을 뿐입니다.
릴리스 이력을 통해 알 수 있는 것은 개발진의 관심이 어디에 쏠려 있는지입니다. 3.0 계열은 3.1.0이 출시되기 며칠 전인 2024년 10월 말에 마지막 릴리스인 3.0.6을 내놓은 이후로는 아무런 업데이트도 없었습니다. 2.x 계열은 2.3.8에서 종료되었습니다. 따라서 실질적으로 3.1.x만이 여전히 릴리스를 받고 있는 유일한 브랜치인 반면, 이전 브랜치들은 공식적으로 단종 선언을 받은 적은 없지만 비활성 상태로 보입니다. 이는 발표된 EOL(지원 종료) 날짜보다 더 나쁜 상황이라고 할 수 있습니다. 감사관에게 달력을 제시할 수도 없고, 제공된 적도 없는 유지보수 기간을 기준으로 계획을 세울 수도 없기 때문입니다.
자신의 상황이 어떤지 잘 모르겠다면, pip show werkzeug 약 2초 만에 답변해 줍니다. 버전이 3.1로 시작하지 않는다면, 앞으로 어떤 문제가 공개되더라도 업스트림에서 수정 사항을 제공할 것이라고 기대하지 마십시오.
프로덕션 환경에서 ‘지원되지 않는 Werkzeug’가 실제로 의미하는 바
지원이 종료됩니다. 애플리케이션은 계속 실행됩니다. 겉으로 보기에는 아무런 문제가 없습니다. 그렇다면 왜 이것이 중요한 것일까요?
패치 배포가 중단된다고 해서 취약점이 사라지는 것은 아니기 때문입니다. 위험은 공격자들이 Werkzeug 업데이트를 미루는 프로젝트 목록을 관리한다는 데 있는 것이 아닙니다. 사실은 그보다 훨씬 더 단순하고 지루한 문제입니다. 결함이 공개되고, 해당 결함이 여러분의 브랜치에 영향을 미치는데, 업스트림에서 이를 해결할 방법이 없는 것입니다. Werkzeug은 Flask의 기반이 되기 때문에, 그 결함은 그 위에 구축된 모든 프로젝트에 그대로 이어지게 됩니다.
다음 두 가지 공개 사례가 이 문제를 잘 보여줍니다.
CVE-2024-34069는 Werkzeug의 대화형 디버거에 존재하는 취약점으로, NVD와 Pallets 권고문 모두에서 7.5점(High)으로 평가되었습니다. 이 취약점을 악용하기 위해서는 공격자가 개발자로 하여금 자신이 제어하는 도메인 및 하위 도메인과 상호작용하게 하고, 디버거 PIN을 입력하게 하며, 애플리케이션 내에서 디버거를 트리거하는 URL을 추측하도록 유도해야 합니다. 이 세 가지 조건이 모두 충족될 경우, 디버거가 localhost에 바인딩되어 있더라도 개발자의 컴퓨터에서 코드가 실행될 수 있습니다. 이 문제는 3.0.3 버전에서 수정되었습니다.
바로 그 수정 사항이 핵심입니다. Werkzeug 3.0.3이 이 수정 사항을 포함한 첫 번째 릴리스이며, 이후 모든 3.x 릴리스에는 이 수정 사항이 그대로 이어졌습니다. 반면, 2.x 계열 릴리스 중에는 이 패치가 적용된 버전이 없으며, 2.x 계열의 마지막 릴리스인 2.3.8도 예외는 아닙니다. 따라서 사용 중인 버전이 2.x 계열에 속한다면, 이 문제를 해결하기 위해 해당 브랜치 내에서 업그레이드할 수 있는 버전은 없습니다.
자세히 알아보기 Werkzeug Security 및 현재 알려진 취약점에 대해 자세히 알아보세요.
CVE-2023-46136은 멀티파트 폼 파서에서 발생하는 서비스 거부(DoS) 취약점입니다. 업로드 데이터가 CR 또는 LF로 시작하고 그 뒤에 해당 문자가 포함되지 않은 메가바이트 단위의 데이터가 이어질 경우, 파서는 바이트 단위로 데이터를 내부 바이트 배열에 차례로 추가하며, 점점 커지는 버퍼에서 경계를 재검색하게 됩니다. 이로 인해 CPU 자원이 과도하게 소모되고, 해당 워커가 다른 요청을 처리하지 못하게 됩니다. 이 취약점은 2.3.7 및 그 이전 버전과 3.0.0 버전에 영향을 미치며, 2.3.8 및 3.0.1 버전에서 수정되었습니다. 티켓에서 이에 대해 논의하기 전에 알아두어야 할 점: NVD는 이 취약점에 7.5점(높음)을 부여한 반면, Pallets 권고문은 5.7점(중간)을 부여했는데, 이는 두 기관이 공격 경로와 필요한 권한에 대해 견해를 달리하기 때문입니다. 스캐너마다 참조하는 피드에 따라 심각도가 다르게 보고될 수 있습니다. 이 취약점에 노출되는 것은 공격자가 제공한 멀티파트 데이터를 실제로 파싱하는 엔드포인트를 가진 애플리케이션입니다.
유지 관리되는 브랜치에서는 이 두 가지 모두 버전 번호가 올라가는 것입니다. 지원되지 않는 브랜치에서는, 새로 공개된 취약점이 사용자가 업그레이드하거나 다른 곳에서 백포트를 적용할 때까지 업스트림에서 패치되지 않은 상태로 남아 있습니다.
지원 종료 후의 보안 및 규정 준수 위험
현재 버전의 표준인 PCI DSS v4.0.1은 요구사항 6.3.3에 따라 중대한 취약점에 대한 패치는 출시 후 1개월 이내에 설치해야 하며, 그 외 적용 가능한 패치는 조직이 문서화한 위험 기반 일정에 따라 처리해야 한다고 규정하고 있습니다. 버전 4.0에서는 이 1개월 기한을 심각도 높은 항목에도 확대 적용했으나, 4.0.1에서는 더 엄격한 표현으로 되돌아갔습니다. 이러한 개정 방향 자체보다 더 중요한 것은 그 이면에 깔린 전제, 즉 설치할 패치가 존재한다는 가정입니다.
해당 브랜치에 대한 업스트림 수정 사항이 없는 경우, 수정 작업이 늦어진 것은 아닙니다. 단지 해당 요구사항이 수립된 모델의 범위를 벗어난 상태일 뿐이며, 이는 평가자와 논의하기 더 어려운 사안입니다.
SOC 2에는 수명이 종료된 소프트웨어에 대한 금지 조항이 포함되어 있지 않으므로, 내부적으로 이 문제를 어떻게 설명할지 신중을 기해야 합니다. 이는 취약점 관리에 대한 의문을 불러일으키고, 효과적인 수정 방안을 입증할 수 있는지 여부에 대한 질문을 초래할 수 있습니다. 지원이 중단된 종속 구성 요소에 미해결 취약점이 발견되었고, 사용할 수 있는 패치도 없는 경우, 이는 후속 요청을 유발하고, 보안 설문 조사 진행을 지연시키며, 갱신 시점에 고객 위험 검토를 복잡하게 만드는 요인이 됩니다.
지원 기간이 만료되었을 때의 선택지
세 가지가 있는데, 어떤 방법을 선택해야 할지는 사용자를 탐지한 CVE보다는 애플리케이션의 Werkzeug 노출 면적이 실제로 얼마나 되는지에 더 크게 좌우됩니다.
Werkzeug을 최신 버전으로 업그레이드하세요
가장 흔한 답변이며, 많은 팀에게 있어 정답이기도 합니다. Werkzeug 3.1.x는 안정적이며, API가 더 깔끔하고, 타입 어노테이션이 실제로 유용합니다.
흥미로운 점은 이것이 단 한 줄의 변경으로 끝난다는 가정입니다. Werkzeug는 저절로 업데이트되지 않습니다. Flask 릴리스는 일반적으로 상한선을 고정하기보다는 최소 Werkzeug 버전을 명시하기 때문에, pip는 Flask 버전이 테스트된 적이 없는 최신 버전의 Werkzeug를 자유롭게 설치할 수 있습니다. 이는 가정이 아닙니다. Werkzeug 3.0에서는 url_quote, Flask 2.2.2가 파일 상단에서 가져오는 app.py, 따라서 가져오기 과정에서 페어링이 실패합니다. 이후 Werkzeug 3.1에서는 werkzeug.__version__, Flask 2.2.4 테스트 클라이언트는 사용자 에이전트 문자열을 생성할 때 여전히 이 값을 읽기 때문에, 새로운 컨테이너에서 테스트 스위트를 처음 실행할 때 AttributeError가 발생합니다. 이에 대한 내용은 Flask 이슈 5652.
따라서 Werkzeug를 업그레이드한다는 것은 사실상 Flask를 업그레이드하는 것을 의미할 수 있으며, 이는 Flask에 고정된 모든 확장 기능을 검토해야 함을 뜻하고, 결국 의존성 트리 전반에 걸친 호환성 점검을 수행해야 함을 의미합니다. 코드베이스가 작고, 확장 기능 목록이 짧으며, 테스트 커버리지가 충실하다면 지금 바로 진행하고 이 글을 더 이상 읽지 마세요. 만약 10년 동안 미들웨어가 축적된 성숙한 애플리케이션이라면, 적절한 예산을 책정하세요.
다른 프레임워크로 마이그레이션하기
일부 팀은 지원되지 않는 종속성을 보고, 진정한 해답은 전혀 다른 곳에 있다고 판단하기도 합니다. ‘모든 것이 포함된’ 프레임워크를 원한다면 Django, Starlette를 기반으로 한 비동기 API 프레임워크를 원한다면 FastAPI, 특정 설계 철학이 반영되지 않은 ASGI 툴킷을 원한다면 Starlette 자체, 더 가벼운 것을 원한다면 Bottle이나 Falcon을 선택하면 됩니다. 특히 FastAPI는 상당한 성장세를 보이고 있으며, 비동기 우선 설계 방식을 채택하고 있고, 무료로 생성되는 OpenAPI 문서도 제공합니다.
이 중 어느 것도 Werkzeug을 그대로 대체할 수 있는 대안은 아닙니다. 라우팅 로직, 요청 파싱, 템플릿 렌더링, 미들웨어 스택, 오류 처리, 배포 구성 등 모든 부분이 재구현되어야 합니다. Werkzeug을 상당 부분 활용하고 있는 애플리케이션의 경우, 제대로 계획되고 테스트된 마이그레이션은 단순한 정리 작업이 아니라 여러 스프린트나 분기에 걸친 프로젝트가 됩니다. 화요일에 스캐너가 오류를 표시했기 때문이 아니라, 애초에 비동기 방식을 도입하고 싶었기 때문에 이 작업을 진행해야 합니다.
확장 지원 서비스를 활용하여 시간을 벌자
세 번째 방법은 현재 사용 중인 버전을 유지하면서 해당 버전에 대한 보안 패치를 계속 받아 사용하는 것입니다. 이렇게 하면 다음 취약점 공개를 제기하는 사람의 손에 일정이 좌우되는 것을 막고, 일정을 다시 여러분의 로드맵에 맞출 수 있습니다. 이 방식이 어떻게 작동하는지에 대한 자세한 설명은 아래에서 확인하실 수 있습니다.
업그레이드를 서두를 때 발생하는 진정한 비용
30명으로 구성된 SaaS 팀이 3주 후 SOC 2 심사 기간이 종료되기 전에 중대성 높은 문제점을 해결하라는 지시를 받았다고 상상해 보세요. 업그레이드 자체는 어려운 부분이 아닙니다. 그로 인한 여파가 문제입니다.
테스트 스위트에서 놓친 버그들이 운영 환경에서 드러나기 시작하고, 지원팀에는 예전에는 아무 문제 없던 동작에 대한 티켓이 잇달아 접수됩니다. 전이적 의존성은 실제 부하가 가해졌을 때만 나타나는 특유의 극한 사례를 유발하는데, 이로 인해 재현하기조차 힘든 간헐적 오류가 발생하며, 사고 검토 시 이를 설명하는 일은 더욱 골치 아픈 일이 됩니다.
그러면 대충 넘어가는 일이 시작됩니다. 리팩토링은 생략되고, 임시방편이 쌓여만 가며, 모두는 나중에 제대로 고치기로 합의합니다. 6개월이 지났지만 ‘나중’은 오지 않았고, 코드베이스는 정리해 주기로 했던 업그레이드 전보다 더 엉망진창이 되어 버렸습니다.
비용도 같은 양상을 보입니다. 아무도 예산에 반영하지 않았던 계약업체의 작업 시간, 분기 단위로 지연되는 로드맵 이행 일정, 그리고 고객이 요청한 기능을 개발하는 대신 일주일 내내 pip 리졸버의 출력 결과를 분석하느라 시간을 보내는 선임 엔지니어들. 이러한 사항들은 초기 견적에 전혀 반영되지 않았는데, 그 이유는 초기 견적이 단순히 버전 번호를 올리는 데만 초점을 맞췄기 때문입니다.
Werkzeug용 TuxCare ELS의 작동 방식
TuxCare는 지원이 종료된 오픈소스에 대한 패치 제공을 15년 동안 이어왔으며, 운영 체제, 런타임, 라이브러리 및 애플리케이션 전반에 걸쳐 42만 개 이상의 패치를 제공하고 1만 4,000건 이상의 취약점을 해결했습니다. Werkzeug는 TuxCare의 ‘ELS for Libraries’ 서비스를 통해 지원됩니다.
현재 지원되는 버전으로는 Werkzeug 1.0.1, 2.2.3 및 2.3.8이 있으며, 요청 시 다른 버전도 제공 가능합니다.
이 접근 방식의 차별점은 무엇인가
Werkzeug의 종속성 범위는 유난히 좁습니다. 2.x 릴리스는 필수 런타임 종속성으로 MarkupSafe 단 하나만을 명시하고 있으며, 1.0.1 릴리스는 종속성을 전혀 명시하지 않아 커버리지 문제를 간단하게 다룰 수 있습니다. ELS는 추가 비용 없이 직접 및 전이적 종속성에 대한 보안 수정 사항을 포함하고 있는데, 이는 생각보다 훨씬 중요한 사항입니다. 왜냐하면 지원 종료 위험이 사용자가 실제로 추적하고 있는 패키지보다 한 계층 아래에 위치할 수 있기 때문입니다.
패치 프로세스는 자동화 방식으로 진행되며, 담당자가 결과물을 확인합니다. TuxCare는 NVD, 벤더 공지, GitHub 보안 공지를 통해 24시간 내내 CVE를 추적하고, 전문가의 검증을 거친 알고리즘 및 AI 기반 자동화 기술을 활용해 업스트림 수정 사항을 백포트한 후, 모든 빌드를 무결성, 기능 및 회귀 테스트를 거쳐 서명하고 ELS 저장소에 게시합니다. 요약보다는 구체적인 작동 원리를 알고 싶으시다면, ‘오픈 소스 패치 프로세스’ 문서를 참고하시면 상세히 다루고 있습니다.
통합은 pip 내부에서 이루어집니다. 다음 명령어를 사용하여 pip가 TuxCare 저장소를 가리키도록 설정하거나, index-url, 또는 공개 PyPI를 계속 사용하면서 이를 extra-index-url, 패치된 빌드를 직접 고정합니다. 패치된 릴리스에는 다음과 같은 버전 문자열이 포함됩니다. werkzeug==1.0.1.post1+tuxcare, 따라서 해결된 버전이 기록된 곳이라면 어디에서든 패치된 빌드를 식별할 수 있습니다. 이는 배포 파이프라인을 재구축하는 것이 아니라, 자격 증명을 포함한 리포지토리 구성 변경입니다.
배포는 ‘최선의 노력’ 방식이 아닌 SLA(서비스 수준 계약)에 기반하며, 패키지는 TuxCare가 호스팅하는 저장소에서 서명되어 제공됩니다. 또한 사람의 개입이 필요한 경우, 문제 분류 및 패치 요청에 대해 연중무휴 24시간 지원을 제공합니다. 감사 작업을 위해 각 패치된 릴리스에는 SBOM 데이터가 포함되며, 이와 함께 VEX 피드가 제공되므로 VEX를 지원하는 도구를 통해 이미 해결된 문제와 여전히 조치가 필요한 문제를 구분할 수 있습니다.
비용 측면에서 볼 때, 연장된 지원이 저렴하다는 것이 정직한 해석은 아닙니다. 오히려 이를 통해 마이그레이션 작업, 호환성 테스트, 회귀 테스트를 긴급 대응 기간에서 계획된 기간으로 옮겨, 해당 작업에 적정한 비용이 소요되도록 한다는 점입니다.
시작하는 방법
사용 중인 버전에 대한 지원 범위가 실제로 어떻게 되는지 알고 싶다면, TuxCare CVE 트래커에서 패치된 Werkzeug 취약점을 확인할 수 있으며, ELS 설명서에서는 지원되는 버전과 설치 옵션에 대해 자세히 다루고 있습니다.
지원되는 버전, 가격 및 실제 구현 방식에 대한 정보는 ‘Werkzeug를 위한 무한 라이프사이클 지원’ 페이지를 먼저 확인하시기 바랍니다.
지원되지 않는 Werkzeug로 인해 시스템이 위험에 처하지 않도록 하십시오
Werkzeug가 Flask의 WSGI 레이어로서 차지하는 위치 때문에, 이에 대한 라이프사이클 관련 결정이 내부적으로만 처리되는 경우는 거의 없습니다. 지원되지 않는 버전에 애플리케이션이 실제로 사용하는 코드에 결함이 포함되어 있다면, 해당 애플리케이션에도 그 결함이 그대로 반영되며, 사용자는 릴리스 노트가 아닌 스캐너나 설문조사를 통해 이 사실을 알게 됩니다.
이 모든 상황에 당황할 필요는 없습니다. 여유가 있는 분기에 Werkzeug 업그레이드를 계획하면 됩니다. 스캐너 문제 외에도 실질적인 기술적 이유가 있다면 다른 프레임워크로 전환할 수도 있습니다. 또는 결정을 내리는 동안 현재 사용 중인 버전에 대한 패치를 계속 받아 적용할 수도 있습니다. 이렇게 하면 보안 상태를 유지할 수 있을 뿐만 아니라, 다음 CVE를 등록할 사람에게 로드맵을 노출시키지 않으면서도 규정 준수 심사를 위한 실질적인 수정 증거를 확보할 수 있습니다.
이 과정을 잘 헤쳐 나가는 팀들은 대개 예산이 가장 많은 팀들이 아닙니다. 이들은 자사가 실제로 어떤 버전을 사용하고 있는지 파악하고, 어떤 기능이 여전히 유지보수되고 있고 어떤 기능은 그렇지 않은지 이해한 뒤, 단순히 상황에 반응하기보다는 신중한 선택을 내린 팀들입니다.
현재 이러한 문제 중 하나라도 겪고 계신다면, ‘오픈소스 소프트웨어를 위한 무한 수명 주기 지원’을 통해, 지원 기간이 만료된 구성 요소가 포함된 나머지 스택에 대해서도 필요한 기간 동안 확장된 지원이 어떻게 제공되는지 확인해 보세요.
