“홈페이지는 멀쩡한데 PHP를 급하게 바꿔야 한다고요?”
보안 공지를 받은 운영자에게는 낯설지 않은 장면입니다. 로그인도 되고 주문도 들어옵니다. 그런데 화면을 만드는 프로그램보다 더 아래, 그 프로그램을 실행하는 엔진에 결함이 있다는 소식이 들립니다. 얼마 지나지 않아 Node2Shell이라는 이름까지 등장합니다. 오래된 PHP가 문제라더니, 비교적 최근의 Node.js도 예외가 아니었던 걸까요?
2026년 10월 21일 PHP2Shell 수정본이 공개됐고, 같은 날 Node.js도 Brotli 압축 해제기의 치명적인 결함을 수정했습니다. 서로 전염된 취약점은 아닙니다. 다만 두 사건은 같은 질문을 남깁니다. 오랫동안 사용한 코드의 문제가 왜 지금 큰 보안 이슈가 됐을까요? AI가 위험을 만들었을까요, 이미 있던 위험을 더 잘 찾게 됐을까요? PHP 보안 릴리스, Node.js 보안 릴리스
답의 출발점은 이미 존재하던 결함입니다. 그러나 코드의 실수만 따라가면 중요한 절반을 놓칩니다. 이상을 알아챈 뒤에도 수정이 늦어진 이유, AI와 사람이 함께 공격 가능성을 입증한 과정, 수정본이 나와도 서버에는 구버전이 남는 문제가 이어집니다. PHP의 시작부터 보면 이 연결이 조금 더 선명해집니다.
PHP는 어떻게 개인 홈페이지에서 서비스의 기반이 됐을까
PHP의 출발은 거창한 웹 플랫폼이 아니었습니다. 1994년 Rasmus Lerdorf가 자신의 온라인 이력서에 누가 방문하는지 확인하려고 만든 도구였습니다. 처음 이름도 Personal Home Page Tools였습니다. C로 작성한 작은 CGI 프로그램 모음이었고, 1995년 소스가 공개된 뒤 폼 입력과 데이터베이스를 다루는 기능을 갖춰 갔습니다. PHP 공식 역사
여기서 정적인 페이지와 웹 서비스의 차이를 생각해 보면 쉽습니다. 미리 저장한 안내문을 보여 주는 데서 그치지 않고, 방문자가 보낸 이름을 읽고 주문을 저장한 뒤 각자 다른 결과를 돌려주는 일이 필요해졌습니다. PHP는 서버에서 이런 작업을 실행하고 결과를 응답하는 데 쓰입니다. 화면에 보이는 HTML과, 그 화면을 준비한 서버의 실행 환경은 서로 다른 층입니다.
1997년에는 Andi Gutmans와 Zeev Suraski가 전자상거래 프로젝트에 필요한 기능을 만들다가 기존 파서를 다시 쓰기 시작했습니다. 1998년 나온 PHP 3에서는 확장 기능과 여러 데이터베이스의 연결이 중요한 강점이 됐습니다. 개인용 도구에서 출발했지만, 다른 소프트웨어와 결합할 수 있는 기반으로 성장한 것입니다. PHP 3와 확장 구조의 역사
이 역사는 “처음부터 작게 만들었으니 지금도 허술하다”는 결론을 뒷받침하지 않습니다. 여러 차례 재작성과 개선이 있었습니다. 현재 문제를 이해하는 데 필요한 대목은 PHP 코드 아래에 입력 처리·메모리 관리·확장 기능을 담당하는 구현이 있고, 수많은 서비스가 그 동작에 의존한다는 점입니다. 화면의 PHP 코드를 꼼꼼하게 작성했어도, 엔진 내부의 메모리 오류까지 자동으로 사라지지는 않습니다. 이번 결함의 위치도 그 아래였습니다.
PHP2Shell은 왜 로그인보다 앞에서 문제가 될까
PHP는 웹 애플리케이션의 코드를 서버에서 실행합니다. 사용자가 요청을 보내면 PHP가 입력을 준비하고, 애플리케이션을 실행하고, 요청에 사용한 메모리를 정리합니다. PHP2Shell의 두 취약점은 이 준비와 정리의 경계에서 발생했습니다.
CVE-2026-109618은 메모리가 부족해 요청 처리를 중단하는 과정의 문제입니다. 내부 자료가 완전히 준비되지 않은 순간에 처리가 끊기면, 뒷정리 과정이 유효하지 않은 메모리를 정상 자료처럼 사용할 수 있습니다. 공식 분석은 Apache의 mod_php에서 애플리케이션 코드가 시작되기 전에도 공격이 가능한 경로를 설명합니다. 따라서 사이트의 로그인 검사나 플러그인의 입력 검증만으로 막을 수 있다고 가정해서는 안 됩니다. PHP의 메모리 부족 처리 취약점 분석
CVE-2026-109619는 앞선 요청의 메모리를 해제했는데도, 처리 프로세스 안에 그 메모리를 가리키는 상태가 남는 문제입니다. 같은 프로세스가 다음 요청을 받으면서 오래된 상태를 다시 사용할 수 있습니다. 메모리의 수명은 끝났는데 이를 가리키는 참조는 살아 있는, 해제 후 사용 문제입니다. PHP의 전역 상태 취약점 분석
그림에서 참조는 데이터 자체가 아니라, 그 데이터가 있던 곳을 가리키는 정보입니다. 메모리 해제는 그 공간의 사용을 끝내는 것이며 내용을 반드시 모두 지운다는 뜻은 아닙니다. 문제는 수명이 끝난 공간을 유효한 데이터가 있는 곳처럼 다시 사용할 수 있다는 데 있습니다.
이 설명에서 한 걸음 더 주의할 점이 있습니다. mod_php의 공격 경로가 특히 분명하다는 사실을 “PHP-FPM은 안전하다”로 바꿔 읽으면 안 됩니다. PHP 팀은 PHP-FPM, FrankenPHP, LiteSpeed 등 웹 요청을 처리하는 설치 환경을 업데이트 대상으로 안내했습니다. 구현에 따라 공격 조건과 검증 수준이 다르다는 점은 우선순위 판단에 필요하지만, 다른 환경의 면책 근거는 아닙니다.
원격 코드 실행도 정확히 이해해야 합니다. 서버의 웹 작업 계정 권한으로 공격자가 코드를 실행할 수 있다는 의미이지, 모든 서버에서 최고 관리자 권한 획득과 데이터베이스 전체 유출이 자동으로 입증됐다는 뜻은 아닙니다. 다만 그 작업 계정이 어떤 파일과 비밀정보에 접근할 수 있는지에 따라 영향이 커질 수 있으므로, “관리자 권한은 아니니 괜찮다”는 판단 역시 충분하지 않습니다.
징후는 2024년에 있었는데, 왜 수정이 늦어졌을까
메모리가 부족하면 요청을 중단한다는 동작 자체는 이상하지 않습니다. 위험은 중단 순간에 자료가 어떤 상태로 남는지에 있습니다. 이처럼 정상 동작과 오류 처리가 만나는 구간은, 로그인 성공 화면만 반복해서 확인해서는 살펴보기 어렵습니다.
더 뜻밖인 것은 전혀 몰랐던 문제만은 아니었다는 사실입니다. PHP Foundation에 따르면 2024년 외부 보안 감사의 지적 사항을 고치는 과정에서 핵심 개발자가 이번 메모리 부족 문제의 징후를 발견했습니다. 당시에는 공격 가능성에 대한 가설은 있었지만 구체적인 원격 코드 실행 경로를 찾지 못했습니다. PHP Foundation의 경위 설명
수정안도 하나가 아니었습니다. 어떤 방법은 기존 프로그램과의 호환성을 깨뜨리고, 다른 방법은 새로운 서비스 거부 위험을 만들거나 많은 외부 확장의 변경을 요구했습니다. 엔진을 깊이 이해하는 소수 인력에게 여러 업무가 몰린 상태에서 합의 중심의 의사결정까지 지연됐습니다. 재단은 이를 의사결정 과정과 자원 부족에서 비롯된 구조적 실패라고 스스로 평가했습니다. 다수결과 절충을 통해 결정을 내릴 수 있도록 절차도 바꾸기 시작했다고 밝혔습니다. 재단이 설명한 수정 지연과 개선
그러니 “오픈소스라서 누군가 알아서 고칠 것”이라는 기대에는 빠진 비용이 있습니다. 발견한 문제의 영향도를 판정하고, 기존 동작을 어디까지 바꿀지 결정하고, 여러 환경에서 검증해 배포하는 사람의 일입니다. 이 사건에는 코드 결함뿐 아니라 그 일을 제때 끝내지 못한 조직적 원인도 있었습니다. 다만 공개 기록만으로 특정 개발자의 고의나 태만을 단정하는 것은 다른 이야기입니다.
AI가 바꾼 것은 ‘수상하다’에서 ‘공격된다’까지의 거리
2026년 여름, 보안 연구팀 Calif는 이 문제를 다시 파고들었습니다. 연구자의 설명에서 흥미로운 부분은 최신 도구보다 오래된 기억입니다. 과거 PHP의 메모리 제한 관련 취약점과 오류 보고를 알고 있었고, 비슷한 위험이 남아 있을 것이라는 가설로 AI에게 질문했습니다. 과거의 다른 취약점을 알고 있었다는 것과, 2004년의 같은 버그가 그대로 방치됐다는 것은 다릅니다. Calif의 연구 배경과 발견 과정
Calif는 7월 30일 감사를 시작한 뒤 8월 초 원격 코드 실행 경로를 찾았고, 8월 11일 첫 보고를 제출했습니다. 이어 재현 코드를 공유하고 PHP 팀의 확인을 받았습니다. 보고서의 무게가 “어쩌면 위험하다”에서 “이 조건에서 실제 코드 실행으로 이어진다”로 달라진 셈입니다. 최초 발견에 특별한 비공개 모델이나 전용 도구가 필수였던 것도 아니라는 설명입니다. 연구자는 대상의 취약점 이력을 알고 질문 방향을 정하는 역할을 강조했습니다. Calif의 보고·검증 일정
Wiz 역시 독립적으로 문제를 연구하고 보고했습니다. Wiz의 기록은 버전·빌드·애플리케이션에 따라 메모리 배치가 달라지고, 실제 공격을 구성하려면 여러 단계를 연결하고 조정해야 했음을 설명합니다. AI의 도움을 받았다는 말이 입력 한 줄로 모든 PHP 서버를 공격할 수 있다는 뜻은 아닙니다. 의심할 지점을 찾고, 가설을 반복해서 검토하고, 실제로 이어지는 경로를 확인하는 연구에 AI가 들어온 것입니다. Wiz의 PHP2Shell 분석
이것이 “AI가 PHP를 망가뜨렸다”보다 근거에 맞는 설명입니다. 결함은 이미 코드에 있었고, AI를 활용한 연구가 그 영향과 공격 경로를 더 분명하게 드러냈습니다. 다만 AI를 쓰지 않았다면 정확히 몇 달이나 몇 년 더 걸렸을지는 이 기록으로 계산할 수 없습니다. 발견을 도왔다는 사실과 발견 속도의 인과 효과를 수치로 증명했다는 주장은 구별해야 합니다.
여기까지 듣고 “그렇다면 오래된 PHP를 떠나면 되겠네”라고 생각할 수도 있습니다. 바로 그 대목에서 Node2Shell이 이야기를 바꿉니다.
Node2Shell은 왜 JavaScript 바깥에서 시작됐을까
Node2Shell은 PHP2Shell의 후속 공격이나 변종이 아닙니다. Red Hat도 이 이름을 Brotli 압축 해제기의 CVE-2026-111497에 연결해 안내합니다. Brotli는 데이터를 작게 전송할 때 사용하는 압축 형식입니다. Node.js 애플리케이션도 외부에서 받은 압축 응답이나 파일을 처리하면서 이 해제기에 도달할 수 있습니다. Red Hat의 Node2Shell 권고
원인은 의외로 작은 판단 차이였습니다. 해제기가 잘못된 블록 전환을 만났을 때 “이 형식은 오류이니 그만 처리하라”고 해야 하는데, “아직 데이터가 부족하니 더 달라”는 상태로 보고했습니다. 스트리밍 해제기는 그 결과 입력을 내부의 8바이트 버퍼에 계속 추가했고, 정해진 범위를 넘어 쓰는 문제가 생겼습니다. 오류와 대기를 혼동한 것이 메모리 손상으로 이어진 것입니다. Node.js의 원인 분석
작은 접수함에 규격이 맞지 않는 서류가 들어왔는데 반려하는 대신 다음 장을 계속 받는 상황에 비유할 수 있습니다. 다만 실제 결함은 압축 파일이 너무 크게 풀린다는 일반적인 ‘압축 폭탄’ 문제가 아닙니다. 잘못된 입력을 만났을 때 해제기의 상태와 쓰기 경계를 올바르게 관리하지 못한 문제입니다.
PHP에서는 요청 중단 뒤의 메모리 상태가, Brotli에서는 압축 입력을 처리하는 상태 전환이 문제가 됐습니다. 프로그램의 표면 언어는 달라도 그 아래 구현의 오류가 서비스까지 올라올 수 있다는 공통점이 있습니다. Node.js를 쓴다는 이유만으로 포함된 네이티브 라이브러리의 메모리 안전성까지 보장되는 것은 아닙니다.
가령 가상의 주문 서비스가 외부 문서를 가져와 발주 내역을 읽는다고 하겠습니다. 사용자가 지정한 주소의 응답을 받아 압축을 풀고 내용을 추출한다면, 그 기능에는 외부 입력을 처리하는 보안 경계가 있습니다. 반면 화면용 파일을 압축해 내보내는 작업을 같은 공격 경로로 취급하면 판단이 흐려집니다. 중요한 것은 누가 만든 데이터를, 어느 구현으로 푸는가입니다.
공격자가 제공한 입력이 해당 해제 경로에 닿으면 프로세스 중단·메모리 노출·원격 코드 실행으로 이어질 수 있지만, 모든 비정상 종료가 코드 실행의 증거인 것은 아닙니다. Node.js 공식 권고는 보고자를 OpenAI Codex Security 팀으로 명시합니다. 여기서도 확인되는 것은 AI 보안 연구와 보고의 역할이며, AI가 원래 결함을 작성했다는 근거는 없습니다. Node.js 보안 권고
10월 21일 수정 릴리스는 22.23.4·24.22.1·26.12.1입니다. PHP 이슈에 관심이 쏠린 동안에도 지원 중인 Node.js 계열의 패치는 별도로 필요했습니다. 프로젝트 패키지의 npm audit 결과만으로 실행 중인 Node.js 바이너리까지 고쳐졌다고 볼 수도 없습니다. PHP 플러그인만 바꾸고 PHP 엔진은 남겨 두는 실수와 비슷합니다. 22 릴리스, 24 릴리스, 26 릴리스
AI의 책임과 사람의 책임은 어디에서 갈릴까
두 사건을 읽을 때는 세 가지 질문을 나눌 필요가 있습니다. 결함이 코드에 들어간 원인은 무엇인지, 누가 어떤 방법으로 발견했는지, 발견 뒤 수정과 적용이 왜 늦어졌는지입니다. 서로 다른 단계의 책임을 한데 묶으면 “AI 때문”이나 “낡은 언어 때문”이라는 쉬운 답만 남습니다.
이번에 확인한 PHP와 Brotli의 직접 원인은 각각 메모리·오류 처리와 해제기의 상태 전환입니다. AI가 작성한 코드에서 생긴 결함이라고 단정할 자료는 없습니다. 발견에는 AI를 활용한 연구가 기여했습니다. PHP의 수정 지연에는 재단이 인정한 의사결정과 자원 부족도 있었습니다. 기술적 결함, 발견 도구, 조직의 대응은 함께 작용하지만 같은 원인은 아닙니다.
AI가 할 일이 늘어났다고 검증할 사람이 필요 없어지는 것도 아닙니다. Mozilla는 Anthropic과의 Firefox 보안 연구에서 22개의 CVE를 확인했고, 그중 14개를 높은 심각도로 분류했다고 밝혔습니다. 작은 재현 테스트가 엔지니어의 확인에 도움이 됐다는 설명과 함께, AI가 만든 오탐 보고가 유지관리자의 부담이 될 수 있다는 문제도 짚었습니다. 보고가 많아진 것과 실제로 고친 문제가 많아진 것은 구분해야 합니다. Mozilla의 검증 결과
운영자가 받아야 할 것도 그럴듯한 경고 목록보다 영향받는 버전, 발생 조건, 수정 근거와 검증 가능한 결과입니다. AI에게 저장소를 읽히는 것은 검토의 한 방법이 될 수 있지만, 그 결과만으로 위험 판정과 패치 검증을 끝내기는 어렵습니다.
따라서 오래됐다는 사실만으로 위험 순위를 정할 수 없습니다. 새 계열에서도 결함은 발견됩니다. 다만 오래 손대지 않은 시스템에 지원 종료, 담당자 부재, 테스트와 배포 절차의 부재까지 겹치면 발견 이후의 대응이 어려워집니다. PHP 밖의 오래된 업무 시스템도 이 관점에서 살펴볼 필요가 있습니다.
JSP 서비스도 화면 아래의 실행 환경을 봐야 한다
“우리는 PHP가 아니라 JSP라서 관계없다”는 답은 이번 PHP 취약점의 직접 대상인지 구분하는 데에는 맞습니다. 그러나 보안 유지보수까지 끝났다는 답은 아닙니다. JSP는 Java 기반 서버 페이지 기술입니다. 이를 사용하는 서비스는 Tomcat 같은 실행 컨테이너, Java 실행 환경인 JDK, 프레임워크와 라이브러리를 함께 살펴봐야 합니다. Apache도 Tomcat 계열마다 지원하는 Servlet·Pages 규격과 Java 버전을 구분해 안내합니다. Tomcat 버전과 지원 규격
실제 사례로 Tomcat의 CVE-2025-24813을 보겠습니다. 2025년에 공개된 이 결함은 Default Servlet의 쓰기 허용, 부분 PUT 요청 지원, 특정 파일 기반 세션 저장과 역직렬화에 이용될 수 있는 라이브러리 등의 조건이 겹칠 때 원격 코드 실행으로 이어질 수 있었습니다. 쓰기는 기본적으로 꺼져 있으므로 “JSP 사이트는 모두 같은 방식으로 뚫린다”는 설명은 틀립니다. 반대로 JSP 파일을 수정하지 않았다는 이유로 실행 컨테이너의 취약점이 사라지지도 않습니다. Apache의 해당 취약점 설명
이 문제는 CISA의 실제 악용 취약점 목록인 KEV에도 올라 있습니다. 단순히 가능성만 적힌 코드 결함과, 실제 악용이 확인돼 대응 우선순위가 높아진 결함을 구분할 때 참고할 자료입니다. 다만 KEV에 없다는 사실이 안전하다는 뜻은 아닙니다. CISA KEV 공식 데이터
최근 사례도 있습니다. Apache는 2026년 10월 20일 CVE-2026-101296을 공개했습니다. 특정 비GSSAPI SASL 인증 방식으로 구성된 JNDIRealm에서 올바른 비밀번호 없이 인증될 수 있는 문제이며, Tomcat 9 계열에서는 9.0.123에 수정이 포함됐습니다. 이 역시 모든 JSP 로그인에 적용되는 주장이 아니라, 해당 인증 구성을 쓰는 환경의 문제입니다. Tomcat 9 보안 권고
여기서 2025년 결함을 고친 9.0.99를 현재의 업데이트 목표로 삼으면 안 됩니다. 그 뒤의 보안 수정이 있기 때문입니다. 특정 CVE의 최초 수정 버전과 지금 유지해야 할 지원 패키지는 다른 질문입니다. 이 Tomcat 사례들이 AI가 발견한 결함이라고 말할 근거도 없습니다. PHP와 다른 기술에서도 실행 환경의 보안 유지가 필요하다는 사례로 읽어야 합니다.
오래된 기술이라는 이름보다 더 구체적인 위험 신호는 지원 종료입니다. 예를 들어 Tomcat 8.5 계열의 Apache 지원은 2024년 3월 31일 종료됐습니다. 공식 수정을 받을 경로가 없는 상태와, 지원 계열에서 보안 수정을 계속 적용하는 상태를 같은 “오래된 Java 서비스”로 묶을 수는 없습니다. 별도 공급자 지원을 이용한다면 그 범위와 실제 제공 패키지를 확인해야 합니다. Apache의 지원 종료 계열 안내
PHP 업데이트, 처음 수정한 버전에서 끝내면 안 되는 이유
이제 이 이야기를 내 서버의 작업으로 바꿔 보겠습니다. PHP2Shell 수정이 처음 포함된 버전은 PHP 8.2.35·8.3.36·8.4.27·8.5.12입니다. 해당 계열에서 수정이 들어간 패키지로 올리는 것이 출발점입니다. PHP 8.1 이하처럼 PHP 프로젝트의 지원이 끝난 계열은 배포판이나 장기 지원 공급자의 백포트 여부를 확인하거나, 지원 계열로 옮겨야 합니다. 백포트는 보안 수정을 기존 버전에 되돌려 적용하는 방식입니다. PHP 업데이트 안내
여기서 버전 숫자는 읽는 방법이 중요합니다. Linux 배포판이 기존 버전 문자열을 유지하면서 수정을 적용할 수 있으므로, 앞자리 숫자만 보고 취약하다고 확정하면 안 됩니다. 반대로 “우리 공급자가 알아서 했을 것”이라는 추측도 수정 증거가 아닙니다. 설치된 패키지의 전체 버전과 해당 CVE 수정 내역이 서로 연결돼야 합니다.
첫 번째 후속 확인은 세션 업로드 진행 상태입니다. PHP 팀은 10월 23일, 특정 세션 직렬화 설정과 실행 중 설정 변경이 결합되는 드문 경로를 별도로 안내했습니다. php_binary 또는 php_serialize로 시작한 뒤 코드에서 php로 바꾸고 세션을 시작하며 업로드 진행 상태 기능도 켜져 있는 조합입니다. 다음 정기 수정 전까지 session.upload_progress.enabled=0을 권고했으며, 이 경로는 CVE-2026-115560으로 안내됐습니다. 처음의 두 취약점을 고쳤다는 이유로 이 추가 조건을 놓치면 안 됩니다. 세션 업로드 진행 상태 추가 권고
두 번째는 Windows 패키지의 재배포 여부입니다. 10월 26일 공개된 PHP 8.2.35-1·8.3.36-1·8.4.27-1·8.5.12-1에는 Brotli 1.2.1이 포함됐습니다. PHP 런타임 버전은 같지만 의존성을 바꾼 패키지입니다. cURL 확장의 HTTP 응답 자동 압축 해제가 활성화되면 관련 해제기에 도달할 수 있다는 설명도 있습니다. 그래서 php -v의 버전 출력만으로 이 재배포까지 적용됐다고 판정하기 어렵습니다. Windows 빌드 재배포 공지
이 두 사례가 보여 주는 것은 “업데이트가 소용없다”가 아닙니다. 패치 적용은 한 번의 설치 버튼으로 닫는 작업이 아니라, 수정 공지의 후속 내용과 실제 배포 파일까지 확인하는 과정이라는 점입니다. 처음 수정이 들어간 버전은 이번 결함의 기준점이며, 설치할 때는 그 이후의 공식 보안 릴리스도 함께 확인해야 합니다.
패치 공개와 내 서버 적용 사이의 빈틈
보안 수정본이 나왔다는 소식은 반갑지만, 공개 자체가 모든 운영 서버를 고쳐 주지는 않습니다. 공급자가 수정한 시점과 우리 서비스가 그 수정본으로 실행되는 시점 사이에는 공백이 생깁니다. 이미 알려진 취약점을 아직 고치지 않은 환경에서 이용하는 공격을 보통 N-day 공격이라고 부릅니다.
공격자는 수정 전후의 코드나 실행 파일을 비교해 무엇이 바뀌었는지 분석할 수도 있습니다. 좋은 소식인 패치가 동시에 결함의 위치를 좁혀 주는 단서가 되는 셈입니다. Anthropic은 6월 연구에서 공개된 보안 패치의 변경 내용을 바탕으로 모델이 취약점을 재현하고 악용 가능성을 찾는 실험을 공개했습니다. 결함을 새로 찾는 능력뿐 아니라, 공개된 수정의 의미를 분석하는 능력도 운영자의 대응 시간과 관련됩니다. N-day 패치 분석 연구
다만 그 연구는 정해진 빌드와 도구, 검증 환경을 사용한 실험입니다. 독립 실행형 JavaScript 엔진에서의 검증을 브라우저 전체 공격과 같게 볼 수 없고, 프로세스를 비정상 종료시키는 재현 코드와 실제 코드 실행도 구별해야 합니다. 실험에서 제시한 시간이나 성공 사례를 “우리 회사 서버가 몇 분 뒤 뚫릴 확률”로 환산해서는 안 됩니다. 그 한계를 감안해도, 공개된 패치를 분석하는 작업까지 자동화될 수 있다는 방향은 패치 적용 지연을 줄일 이유가 됩니다.
PHP에서도 공지 이후만 볼 수는 없습니다. AWS는 MadPot 관측 자료에서 10월 17일의 PHP2Shell 공격 시도를 확인했다고 밝혔습니다. 공개된 보안 릴리스보다 나흘 앞선 시점입니다. 여기서 중요한 단어는 ‘시도’입니다. 이 관찰을 모든 시도가 성공했다거나 모든 PHP 서비스가 침해됐다는 주장으로 확대할 수는 없습니다. AWS는 인터넷에 노출된 치명적 취약점의 대응 시간을 일률적인 7일·30일 창구에만 맞추지 말라고도 권고했습니다. AWS의 관찰과 운영 권고
그렇다고 AI 등장 전에는 보안 수정을 느긋하게 미뤄도 됐던 것은 아닙니다. Mandiant의 2023년 취약점 악용 분석에서도 악용까지의 시간이 짧아지는 양상이 관찰됐습니다. 이 과거 통계가 AI의 인과적 효과를 측정한 자료는 아닙니다. 이미 필요했던 신속한 대응을, AI의 분석 능력까지 고려해 더 반복 가능하게 만드는 일이 현재 운영자가 가져갈 결론에 가깝습니다. Mandiant의 2023년 악용 시간 분석
정기 업데이트와 긴급 대응을 따로 설계하기
그렇다면 무조건 자주 바꾸기만 하면 될까요? 실무에서는 업데이트로 로그인, 파일 업로드, 외부 연동이 깨질 가능성도 관리해야 합니다. 모든 변경을 한꺼번에 밀어 넣으면 어느 변경이 문제인지 찾기 어렵고, 반대로 회귀 테스트가 없다는 이유로 아무것도 바꾸지 않으면 패치 공백이 쌓입니다.
출발점은 업데이트를 별도 기능 개발의 덤으로 취급하지 않는 것입니다. NIST의 기업 패치 관리 지침은 패치 관리를 식별, 우선순위 결정, 확보, 설치, 설치 확인까지 포함하는 예방 정비로 설명합니다. 새 기능이 없어도 운영 비용으로 계획해야 한다는 관점입니다. NIST SP 800-40 Rev. 4
작은 팀이라면 다음과 같은 리듬을 운영 예시로 삼을 수 있습니다. 공식 보안 알림은 상시 받아 담당자를 지정하고, 주간 검토에서 새 공지와 자신의 자산 목록을 대조합니다. 정기 유지보수 창구에는 검토한 보안 패치와 지원 패키지 갱신을 모으고, 실제 악용이나 인터넷 노출과 관련된 치명적 결함은 그 창구를 기다리지 않는 별도 긴급 절차로 처리합니다. 주간 검토나 정기 창구는 이 글의 운영 예시이며, 모든 서비스에 통용되는 보편적 시한은 아닙니다.
우선순위를 정할 때는 심각도 숫자 하나보다 ‘우리 버전과 설정이 해당하는가, 외부 입력이 닿는가, 실제 악용 정보가 있는가, 노출되면 어떤 업무와 정보가 영향을 받는가’를 함께 봅니다. 내부망 서비스도 연동 경로와 접근 주체를 확인해야 하지만, 인터넷에서 인증 없이 닿는 경로와 아무 구분 없이 같은 일정에 넣는 것도 적절하지 않습니다.
| 구분 | 역할 | 설명 | 예시 |
|---|---|---|---|
| 긴급 보안 수정 | 정기 창구보다 먼저 판단 | 실제 악용·외부 노출·영향 조건을 확인하고 담당자가 즉시 조치 순서를 정합니다. 필요한 임시 통제와 수정 적용을 함께 추적합니다. | 웹 요청을 처리하는 취약 PHP 환경의 PHP2Shell 대응 |
| 정기 보안 유지 | 검토와 배포를 반복 | 지원 계열의 수정 내역을 주기적으로 확인하고 검증한 패키지를 배포합니다. 완료 여부는 실제 실행 환경으로 확인합니다. | Node.js 24 보안 릴리스와 JDK·컨테이너 갱신 검토 |
| 메이저 버전 이전 | 호환성과 복구를 검토 | 프레임워크·라이브러리·설정 변화와 주요 업무의 회귀 결과를 확인합니다. 이전 준비가 현재 계열의 보안 수정을 막지 않게 합니다. | Node.js 24→26 또는 Tomcat 계열 전환 |
“패치가 없는데 지금 당장 옮길 수도 없다”면 접근 제한이나 영향받는 기능의 임시 중지, 격리처럼 노출을 줄일 대책을 공급자 지침과 함께 검토해야 합니다. 그 대책에는 담당자, 재평가 시점, 해제 조건도 붙여야 합니다. 임시 조치를 적용했다는 기록만 남고 실제 수정은 기한 없이 사라지는 상태를 피하기 위해서입니다.
Reddit의 관리자 패치 공유 글에서도 수정 공지와 임시 완화책이 함께 공유됐습니다. 이런 커뮤니티는 놓친 공지를 발견하는 데 유용하지만, 댓글의 설정 한 줄보다 공식 권고의 전체 조건을 확인해야 합니다.
특히 PHP의 초기 완화책인 memory_limit=-1 같은 설정은 맥락 없이 복사하면 안 됩니다. 공식 권고도 완전한 해결은 업데이트라고 밝힙니다. 공격 경로 일부를 줄이는 설정과 메모리 고갈·서비스 중단의 운영 위험을 함께 봐야 합니다. 오류 출력을 숨기는 설정도 내부 메모리 상태를 고치는 조치가 아닙니다. PHP 공식 완화책과 한계
오래된 주문 시스템을 맡았다면 어디서 시작할까
가상의 작은 회사 주문 서비스를 생각해 보겠습니다. 고객 접수 화면은 PHP, 사내 관리 화면은 JSP, 외부 문서 수집 작업은 Node.js입니다. 몇 년 동안 큰 장애가 없었고, 유지보수 업체는 “프로그램은 최신”이라고 답합니다. 이 말만으로는 어느 실행 환경이 어떻게 바뀌었는지 알 수 없습니다.
먼저 업무별로 실제 실행 위치와 관리자를 정리합니다. PHP 웹 프로세스, JSP를 실행하는 Tomcat과 JDK, Node.js 문서 작업, 예약 작업과 컨테이너 이미지를 분리합니다. 각 항목에 전체 버전·패키지 공급자·지원 상태·외부 입력 경로·교체 담당자를 붙이면, 취약점 공지를 보고 확인할 곳이 비로소 생깁니다. ‘홈페이지 담당 업체’라는 연락처 하나보다 구체적인 운영 목록이 필요합니다.
그다음 “최신으로 해 주세요”를 다음처럼 바꿔 요청할 수 있습니다.
고객 접수·사내 관리·문서 작업에서 실제 실행 중인 PHP, Tomcat, JDK, Node.js의 버전과 공급자를 알려 주세요. 해당 보안 공지의 영향 조건과 수정 포함 근거, 배포한 패키지·이미지, 모든 운영 프로세스의 교체 여부를 확인해 주세요. 지원 종료 항목은 대체 지원이나 이전 계획도 함께 알려 주세요.
직접 관리한다면 터미널의 PHP·Node.js 정보를 확인하는 다음 명령을 출발점으로 사용할 수 있습니다. 설치와 실행 경로가 준비된 Windows PowerShell·macOS·Linux 공통 명령입니다. PHP 옵션과 Node.js 프로세스 정보에 근거한 안내이며, 운영 서버 전체를 자동 점검하는 명령은 아닙니다. PHP 명령행 옵션, Node.js 프로세스 정보
php -v
php --ini
node -p "JSON.stringify({version:process.version,execPath:process.execPath})"
이 결과에도 경계가 있습니다. 명령행 PHP가 읽는 설정이 웹 서버의 PHP-FPM 설정과 같다는 보장은 없습니다. 터미널의 Node.js가 바뀌었어도 이미 실행 중인 서비스는 예전 프로세스일 수 있습니다. 컨테이너 서비스라면 호스트가 아니라 그 이미지 안의 실행 환경을 확인해야 합니다. 명령을 찾지 못한다는 오류도 원격 호스팅에 해당 프로그램이 없다는 증거는 아닙니다.
호환성 문제가 있다면 수정 적용과 전면 개편을 한 작업으로 묶지 않는 편이 판단에 도움이 됩니다. 예를 들어 Tomcat 9에서 10 계열로 넘어갈 때는 javax.*에서 jakarta.*로의 API 패키지 변경처럼 애플리케이션 검토가 필요한 차이가 있습니다. 이를 이유로 지원 중인 기존 계열의 보안 패치까지 모두 멈추는 것은 별개의 선택입니다. 지원 가능한 조합을 유지하면서 이전의 범위와 테스트를 정해야 합니다. Apache의 Tomcat 10 마이그레이션 안내
적용 전에는 현재 패키지·설정과 복구 절차를 기록하고, 로그인·권한 확인·파일 업로드·주문 저장·예약 작업·외부 문서 처리처럼 업무를 대표하는 경로를 고릅니다. 배포 후에는 같은 경로의 결과와 오류를 확인하며 모든 인스턴스가 교체됐는지 대조합니다. 되돌릴 상황이라면 취약한 환경을 다시 노출한다는 문제도 생기므로, 복구와 함께 필요한 접근 제한과 후속 수정 계획까지 판단해야 합니다.
마지막으로 서비스가 정상 동작하는지와 이전에 침해됐는지는 다른 검사입니다. PHP 후속 보도가 소개한 허니팟 연구는 Apache에서 셸 프로세스가 시작된 흔적 같은 실행 증거를 제시하면서, 관찰 환경의 한계도 밝혔습니다. 우리 환경에서 침해가 의심된다면 관련 로그를 보존하고, 평소와 다른 자식 프로세스·파일 변경·외부 통신을 담당 보안팀이나 운영사와 조사해야 합니다. 패치를 설치했다는 사실만으로 과거의 무단 접근 가능성이 없어지지는 않습니다. 허니팟 관찰 원문
Node.js 26 전환, 최신 버전이 항상 정답일까?
보안 업데이트를 서둘러야 한다는 말은 새 메이저 버전이 나오자마자 모든 프로젝트를 옮겨야 한다는 뜻일까요? Node.js 26 전환을 생각하면 이 차이가 더 분명해집니다. 공식 일정에는 26의 LTS 전환일이 2026년 10월 28일로 잡혀 있습니다. 새로운 장기 지원 계열을 선택할 수 있게 되는 것은 반가운 변화지만, 기존 서비스의 이전 시점은 따로 판단할 문제입니다.
LTS는 장기 지원 정책을 뜻합니다. 내가 쓰는 프레임워크, 데이터베이스 드라이버, 네이티브 모듈과 배포 환경의 모든 조합이 이미 검증됐다는 보증서는 아닙니다. 공식 문서도 운영 애플리케이션에 Active LTS 또는 Maintenance LTS 계열을 사용하라고 안내합니다. 지원 중인 LTS를 보안 수정과 함께 유지하는 선택도 운영 기준에 들어갑니다. Node.js 릴리스 정책
여기서 ‘기존 LTS를 유지한다’는 말을 특정 소버전에 계속 고정한다는 뜻으로 받아들이면 곤란합니다. 예를 들어 24 계열을 계속 쓰더라도, 이번 보안 수정이 들어간 24.22.1과 그 이후의 공식 보안 릴리스를 확인해야 합니다. 익숙한 버전이라는 이유로 보안 수정까지 멈추면 앞서 살펴본 패치 공백이 다시 생깁니다. 메이저 전환을 기다리는 것과 현재 계열의 보안 패치를 미루는 것은 다른 결정입니다. Node.js 24.22.1 보안 릴리스
저도 지금까지 Node.js 24로 개발한 프로젝트가 많습니다. 24의 지원 종료일은 공식 일정상 2028년 4월 30일이므로, 현재 계열의 보안 업데이트를 유지하면서 26의 호환성과 운영 사례를 살펴보려 합니다. 다만 막연히 ‘좀 더 안정되면’이라는 생각만으로 기다리지는 않으려 합니다. 무엇을 확인하면 옮길 수 있는지, 그 조건을 미리 정해 두는 편이 낫다고 봅니다.
준비할 것은 구체적입니다. 별도의 검증 환경에서 26으로 빌드하고 주요 업무 경로를 확인할 수 있게 하고, 사용하는 프레임워크와 패키지의 지원 여부를 살펴보려 합니다. 배포할 실행 이미지와 문제가 생겼을 때의 복구 절차도 준비 대상입니다. 검증 중 발견한 문제를 기록해 두면, 이후 보안 공지나 지원 정책 변화가 생겼을 때 남은 작업을 빠르게 판단할 수 있습니다.
결국 제가 택하려는 방향은 24의 보안 수준을 유지하며, 26으로 옮길 준비를 진행하는 것입니다. 새 버전의 숫자만 보고 서두르기보다 필요한 확인을 하되, 기다림이 대응 불능으로 이어지지 않게 하려 합니다. 보안 문제의 영향 조건이 우리 환경과 맞거나 기존 지원 경로가 달라진다면, 그때는 이전에 정한 일정도 다시 판단해야 합니다.
PHP2Shell을 보고 남겨 둘 질문도 이와 닿아 있습니다. 우리 서비스는 무엇으로 실행되는지, 누가 보안 공지를 확인하는지, 수정본을 검증해 적용할 경로가 있는지, 실제로 바뀌었다는 증거가 있는지입니다. 오래된 결함이 지금 드러난 이유를 AI 하나로 설명할 수는 없습니다. 이미 있던 코드의 약점, 위험을 구체화한 연구, 수정 결정을 늦춘 구조, 현장의 적용 지연을 함께 봐야 합니다. 오랫동안 잘 돌아간 시스템을 앞으로도 믿고 쓰려면, 발견 속도에 맞춰 수정하고 교체할 수 있는 상태를 꾸준히 유지해야 합니다.
VIEW—


