PHP2Shell 긴급 완화 가이드: session.upload_progress 비활성화와 EOL 백포트 패치
PHP2Shell 긴급 완화 가이드: session.upload_progress 비활성화와 EOL 백포트 패치
<time datetime="2026-10-23">검증일: 2026년 10월 23일</time>
CVE-2026-109618과 CVE-2026-109619(통칭 PHP2Shell)는 수백 대의 서버를 동시에 운영하는 조직에 곤란한 상황을 만듭니다. 전체 패키지를 즉시 올리면 호환성 문제와 다운타임이 발생할 수 있고, 미루면 미인증 RCE 노출이 계속됩니다. 이 가이드는 실제 운영 환경에서 검증된 세 가지 경로를 제시합니다.
- 옵션 A —
php.ini무중단 구성 강화 (가장 빠름, 약 30초) - 옵션 B — Nginx / Cloudflare 경계 필터링
- 옵션 C — EOL PHP 8.1용 upstream 백포트(commit
1d7dec1) 직접 빌드
1. 트리아지 및 조치 결정 트리
[ 경보: PHP2Shell (CVE-2026-109618 / 109619) ]
│
┌──────────────────┴──────────────────┐
│ 백엔드 php.ini / 컨테이너를 제어 가능? │
└──────────────────┬──────────────────┘
│
┌────────────────┴────────────────┐
▼ YES ▼ NO (프록시/CDN만 제어)
┌───────────────────────────┐ ┌───────────────────────────┐
│ 전략 A (최우선) │ │ 전략 B (경계 방어) │
│ php.ini에서 콜백 비활성화 │ │ Nginx/Cloudflare에서 │
│ 무중단, 약 30초 │ │ multipart 요청 차단 │
└─────────────┬─────────────┘ └───────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ PHP 8.1 이하 EOL 환경은 전략 C로 이동 │
│ ➜ commit 1d7dec1 백포트 컴파일 또는 vendor 패키지 적용 │
└─────────────────────────────────────────────────────────┘
패치 전에도 공격면을 0으로 만들 수 있는 것이 옵션 A입니다. 취약점 트리거가 RFC1867 업로드 진행 콜백에만 의존하기 때문입니다.
2. 옵션 A: php.ini 무중단 강화 (권장)
2.1 전역 설정
; /etc/php/8.3/fpm/php.ini
; RFC1867 업로드 진행 추적 콜백 비활성화
session.upload_progress.enabled = Off
session.upload_progress.cleanup = On
설정 위치는 배포판마다 다릅니다.
php --ini # 로드되는 php.ini 경로 확인
php-fpm -i | grep -i 'Loaded Configuration File'
2.2 FPM 풀 단위 강제
; /etc/php/8.3/fpm/pool.d/www.conf
php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[session.upload_progress.prefix] = ""
php_admin_flag는 애플리케이션의 ini_set()으로 되돌릴 수 없으므로, 멀티테넌트 호스팅에서 특히 중요합니다.
2.3 무중단 리로드와 검증
# systemd 기반
sudo systemctl reload php8.3-fpm
sudo systemctl status php8.3-fpm --no-pager
# PID 직접 시그널 (USR2 = graceful reload)
sudo kill -USR2 "$(cat /var/run/php-fpm.pid)"
# CLI 검증
php -r 'echo "upload_progress=", ini_get("session.upload_progress.enabled"), PHP_EOL;'
# FPM 런타임 검증 (임시 phpinfo 페이지 또는)
php-fpm -tt 2>&1 | grep -i upload_progress
리로드 후 upload_progress= 뒤에 빈 문자열 또는 0이 출력되어야 합니다. FPM의 경우 Local Value와 Master Value를 모두 확인하십시오.
2.4 왜 파일 업로드가 깨지지 않는가
session.upload_progress는 PHP 5.4에서 도입된 레거시 진행률 표시 기능입니다. 실제 업로드 파이프라인($_FILES, move_uploaded_file, 임시 디렉터리)과는 분리되어 있습니다. 최신 SPA는 브라우저 File API와 XHR 진행 이벤트를 사용하므로 기능 영향이 없습니다.
3. 옵션 B: Nginx / CDN 경계 필터링
백엔드 설정을 만질 수 없는 경우의 차선책입니다. 다만 근본 수정이 아니므로 우회 가능성을 전제로 해야 합니다.
# /etc/nginx/conf.d/php2shell.conf
map $http_content_type $php2shell_multipart {
default 0;
"~*multipart/form-data" 1;
}
server {
# 파일 업로드가 필요 없는 공개 엔드포인트에서 본문 상한 축소
client_max_body_size 2m;
client_body_buffer_size 128k;
location ~ \.php$ {
# PHP_SESSION_UPLOAD_PROGRESS 헤더/쿼리 차단 (보조 수단)
if ($arg_PHP_SESSION_UPLOAD_PROGRESS) {
return 403;
}
if ($http_php_session_upload_progress) {
return 403;
}
# 업로드 전용 location 이외에서는 multipart 자체를 거부
if ($php2shell_multipart = 1) {
return 415;
}
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
# 업스트림이 비정상 응답 반복 시 빠르게 차단
proxy_next_upstream off;
}
}
sudo nginx -t && sudo systemctl reload nginx
Cloudflare를 사용하는 경우 WAF 커스텀 규칙에서 Content-Type이 multipart/form-data이면서 PHP_SESSION_UPLOAD_PROGRESS 문자열을 포함하는 요청을 차단할 수 있습니다. 단, 공격자는 파라미터 이름을 인코딩하거나 분할 전송할 수 있으므로 보조 계층으로만 취급하십시오.
4. 옵션 C: EOL PHP 8.1 백포트 (commit 1d7dec1)
PHP 8.1은 upstream EOL로 공식 릴리스가 없습니다. 코어 팀이 제공한 최소 완화 커밋 1d7dec1은 두 가지를 변경합니다.
4.1 핵심 변경
/* ext/session/session.c — 콜백 무조건 비활성화 */
- if (!PS(rfc1867_enabled)) {
+ if (!PS(rfc1867_enabled) || true) {
return retval;
}
/* Zend/zend_alloc.c — OOM 발생 시 즉시 종료 */
- zend_error_noreturn(E_ERROR, "Allowed memory size exhausted");
+ _exit(1);
두 번째 변경은 OOM이나 정수 오버플로 시 워커를 즉시 종료하여, UAF와 힙 그루밍이 성립할 시간 창을 제거합니다. 가용성보다 무결성을 우선하는 선택이며, FPM pm.max_children과 재시작 임계값을 함께 조정해야 합니다.
4.2 빌드 절차
sudo apt build-dep php8.1 -y 2>/dev/null || \
sudo dnf builddep php -y
git clone --depth 1 -b PHP-8.1-CVE-2026-109618-109619 \
https://github.com/php/php-src.git
cd php-src
./buildconf --force
./configure \
--prefix=/usr/local/php81-patched \
--enable-fpm \
--with-fpm-user=www-data \
--with-fpm-group=www-data \
--with-openssl \
--with-zlib \
--enable-mbstring
make -j"$(nproc)"
sudo make install
sudo /usr/local/php81-patched/sbin/php-fpm -t
4.3 배포 전 확인
/usr/local/php81-patched/bin/php -v
/usr/local/php81-patched/bin/php -r \
'echo ini_get("session.upload_progress.enabled"), PHP_EOL;'
systemctl daemon-reload
systemctl restart php81-patched-fpm
커스텀 빌드는 보안 업데이트 책임이 전적으로 운영팀에 있습니다. 가능하면 Ubuntu Pro / CloudLinux / Debian LTS의 vendor backport를 우선하고, 커스텀 빌드는 최후 수단으로 사용하십시오.
5. 운영 FAQ
Q1. session.upload_progress.enabled = Off가 레거시 업로드 폼을 깨뜨릴 수 있나요?
진행률 표시줄만 사라집니다. 업로드 성공/실패, 파일 크기 제한, MIME 검사는 그대로 동작합니다.
Q2. 완화 조치가 실제로 적용되었는지 어떻게 확신하나요?
CLI에서는 php -r 'var_dump(ini_get("session.upload_progress.enabled"));', FPM에서는 임시 phpinfo() 페이지로 Local/Master 값을 모두 확인합니다. 또한 Suricata/Falco 규칙(별도 문서)으로 콜백 트리거 시도를 모니터링하십시오.
Q3. WAF 본문 크기 제한만으로 충분하지 않은 이유는? PHP2Shell 페이로드는 450바이트 안팎으로 매우 작습니다. 2MB, 5MB 상한은 이런 초소형 요청을 전혀 걸러내지 못합니다. 엔진 레벨 비활성화가 유일하게 확실한 차단입니다.
Q4. PHP 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35로 올리면 완전히 안전한가요?
공개된 두 CVE에 대해서는 수정이 포함됩니다. 다만 업그레이드 후에도 session.upload_progress를 사용하지 않는다면 비활성화를 유지하는 것이 공격면 최소화에 유리합니다.
6. 롤백 계획
구성 변경은 반드시 버전 관리하고, 다음 순서로 롤백 가능성을 확보하십시오.
sudo cp /etc/php/8.3/fpm/php.ini /etc/php/8.3/fpm/php.ini.bak-$(date +%F)
# 변경 적용 후 문제 발생 시
sudo cp /etc/php/8.3/fpm/php.ini.bak-2026-10-23 /etc/php/8.3/fpm/php.ini
sudo systemctl reload php8.3-fpm
무중단 리로드는 기존 연결을 유지하지만, opcache와 세션 저장소 구성에 따라 첫 요청 지연이 발생할 수 있습니다. 트래픽이 낮은 시간대에 적용하고, 리로드 전후로 5xx 비율을 모니터링하십시오.