Skip to content

우리는 다 파악하고 있을까 ​

PHP2Shell 취약점으로 떠들썩하던 날, 네이버 뉴스에는 온통 PHP 이야기만 가득했지만 마침 개발자 커뮤니티에 "Node.js도 버전 올려야 해요"라는 글이 올라왔다.

알고 보니 Node.js에서도 10월 보안 릴리즈를 발표했는데, 보안 연구원이 원격 코드 실행(RCE)이 가능한 브로틀리(Brotli) 취약점에 'Node2Shell'이라는 별칭을 붙여두었다.

영향 대상이 되는 조건에 맞는지를 고려하기보다 일단 패치된 버전으로 올려두는 게 안전하겠다 싶어, 사내에 개발자 PC의 로컬 노드 버전부터 업데이트하자고 공유했다. 그러자 "우리 서비스 제품은 노드로 돌아가지도 않고 영향 대상도 아닌데 굳이 번거롭게 왜 올려야 하냐" 는 반응이 돌아왔다.

그 되물음을 듣다 보니 문득 한 가지 의문이 들었다. 과연 우리는 다 파악하고 있을까? 우리가 만든 제품과 사내 도구들이 어떤 언어와 라이브러리에 의존하고 있는지 말이다.

사내에서 사용하는 서드파티 ​

그동안 우리가 직접 개발하고 고객에게 제공하는 제품만 신경 썼지만, 돌아보니 사내에서 업무와 운영을 위해 사용하는 서드파티 제품도 정말 많았다. 그리고 그 도구들의 의존성 실체는 겉보기보다 훨씬 복잡하게 얽혀 있었다.

Uptime Kuma ​

제품들의 상태 모니터링을 위해 사내에 도입해 둔 오픈소스 도구다. 평소 "우리 조직은 노드 기반 제품이 없다" 고 생각해 처음엔 취약점 영향 대상으로 떠올리지도 않았다.

그런데 뒤늦게 확인해 보니 이 도구가 Node.js 위에서 돌아가고 있었다. 아차 싶어 AI 에이전트에게 내부 코드 분석을 맡겼는데, 다행히 HTTP 통신에 axios를 쓰면서도 브로틀리 압축 해제는 수행하지 않아 영향 대상이 아니라는 결과를 받았다.

보이지 않는 런타임

매일 쓰고 있는 도구조차 어떤 런타임 위에서 돌아가는지 전혀 모르고 있었던 셈이에요.

GlitchTip ​

최근 Sentry를 대체하기 위해 도입한 에러 트래킹용 오픈소스 도구다. 이번 이슈가 'Node.js 취약점'으로 알려졌던 데다, 에이전트에게 물어보니 GlitchTip은 파이썬으로 구성되었다고 해서 처음엔 당연히 무관할 거라 생각하고 넘어갔다.

하지만 글을 쓰며 AI 에이전트와 대화를 나누다 보니 취약점의 실체는 전혀 달랐다. 근본 원인은 구글의 Brotli C 라이브러리 결함(GHSA-9v8h-qqgm-4m35)이었고, 이를 바인딩해 쓰는 파이썬 생태계와 GlitchTip의 Brotli 1.2.0 역시 영향권이었다.

처음에는 에이전트조차 GlitchTip이 압축 해제를 직접 호출하지 않아 안전하다고 했지만, 자세히 뜯어보니 상황이 달랐다. 외부 통신 라이브러리인 aiohttp(ssrf_protection.py)가 브로틀리가 설치된 환경에서는 기본적으로 응답 압축을 풀도록 되어 있었고, 그 과정에서 취약한 라이브러리를 그대로 쓰고 있었던 것이다.

겉으로 드러난 메인 언어가 다르거나 일반적인 AI 에이전트 분석만 믿고 넘어가서는 안 되는 이유다. 취약점이 처음 알려진 특정 런타임에만 시선을 빼앗기다 보면, 보이지 않는 곳에서 그 라이브러리를 바인딩해 호출하는 다른 제품들의 진짜 위험을 완전히 놓쳐버리게 된다.

Node.js 버전 업데이트 하세요

보안 소식이 터지면 실행 경로나 호출 여부도 따지지 않은 채, "당장 올리라니까 일단 올리고 보는" 커뮤니티의 맹목적인 버전 업데이트 행태도 다시 돌아보게 돼요.

미처 챙기지 못하는 사각지대 ​

현재 우리 조직은 PHP나 Node.js로 직접 웹 요청을 받는 서버 제품을 운영하지 않는다. 그러니 얼핏 보면 "우리 제품은 해당 없다"고 선을 긋기 쉽다. 하지만 개발과 운영 환경을 조금만 넓혀보면 생각지 못한 사각지대가 드러난다.

Vite 개발 서버 ​

"굳이 왜 로컬 노드 버전을 패치해야 하느냐"고 되물었던 바로 그 개발자는, 평소 개발 중인 화면을 동료나 외부에 보여주기 위해 Vite 개발 서버(--host)를 띄우곤 한다.

공식적으로는 로컬 개발이나 빌드용 도구라고 단정해서 바라볼지도 모른다. 하지만 외부 접근 포트가 열린 이상, 그 순간 개발자 PC도 Node.js 기반의 웹 서버로 동작하는 셈이다.

물론 공격 대상이 될 가능성은 적더라도, 인터넷이나 외부망에 공개되어 접근할 수 있는 상태라면 언제든 위험에 노출되는 상황인 것은 분명하다. 로컬 환경이 뚫리면 PC 안에 보관된 소스 코드와 환경 변수(.env), 사내 자격 증명이 고스란히 유출될 수 있다.

고객 환경의 서드파티 ​

우리가 제공하는 제품이 PHP가 아니라고 해서 완전히 무관한 일로 끝나는 것도 아니다. 실제로 고객 문의에 따라 "우리 제품은 PHP를 사용하지 않는다" 라고 답변했지만, 뒤늦게 해당 고객의 인프라 구성상 웹 프론트엔드 화면을 위해 PHP를 사용 중인 Zabbix가 함께 돌아가고 있다는 사실을 알게 된 적이 있다.

물론 해당 인프라는 고객이 직접 구성하고 도입한 관제 솔루션이므로 우리 제품 팀이 직접 대응해 주어야 할 영역은 아니었지만, 관련 보안 공지를 참고 정보로 전달했다.

통합 로그인(Keycloak)과 자바 런타임의 맹점 ​

서비스 제품들의 통합 로그인을 위해 사내에서 Keycloak을 사용 중인데, 최근 비밀번호 재설정 절차 우회로 계정을 탈취할 수 있는 심각한 취약점(GHSA-4gv3-mc9p-5wqc)이 보고되었다.

전담 보안팀이 없는 조직에서는 도입해 쓰고 있는 인프라 제품의 취약점 소식을 제때 공유받기 어렵다. 게다가 IT 뉴스 헤드라인은 온통 PHP2Shell 이야기뿐이다 보니, 정작 자바 생태계에서 터진 OpenJDK 중간 CA 검증 취약점 같은 심각한 런타임 문제는 개발자 커뮤니티에서 누군가 버전을 올려야 한다고 공유해 주지 않았다면 전혀 모르고 지나쳤을 것이다.

인터넷에서 화제가 된 자극적인 취약점에만 시선을 뺏기는 사이, 정작 서비스의 관문을 쥐고 있는 솔루션이나 밑바닥을 받치는 런타임의 취약점은 사내에서 인지조차 못한 채 조용히 지나쳐 버린다.

나조차도 모르고 지나친다 ​

돌아보면 지금 이 글을 쓰고 있는 나조차도 보안 뉴스를 보고 사내에 공유하긴 하지만, 정작 모르고 넘어가는 취약점이 훨씬 많다는 고백이기도 하다.

내가 개발 중인 제품이 정확히 어떤 언어 버전과 전이 의존성을 타고 올라가는지 평소엔 굳이 들여다보지 않게 된다. 솔직히 말해 고객사에서 "이번 취약점에 우리 서비스도 영향이 있나요?" 라고 먼저 물어오지 않으면 능동적으로 살펴볼 생각조차 하지 않는 게 현실이다.

이렇게 평소에 전혀 신경 쓰지 않고 있다가 막상 등 떠밀려 버전을 올리려고 하면 사실상 불가능에 가깝다. 이미 버전 격차가 너무 크게 벌어져 있어서, 간단한 보안 패치 하나조차 감당하기 힘든 대규모 마이그레이션 작업이 되어버리기 때문이다. 이러한 보안 문제가 발생할 때마다 우리는 왜 미리 버전을 올려두지 않았을까 뒤늦게 후회하지만, 박명수가 남긴 "늦었다고 생각할 때가 진짜 너무 늦었다"는 명언이 있다.

늦었다고 생각할 때가 진짜 너무 늦었다

그래서 기술 스택을 다 파악했나요? — 아니요.

참고 자료 ​

Released under the MIT License.