10월 21일 PHP와 Node.js가 같은 날 원격 코드 실행 취약점을 고친 보안 릴리스를 냈다. PHP2Shell(CVE-2026-109618·109619)은 8.2.35·8.3.36·8.4.27·8.5.12 미만이, Node.js Brotli 디코더 결함(CVE-2026-111497, 높음)은 22.23.4·24.22.1·26.12.1 미만이 대상이다. 우리 사이트가 밖에서 어떤 버전을 드러내는지, 사내에서 띄운 AI 서비스가 인증 없이 응답하지 않는지는 외부 응답으로 먼저 확인할 수 있다. 다만 외부 응답은 신호일 뿐이라, 판정이 나오면 서버 안에서 실행 중인 버전과 설정을 다시 확인해야 한다.
PHP2Shell의 원인과 대응 순서는 PHP2Shell 대응 가이드에, MCP 서버를 노린 공격 흐름은 AI 코딩 에이전트 보안에 정리했다. 이 글은 '밖에서 무엇이 보이는가'를 확인하는 방법과 결과를 읽는 법을 다룬다.
한눈에 보기
| 점검 | 밖에서 보는 것 | 결과가 뜻하는 것 | 서버 안에서 확인 |
|---|---|---|---|
| PHP2Shell | 응답 머리글의 PHP 버전(X-Powered-By·Server), PHPSESSID 쿠키, 같은 사이트의 .php 주소 | 영향 범위 버전 표기 / 버전 없이 PHP 흔적만 / 수정 버전 이상 표기 | 웹 서비스가 실제로 쓰는 PHP(PHP-FPM 등)의 버전과 재시작 여부 |
| Node.js Brotli RCE | 응답 머리글의 node 버전, Express·Next.js 같은 프레임워크 흔적 | 영향 범위 버전 표기 / 지원 종료 계열 / 수정 버전 이상 표기 | 앱 프로세스의 process.version, 외부 Brotli 데이터를 node:zlib 로 푸는 경로 |
| AI 서비스 노출 | 첫 화면 응답 호스트의 /api/tags, /v1/models, /sse, /mcp 응답 형식(포트 80·443) |
모델 목록 공개 / MCP 접속 정보 응답 / JSON-RPC 응답 / 인증 요구 응답 | 서비스가 묶인 주소, 리버스 프록시·터널·컨테이너 포트 공개, 앞단 인증 |
| 하위 도메인 목록 | 인증서 공개 기록(CT)에 남은 이름, 공인 IP 로 풀리는지 | 우리 도메인 아래에 공개된 이름(과거 이름 포함) | 쓰지 않는 이름의 DNS 정리, 개발·관리용 이름의 접근 통제 |
왜 지금 밖에서부터 보나
두 취약점 모두 공격자가 요청만으로 서버 프로세스를 망가뜨릴 수 있는 종류다. PHP2Shell은 웹 요청을 처리하는 PHP가 대상이고 로그인이 필요 없다(PHP 공식 공지). Node.js 결함은 앱이 외부에서 받은 Brotli 압축 데이터를 node:zlib 로 풀 때 내부 버퍼를 넘겨 쓰게 만들어, 프로세스 중단·메모리 노출·원격 코드 실행으로 이어질 수 있다(Node.js 보안 공지). Node.js는 지원이 끝난 버전은 보안 릴리스 때마다 늘 영향을 받는다고 밝힌다.
문제는 '어느 서버가 몇 버전인지'를 담당자도 바로 모르는 경우가 많다는 점이다. 오래된 쇼핑몰, 협력사가 만든 이벤트 사이트, 개발용으로 띄워 둔 서버가 응답 머리글에 버전을 그대로 드러내고 있으면 공격자도 같은 방법으로 먼저 찾는다. 사내에서 시험용으로 띄운 Ollama나 OpenAI 호환 API 서버, MCP 서버가 앞단 인증 없이 인터넷에 응답하면 누구나 모델 목록을 보거나 접속을 시도할 수 있다.
1. 밖에서 확인하는 방법
가장 먼저 볼 것은 사이트 첫 화면의 GET 응답 머리글이다. Server, X-Powered-By, Set-Cookie 줄에 PHP·Node.js 버전이나 흔적이 드러나는지 본다.
- Linux·macOS:
curl -sS -D - -o /dev/null https://company.co.kr/ - Windows:
curl.exe -sS -D - -o NUL https://company.co.kr/ - 응답이 리다이렉트(301·302)면
Location의 대상 주소도 같은 방법으로 확인한다. 요청 조건(브라우저 머리글, 쿠키)에 따라 응답이 달라질 수 있다.
AI 서비스는 같은 호스트의 대표 경로를 따로 확인한다. curl -sS https://company.co.kr/api/tags(Ollama), /v1/models(OpenAI 호환 API)가 모델 목록 JSON 을 돌려주면 인증 없이 응답하는 것이다.
이 확인을 한 번에 하는 무료 도구도 쓸 수 있다(글 끝 안내). 그 도구가 보내는 요청은 다음과 같다.
- 요청: GET 만 보낸다. 첫 화면과 같은 도메인 안의 리다이렉트, PHP 흔적이 없을 때만
/index.php하나, 첫 화면의 최종 응답 호스트·프로토콜에서 AI 경로 네 곳(리다이렉트는 따라가지 않음). https 연결이 안 되면 http 로 한 번 더 시도하되(인증서 검증 오류면 다시 시도하지 않고 '확인불가'로 표시), 이를 모두 합쳐 주소 하나에 많아야 12번이다. 공격 코드·로그인 시도·경로 탐색·포트 스캔은 하지 않는다. - 범위: 주소만 넣는 빠른 점검은 입력한 경로를 버리고 첫 화면에서 시작하며, 같은 등록 도메인 안의 다른 호스트로 리다이렉트될 수 있다. 회사 메일 점검은 허용 범위 안에서 입력한 URL 경로를 쓴다. 허용 도메인 밖의 호스트, 기본 포트(80·443)가 아닌 서비스, 로그인 뒤 화면, POST 로만 확인되는 기능은 범위 밖이다.
- 반복 제한: 같은 호스트에 최근 24시간 안에 만든 진행 중·완료 작업이 있으면 새로 점검하지 않고 그 결과를 보여 준다. 조치 직후에 다시 넣으면 이전 결과가 나올 수 있으니 점검 시각을 확인한다. 같은 등록 도메인은 첫 사이트와 하위 사이트를 합쳐 24시간에 새 점검 3건까지다.
- 결과: 점검이 끝나면 같은 화면에 판정이 나오고(대기 작업과 대상 응답에 따라 시간이 더 걸릴 수 있다), 결과 링크로 30일 동안 다시 열거나 CSV 로 내려받을 수 있다.
- 회사 메일로 신청하면: 메일 확인 뒤 신청 메일 도메인과 그 하위 호스트의 URL 을 30개까지(같은 호스트는 경로 5개까지) 한 번에 점검하고 결과를 메일로 받는다. '하위 도메인 목록'을 켜면 CT 기록과 DNS 확인 결과도 받는다. CT 기록에는 과거 이름이 섞이고 수집 상한이 있어 전체 자산을 빠짐없이 보여 주지는 않는다. 찾은 이름에는 접속하지 않는다.
2. 결과 읽는 법
판정은 '밖에서 관측한 사실'만 말한다. 색은 네 가지다.
| 색 | 실제 라벨 | 뜻 |
|---|---|---|
| 빨강 | 영향 버전 | 응답에 영향 범위의 버전이 그대로 적혀 있다(예: X-Powered-By: PHP/8.3.10, 8.3.36 미만) |
| 주황 | 확인 필요, 모델 목록 공개, MCP 접속 정보 응답, JSON-RPC 응답 | 버전 없는 PHP 흔적, 지원 종료 계열의 node 표기, AI 경로가 인증 없이 응답하는 등 서버 안 확인이 필요하다 |
| 초록 | 수정 버전, 인증 요구 응답, 접근 거부 응답 | PHP·Node.js 는 수정 기준 이상의 버전 표기. AI 는 주황 신호가 없고, /v1/models 의 401·403 에서 API 형식의 인증 오류 JSON 을 받았거나 /sse·/mcp 의 401 에서 WWW-Authenticate: Bearer resource_metadata=… 머리글을 확인한 경우. 초록이어도 일부 경로는 미확인일 수 있으니 근거를 함께 본다 |
| 회색 | 확인불가, 신호 없음, 일부 확인불가 | 판정할 신호가 없거나 응답이 없다. Express 같은 프레임워크 흔적만 있고 node 버전이 없을 때도 회색이다 |
세 가지는 꼭 함께 읽는다.
- 빨강이 곧 뚫렸다는 뜻은 아니다. 배포판 패키지는 번호를 그대로 두고 보안 수정만 넣기도 한다(예:
8.2.7-1~deb12u1). 근거에 배포판 표기가 붙어 있으면 패키지 변경 기록에서 두 CVE 반영 여부를 확인한다. - 회색이 안전하다는 뜻도 아니다. 버전을 숨긴 서버, CDN 뒤 서버, 기본 포트(80·443)가 아닌 서비스는 밖에서 판정할 수 없다. Ollama 기본 포트 11434 처럼 다른 포트에 열린 서비스는 이 점검 범위 밖이다.
- AI 판정은 '응답'까지만 본다. 모델 목록이 인증 없이 응답해도 실제로 모델을 호출하거나 관리할 수 있는지는 확인하지 않는다(프롬프트를 보내지 않는다).
/mcp의 JSON-RPC 응답은 MCP 일 수도, 다른 JSON-RPC 서버일 수도 있고, 인증 없이 도구를 호출할 수 있다는 뜻도 아니다. 인증 여부는 서버에서 확인한다.
3. 판정별로 할 일
PHP: 영향 버전·확인 필요
- 웹 서비스가 실제로 쓰는 PHP 를 확인한다. 셸의
php -v와 웹 서버가 쓰는 PHP-FPM(php-fpm -v, 서비스 설정의 실행 경로)이 다를 수 있다. - 8.2.35·8.3.36·8.4.27·8.5.12 이상(같은 계열)으로 올리고 PHP 를 실행하는 서비스를 모두 재시작한다. 호스팅·외주 운영이면 업체에 적용 패키지와 재시작 완료 여부를 받는다.
- 8.1 이하는 PHP 공식 수정 릴리스가 없다. 배포판·LTS 업체가 두 CVE 의 수정 패키지를 냈는지 바로 확인하고, 적용이 어렵다면 외부 접근 제한 같은 임시 대응을 정한 뒤 지원 버전으로 옮긴다.
- PHP 후속 공지 두 건도 확인한다. 10월 23일 공지(CVE-2026-115560)는
session.serialize_handler를php_binary또는php_serialize로 설정한 뒤session_start()호출 전에ini_set()으로php로 바꾸고, 세션 업로드 진행 추적도 켜 둔 앱에 해당한다. 그런 앱은 다음 정기 릴리스까지session.upload_progress.enabled=0을 적용하라고 권고한다. 10월 26일에는 Brotli 결함(CVE-2026-111497)을 고친 Windows 빌드(8.5.12-1·8.4.27-1·8.3.36-1·8.2.35-1)가 나왔다. cURL 확장의 자동 압축 해제로 닿는 경로이고 PHP 버전 번호는 그대로라, 외부 버전 표기로는 구분할 수 없다. - 버전 표기를 지운다. 웹 서비스가 실제로 읽는 php.ini 에
expose_php = Off를 넣고 서비스를 재시작한 뒤 GET 응답에서X-Powered-By가 빠졌는지 본다. 앱이나 프록시가 따로 넣는 버전 머리글은 그 계층에서 지운다. 표기를 지워도 취약점은 그대로이므로 업데이트가 먼저다.
Node.js: 영향 버전·확인 필요
node -v는 지금 셸에서 잡히는 설치본일 뿐이다. 서비스 계정·버전 관리자·컨테이너의 Node.js 는 다를 수 있으므로, 앱 시작 로그 등에서process.version과 실행 경로를 확인한다.- 그 런타임을 22.23.4·24.22.1·26.12.1 이상(같은 계열)으로 올리고 앱 프로세스를 재시작하거나 컨테이너를 교체한 뒤 다시 확인한다. 23.x·25.x·20.x 이하처럼 지원이 끝난 계열은 지원 계열로 옮긴다.
- 앱이 외부에서 받은 Brotli 데이터를 node:zlib 로 푸는지(업로드 해제, 프록시, 압축된 요청 본문 처리) 확인한다. 이 경로가 있으면 우선순위를 올린다.
- Express 를 쓴다면
app.disable('x-powered-by')로 프레임워크 표기를 지운다.
AI 서비스: 모델 목록 공개·MCP 접속 정보 응답·JSON-RPC 응답
- Ollama 의 기본 바인딩은
127.0.0.1:11434다. 그래도OLLAMA_HOST설정뿐 아니라 리버스 프록시, 터널, 컨테이너 포트 공개로 밖에 열릴 수 있으므로 이 경로를 모두 확인하고, 외부에 열 필요가 없다면 막는다. - 열어야 한다면 앞단(리버스 프록시·API 게이트웨이)에 인증을 둔다. MCP 규격도 서버가 모든 연결에 인증을 두고 Origin 머리글을 검증하며, 로컬에서 돌리면 127.0.0.1 에만 묶으라고 권고한다.
- 접근 기록에서 처음 보는 출발지의
/api/tags,/v1/models,/api/generate,/sse호출이 있었는지 본다.
4. 밖에서 알 수 없는 것
외부 점검은 '공격자가 처음 보는 화면'을 같이 보는 일이다. 서버 안의 실제 패치 상태, 재시작 여부, 내부망 서버, 로그인 뒤 기능, 다른 포트의 서비스, Windows 빌드처럼 버전 번호가 같은 패키지 차이는 볼 수 없다. 판정이 빨강이나 주황이면 서버 안에서 버전·패키지·재시작 시각을 확인하고, 10월 17일 이후 접근 기록을 함께 본다(PHP2Shell 은 공개 4일 전부터 공격 시도가 관측됐다).
무료로 확인하고 해석까지 받으려면
이 글에서 설명한 점검은 SENTRIX 무료 취약점 스캐너에서 회원가입 없이 할 수 있다. 결과 해석이나 서버 안 확인이 필요하면 결과 링크를 복사해 '진단 문의하기'의 문의 내용에 붙여 주면 판정 근거와 확인 순서를 함께 정리한다. 하위 도메인까지 계속 관리해야 한다면 SENTRIX ASM, 사내 AI 기능과 MCP 연결을 깊게 보려면 AI 모의해킹(AI 심화 취약점 진단)을 참고하면 된다.