2026년 10월 21일, PHP 프로젝트는 PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35를 보안 릴리스로 공개했습니다. 두 취약점 CVE-2026-109618과 CVE-2026-109619는 인증 없이 원격 코드 실행으로 이어질 수 있습니다. PHP 보안팀은 공지에서 "완전한 완화책은 없으며 업데이트가 유일한 안전한 선택"이라고 밝혔습니다.

이 사건이 불편한 이유는 간단합니다. 취약점이 WordPress, Laravel, Symfony 같은 애플리케이션 코드에 있지 않습니다. 그 코드가 실행되기 전에 요청을 해석하고 정리하는 PHP 엔진의 요청 수명주기 안에 있습니다. 애플리케이션에 인증을 잘 걸어 두어도, PHP가 요청을 읽는 단계에서 문제가 터지면 애플리케이션 코드는 개입할 기회를 얻지 못합니다.

무엇이 고쳐졌나: 두 개의 메모리 수명주기 버그

두 CVE는 이름만 다를 뿐 같은 종류의 실패를 공유합니다. 요청이 비정상적으로 끝날 때 PHP 내부 상태가 완전히 초기화되지 않거나 해제된 메모리를 가리키고, 그 상태를 다음 요청이나 종료 절차가 다시 사용합니다.

항목 CVE-2026-109618 CVE-2026-109619
공식 이름 OOM corruption을 통한 RCE stale global state를 통한 RCE
핵심 조건 multipart 요청 처리 중 메모리 한도 도달 요청 소유 전역 상태가 해제 후에도 남음
실패 지점 longjmp()로 요청을 중단하면서 request memory가 일부만 초기화됨 같은 worker의 다음 요청이 초기화 전에 이전 상태를 읽음
확인된 영향 인증 없는 원격 코드 실행, worker 메모리 노출 heap use-after-free, 메모리 노출, 원격 코드 실행
CVSS v4.0 9.2 Critical 9.2 Critical
주의할 전제 mod_php 기본 설정에서 애플리케이션 코드 실행 전 공격 가능 producer와 consumer 요청이 같은 worker에 도달해야 함

두 취약점의 CVSS 벡터는 AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N입니다. 네트워크에서 공격할 수 있고 권한이나 사용자 상호작용은 필요 없지만, 공격 복잡도는 높고 특정 조건이 필요합니다. 높은 복잡도가 낮은 위험을 뜻하지는 않습니다. 조건이 맞는 서버에서는 인증 없이 코드 실행까지 갈 수 있고, 조건이 맞지 않는 서버에서도 메모리 노출은 성립할 수 있습니다.

왜 애플리케이션 코드 방어로는 부족한가

PHP는 요청을 받으면 애플리케이션 코드를 실행하기 전에 multipart 파싱, 세션 업로드 진행률 처리, 헤더와 입력 초기화 같은 작업을 먼저 합니다. 이 초기화 구간에서 할당 실패가 발생하면 PHP는 longjmp()로 현재 요청을 빠져나옵니다. 문제는 이탈 과정에서 request memory가 아직 완전히 초기화되지 않았을 수 있다는 점입니다. 이후 종료 절차가 그 메모리를 정상 상태로 착각하고 읽거나 분기하면 메모리 손상으로 이어집니다.

두 번째 경로는 더 조용합니다. 요청이 끝나면 PHP는 요청 소유 전역 상태를 해제하고 다음 요청을 위해 초기화해야 합니다. 그런데 fatal error나 경고가 이 순서를 깨뜨리면, 해제된 메모리를 가리키는 전역 상태가 남습니다. 같은 worker가 다음 요청을 처리할 때 정상 초기화가 덮어쓰기 전에 이 상태를 읽으면 use-after-free가 됩니다.

요청 A: multipart 파싱 또는 업로드 진행률 처리
  → 메모리 한도 도달
  → longjmp로 요청 중단
  → request memory 일부만 초기화된 상태로 종료
  → 종료 절차가 잘못된 상태를 읽음
  → 같은 worker의 요청 B가 남은 상태를 재사용

Wiz Research는 이 경로를 조합해 WordPress, Drupal, Joomla의 최신 컨테이너 이미지에서 인증 없는 RCE를 시연했습니다. 중요한 단서는 이것이 해당 CMS의 플러그인 취약점이 아니라는 점입니다. Wiz는 "Hello World" 수준의 애플리케이션도 취약한 PHP 버전 위에서 실행되면 영향을 받을 수 있다고 설명합니다.

그래서 "우리 애플리케이션에는 입력 검증이 있다", "WAF가 있다", "로그인 페이지 뒤에 있다" 같은 대답은 이 취약점의 핵심을 피해 갑니다. 물론 WAF와 프록시 제한은 특정 공격 경로를 줄이는 데 도움이 됩니다. 그러나 메모리 안전성 문제를 애플리케이션 계층에서 완전히 대체할 수는 없습니다.

누가 영향을 받나: 버전과 SAPI

공식 패치는 PHP 8.2부터 8.5까지 제공됩니다. 8.1 이하는 PHP 프로젝트의 정기 지원이 끝났고, 배포판이나 LTS 벤더의 백포트를 확인해야 합니다. PHP 보안팀은 소스에서 직접 빌드하는 사용자를 위해 7.4, 8.0, 8.1용 최소 소스 백포트도 제공했습니다.

PHP 계열 수정 버전 메모
PHP 8.5 8.5.12 현재 안정 계열
PHP 8.4 8.4.27 보안 릴리스
PHP 8.3 8.3.36 보안 릴리스
PHP 8.2 8.2.35 2026년 12월 31일 EOL 예정
PHP 8.1 이하 업스트림 정기 지원 종료 배포판·LTS 백포트 확인 또는 업그레이드

Wiz는 PHP 5.4 이후 거의 모든 버전이 두 취약점 중 하나에는 해당한다고 보고 있습니다. 이는 Wiz의 분석 범위이며, 공식 수정 버전은 8.2~8.5에 집중되어 있습니다. 오래된 버전을 유지하는 조직은 "우리 PHP는 너무 오래돼서 해당 없음"이 아니라 "벤더가 백포트를 제공했는가"를 확인해야 합니다.

SAPI별 위험도 같지 않습니다. PHP 공식 공지는 PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, LiteSpeed를 모두 위험 대상으로 명시합니다. 그중에서도 mod_php의 위험이 가장 높다고 봅니다. 인증 없는 요청이 애플리케이션 코드 실행 전에 취약 경로를 건드릴 수 있는 데모가 확인됐기 때문입니다.

실행 환경 영향 확인할 점
Apache + mod_php 가장 높은 위험 요청 초기화 단계에서 애플리케이션 코드 전에 트리거 가능
PHP-FPM 영향 있음 세션 수명주기 경로가 mod_php와 FPM에서 검증됨
php-cgi, FrankenPHP, LiteSpeed 공식 공지에 포함 CLI 버전과 실제 웹 SAPI 버전을 구분해서 확인
CLI만 사용하는 배치 웹 요청 경로와 다름 그래도 동일 PHP 바이너리·패키지 인벤토리는 확인

여기서 "인증 없이"는 "네트워크 접근 없이"와 다릅니다. 공격자는 해당 PHP 엔드포인트에 요청을 보낼 수 있어야 하고, multipart 요청이나 worker 재사용 같은 조건을 맞춰야 합니다. 그러나 애플리케이션 로그인은 필요 없습니다. 이 구분이 실무에서 중요합니다. 내부망이라고 안전하다고 단정할 수 없고, 인터넷에 노출된 PHP 엔드포인트는 더 급합니다.

지금 할 일: 패치와 재시작

가장 중요한 원칙은 하나입니다. PHP를 수정 버전으로 올리고, 모든 worker를 재시작하세요. 패키지만 업데이트하고 오래된 FPM master나 Apache worker가 계속 돌면 수정된 바이너리를 쓰지 않을 수 있습니다.

  1. 실제 실행 중인 런타임을 확인합니다. composer.json의 PHP 요구사항이 아니라 웹 SAPI가 로드한 바이너리와 패키지 버전을 봐야 합니다.
  2. 배포판 패키지, 공식 소스, 컨테이너 베이스 이미지 중 어디서 PHP를 공급하는지 확인하고 수정 버전으로 올립니다.
  3. FPM master, Apache, 컨테이너 인스턴스를 롤링 재시작합니다. opcache만 초기화하는 것으로는 부족합니다.
  4. 실제 웹 SAPI에서 버전을 다시 확인합니다. CLI php -v와 FPM이 서로 다른 패키지인 경우가 흔합니다.
  5. WAF나 CDN 규칙을 임시로 적용했다면 패치를 대신하는 것으로 취급하지 않습니다.

Cloudflare는 공개 전에 기본 WAF 보호를 배포했다고 밝혔습니다. 이는 위험을 줄이는 데 유용하지만, 패치를 대신하지는 않습니다.

배포 파이프라인에 넣을 수 있는 최소 버전 게이트는 다음과 같습니다. 이 코드는 이 글에서 실행하지 않았으며, 실제 배포 전에 여러분의 PHP 패키지 구조에 맞춰 검증해야 합니다.

<?php
// 미실행 예시: CI/배포 전 CLI 버전 게이트
$minimum = [
    '8.2' => 80235,
    '8.3' => 80336,
    '8.4' => 80427,
    '8.5' => 80512,
];

$branch = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION;
$required = $minimum[$branch] ?? null;

if ($required === null || PHP_VERSION_ID < $required) {
    fwrite(STDERR, 'Unpatched PHP: ' . PHP_VERSION . PHP_EOL);
    exit(1);
}

fwrite(STDOUT, 'PHP ' . PHP_VERSION . ' meets the minimum patch level.' . PHP_EOL);

이 스크립트는 CLI 기준입니다. FPM pool의 php_admin_value, 실제 웹 SAPI, 컨테이너 이미지 태그는 별도로 확인해야 합니다. 배포 게이트가 통과해도 웹 worker가 이전 바이너리를 계속 들고 있으면 의미가 없습니다.

패치할 수 없다면: 공식 완화책의 범위

PHP 공식 공지는 업데이트가 불가능한 경우의 임시 완화책을 제공하지만, "완전한 해결"이 아니라고 명시합니다. 완화책은 특정 공격 경로를 줄일 뿐, 취약한 런타임을 안전하게 만들지 않습니다.

취약점 임시 설정 기대 효과 한계
CVE-2026-109618 memory_limit=-1을 FPM pool이나 Apache 설정의 php_admin_value로 고정 애플리케이션이 조작할 수 있는 PHP 메모리 한도 실패를 제거 기저 할당 실패는 여전히 취약 경로에 도달할 수 있음
CVE-2026-109618 post_max_size=8M(업로드 없음) 또는 32M(업로드 필요), upload_max_filesize를 그보다 작게 multipart 요청 크기와 파싱 부담 감소 모든 요청 형태를 막지는 못함
CVE-2026-109618 프록시·웹서버에서 디코딩된 요청 크기, 필드 수, multipart 파트 수, 헤더 수, 중첩 깊이 제한 Content-Length가 없는 요청까지 방어 범위 확장 애플리케이션별 정상 트래픽과 충돌할 수 있음
CVE-2026-109619 session.upload_progress.enabled=0을 php.ini/FPM pool/서버 설정에 적용하고 모든 worker 재시작 확인된 세션 업로드 경로 차단 다른 stale state 변형은 막지 못함
CVE-2026-109619 PHP 8.5에서 fatal_error_backtraces=0 적용 후 모든 worker 재시작 확인된 fatal backtrace 경로 차단 PHP 8.5에 한정되며 다른 경로는 남음

memory_limit=-1은 직관과 반대됩니다. 메모리 한도를 없애면 OOM 자체를 피할 수 있지만, 그 대신 컨테이너나 cgroup 같은 외부 경계가 필요합니다. PHP 공지는 RLIMIT_AS나 RLIMIT_DATA로 대체하지 말라고 경고합니다. 이 제한이 오히려 취약한 할당 실패를 유발할 수 있기 때문입니다.

FPM pool에 적용하는 예시는 다음과 같습니다. 이 설정도 미실행 예시이며, 실제 값은 서비스의 정상 요청 크기와 업로드 요구사항을 보고 정해야 합니다.

; 미실행 예시: 업로드가 없는 서비스의 임시 FPM pool 설정
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 4M
php_admin_value[session.upload_progress.enabled] = 0

ini_set()이나 FPM .user.ini에 의존하면 안 됩니다. 이 취약점은 요청 파싱 전에 발생할 수 있으므로, 요청마다 애플리케이션이 늦게 적용하는 설정으로는 경로를 막기 어렵습니다. error_reporting이나 display_errors로 진단 메시지를 숨기는 것도 해결책이 아닙니다.

탐지: 로그에서 무엇을 볼까

Wiz는 패치 전 조사에 참고할 수 있는 신호로 비정상적인 multipart 요청 뒤의 메모리 한도 오류, 반복되는 HTTP 500, 비정상적으로 크거나 잘못된 PHPSESSID 응답 쿠키, 설명 없는 PHP worker 교체를 언급했습니다. 이 신호들은 공격의 확정 증거가 아니라 조사 단서입니다. 취약점이 애플리케이션 계층 아래에서 실행되므로 평소 애플리케이션 로그에는 맥락이 거의 남지 않을 수 있습니다.

이런 신호가 보이면 순서를 바꾸지 마세요. 먼저 패치하고 worker를 재시작한 뒤, 침해 조사를 합니다. 로그만 보고 "공격 흔적이 없으니 괜찮다"고 판단하면 메모리 노출이나 코드 실행을 놓칠 수 있습니다.

AI는 무엇을 했나: 발견·악용·패치 검증의 분업

이 사건을 "AI가 PHP를 해킹했다"거나 "AI가 PHP를 고쳤다"로 요약하면 중요한 부분을 잃습니다. Wiz의 설명과 OpenAI의 공개 글을 나눠 보면 역할이 다릅니다.

주체 역할 근거
Wiz Research 취약점 발견·보고, WordPress/Drupal/Joomla 대상 악용 시연, 패치 검증 참여 Wiz 기술 블로그
Gemini 4 Argon Wiz가 발견, 악용 개발, 공격 탐지, 패치 검증에 기여했다고 밝힌 모델 Wiz 기술 블로그
Calif GPT-5.6 Sol을 사용해 취약점 중 하나를 발견 OpenAI Patch the Planet 글
OpenAI Patch the Planet 이니셔티브로 조정, 연구·기술 지원 OpenAI Patch the Planet 글
PHP 유지관리자 수정 코드 작성·검토와 공식 릴리스 PHP.net, GitHub Security Advisory

Wiz는 프런티어 모델이 수십 년 묻혀 있던 결함을 실용적인 공격으로 바꾸는 장벽을 크게 낮췄다고 설명했습니다. 구체적으로는 여러 PHP 버전과 설정, 메모리 레이아웃에 맞춰 익스플로잇을 반복 조정하는 작업을 모델이 도왔고, 여러 애플리케이션에 적용 가능한 익스플로잇을 몇 시간 만에 개발·적응할 수 있었다고 합니다.

이것이 AI가 패치를 작성했다는 뜻은 아닙니다. PHP 유지관리자가 수정을 주도했고, Wiz와 Calif, OpenAI는 발견·조정·검증에 참여했습니다. 그러나 방어자에게 주는 메시지는 분명합니다. 공격자가 취약점 하나를 여러 버전과 설정에 맞춰 빠르게 변형할 수 있다면, "패치가 나오면 나중에 하자"는 일정은 더 이상 안전하지 않습니다.

시연과 일반화 사이의 거리

Wiz의 시연은 강력하지만, 모든 PHP 서버에서 같은 성공률로 재현된다고 일반화하면 안 됩니다. Wiz도 익스플로잇의 이식성과 신뢰도가 PHP 빌드, 확장 모듈, 프런트엔드 동작, 할당자 레이아웃에 따라 달라진다고 밝혔습니다. worker가 많거나 트래픽이 섞이면 성공까지 필요한 시도 횟수가 늘어날 수 있습니다. 다만 그 조건이 사라지는 것은 아닙니다.

또한 Wiz의 "웹사이트 70%"는 Wiz가 제시한 영향 범위 추정치입니다. PHP가 널리 쓰인다는 방향은 분명하지만, 개별 조직의 노출도는 자신의 런타임 인벤토리로 판단해야 합니다.

결론: 런타임을 자산 목록의 1급 시민으로

이번 사건의 교훈은 "PHP를 최신 버전으로 유지하자"보다 조금 더 구체적입니다. 많은 팀이 애플리케이션 의존성은 lockfile로 관리하면서, 실제 서비스가 어떤 PHP 바이너리와 SAPI와 설정으로 요청을 처리하는지는 정확히 모릅니다. 그 상태에서는 패치가 나와도 어떤 worker를 언제 재시작해야 하는지 알기 어렵습니다.

지금 확인할 것은 세 가지입니다. 첫째, 서비스별 PHP 버전과 SAPI를 인벤토리로 만들었는가. 둘째, 수정 버전으로 올린 뒤 모든 worker를 실제로 재시작했는가. 셋째, 배포 후 실제 웹 SAPI에서 버전과 핵심 설정을 확인하는가입니다.

애플리케이션 코드 리뷰로는 이 취약점을 막을 수 없습니다. 런타임을 패치하고, 그 패치가 실제 요청 처리 경로에 적용됐는지 확인하는 일만이 완전한 해결에 가깝습니다.

함께 읽으면 좋은 글

참고 자료

이 주제를 직접 따라가며 배우고 싶다면

웹 서비스의 런타임과 배포 경로를 직접 다뤄 보면, 패치가 코드 저장소 밖에서 끝나지 않는다는 감각이 생깁니다. 웹사이트를 기획하고 구현하고 배포하는 전 과정을 AI와 함께 연습해 보세요.