2026년 10월 21일, PHP 프로젝트가 인증 없이 원격 코드 실행으로 이어질 수 있는 취약점 두 건(CVE-2026-109618, CVE-2026-109619, 통칭 PHP2Shell)의 수정 버전을 공개했습니다. 웹 요청을 받는 PHP라면 PHP-FPM이든 Apache 모듈이든 대상이라서, 포르본허도 운영 중인 서버를 전부 점검했습니다. 이 글은 그때 확인한 순서와, 패치만 올렸다면 놓칠 뻔한 지점을 정리한 기록입니다. 내용은 2026년 10월 26일 기준이며 상황이 빠르게 바뀌고 있으니 공식 안내를 함께 확인해 주세요.

무슨 문제인가

  • PHP 코어가 요청을 처리하는 과정(멀티파트 업로드 포함)의 메모리 결함입니다. 캐나다 사이버보안센터는 두 건을 use-after-free(CWE-416)로 분류했고, 인증 없는 공격자가 임의 코드를 실행할 수 있다고 경고했습니다.
  • 수정 버전은 8.5.12, 8.4.27, 8.3.36, 8.2.35입니다. 8.1 이하는 PHP 프로젝트가 더 이상 업데이트하지 않고, 일부 배포판과 장기 지원 업체만 백포트를 제공할 수 있습니다.
  • Apache의 mod_php 구성이 특히 위험합니다. 애플리케이션 코드가 실행되기도 전에 요청만으로 문제가 발동할 수 있기 때문입니다. PHP-FPM, php-cgi, FrankenPHP, LiteSpeed도 영향권입니다.
  • 개념 증명(PoC)이 공개되어 있고, 일부 업체는 실제 공격 시도를 관측했다고 밝혔습니다. 다만 기관마다 평가가 엇갈리니 "당장 공격받고 있다"보다 "곧 자동화된 스캔이 온다"로 받아들이는 편이 안전합니다.

이틀 뒤 나온, 패치가 없는 세 번째 문제

10월 23일에는 CVE-2026-115560이 별도로 보고되었습니다. 10월 21일 업데이트로는 해결되지 않는 원격 코드 실행 결함입니다. 다행히 조건이 까다롭습니다. 앱이 세션 직렬화 방식(session.serialize_handler)을 실행 중에 바꾸고, 업로드 진행률 기능이 켜져 있어야 하며, 그런 코드를 쓰지 않는 앱은 영향이 없다는 것이 현재까지의 안내입니다. 공식 임시 방어는 아래 한 줄입니다. 이 글을 쓰는 시점에는 이 문제를 고친 정식 버전을 확인하지 못했습니다.

session.upload_progress.enabled = 0

이 설정은 PHP2Shell 두 건을 막아 주지는 못합니다. 그래서 "패치는 패치대로, 이 설정은 설정대로" 둘 다 필요합니다.

저희가 한 순서

  1. 웹 요청을 실제로 받는 PHP를 찾았습니다. 서버에 설치된 버전을 세는 게 아니라, 어떤 사이트가 어떤 FPM 풀이나 컨테이너로 가는지부터 그렸습니다.
  2. 패치 버전으로 교체했습니다. 컨테이너는 베이스 이미지를 새로 받아 다시 빌드하고 직전 이미지를 롤백용 태그로 남겼습니다. 서버에 직접 설치된 PHP는 새 버전을 병행 설치한 뒤 사이트를 하나씩 옮겼습니다.
  3. 교체 전후의 응답 코드를 자동으로 비교하고, 달라지면 스스로 되돌리게 했습니다. 사람이 새벽에 지켜보지 않아도 되는 구조가 가장 안전했습니다.
  4. session.upload_progress.enabled = 0을 모든 웹 PHP에 넣었습니다. 패치된 버전에도 이중 방어로 넣었고, 115560의 임시 방어이기도 합니다.
  5. 끝난 뒤 남은 길을 다시 찾았습니다. 아래 4가지는 그 과정에서 정리한 점검 항목입니다.

패치 뒤에 놓치기 쉬운 4가지

1. CLI에서 본 설정은 웹에서 쓰는 설정이 아닙니다

터미널에서 php -i로 확인한 값은 CLI용 설정입니다. 웹 요청은 FPM이나 Apache 모듈이 읽는 별도의 설정을 따릅니다. 저희 서버 중에는 CLI에서는 값이 1로 나오는데 FPM 설정에는 이미 0이 들어 있던 곳도 있었습니다. 반대로 CLI에서 0이라고 안심하면 웹은 그대로일 수 있습니다. 확인은 웹을 서빙하는 쪽의 설정으로 해야 합니다. FPM이라면 php-fpm -i(배포판에 따라 php-fpm8.3 -i처럼 버전이 붙습니다) 출력에서 session.upload_progress.enabled를 보면 됩니다.

2. 설정은 프로세스가 다시 읽어야 적용되고, 컨테이너는 이미지에 넣어야 남습니다

php.ini를 고쳐도 FPM을 reload하기 전에는 그대로입니다. 컨테이너에서는 더 조심해야 합니다. 실행 중인 컨테이너 안에서 설정을 고치면 다음 배포나 재생성 때 사라집니다. 설정 파일을 이미지(또는 마운트하는 파일)에 넣고 다시 배포해야 합니다.

3. 사이트 이름 목록으로 옮기면, 목록 밖의 요청은 옛 버전에 남을 수 있습니다

위험을 줄이려고 사이트 이름을 하나씩 새 버전으로 옮기는 방식은 안전하지만 허점이 있습니다. 목록에 없는 별칭 도메인이나 IP 주소로 직접 들어오는 요청은 기본 처리기를 따라 옛 버전으로 갈 수 있고, 스캐너는 바로 그 경로로 옵니다. 실제로 어느 PHP가 처리하는지는 새 버전과 옛 버전 풀의 작업 프로세스가 쓴 CPU 시간을 재 두고 테스트 요청을 보낸 뒤 어느 쪽 시간이 늘어나는지 비교하면 알 수 있습니다. 점진 전환이 끝나면 마지막에는 목록이 아니라 "기본값" 자체를 새 버전으로 바꾸는 단계가 꼭 필요합니다.

4. 지원이 끝난 버전은 설정만으로 끝낼 수 없습니다

8.1 이하를 쓰고 있다면 임시 설정은 시간을 벌 뿐입니다. 배포판이 백포트를 제공하는지 확인하고, 그렇지 않다면 업그레이드 일정을 잡아야 합니다. 오래된 코드베이스는 새 버전에서 경고 메시지가 응답 앞에 섞여 나가 리다이렉트가 깨지는 일도 있어서, 전환 전에 사이트별로 동작을 확인하는 시간이 필요합니다.

지금 해야 할 일 요약

  • 웹을 서빙하는 PHP의 버전을 확인하고 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 이상으로 올립니다. 배포판 패키지를 쓴다면 배포판의 보안 업데이트가 더 빠를 수 있습니다.
  • session.upload_progress.enabled = 0을 넣고, FPM이나 Apache를 reload한 뒤 웹 쪽 설정으로 확인합니다.
  • 옛 버전이 처리하는 길(별칭 도메인, IP 직접 접속, 크론, CLI)이 남아 있지 않은지 찾습니다.
  • 8.1 이하라면 백포트 여부를 확인하고 업그레이드 계획을 세웁니다.
  • 관리자 화면은 인터넷에 그대로 열어 두지 않습니다.

서버 구성이 복잡하거나 오래된 PHP 코드를 새 버전으로 옮겨야 하는데 일정과 인력이 빠듯하다면, 포르본허가 PHP 버전과 처리 경로 점검, 패치 적용과 롤백 계획, 레거시 코드의 새 버전 호환성 확인까지 언제든 도와드릴 수 있습니다. 아래 문의 양식에 상황을 간단히 남겨 주세요.

참고 자료