“PHP2Shell 터졌다던데 우리 사이트는 괜찮나요?” 요즘 이런 문의가 부쩍 늘었습니다. 서버의 PHP나 Next.js를 바로 업데이트할 수 있으면 가장 좋지만, 운영 중인 사이트는 업데이트에 시간이 걸리거나 아예 패치가 나오지 않는 버전을 쓰는 경우도 많습니다. 이럴 때 웹 방화벽이 어디까지 도움이 되는지를 사실대로 정리했습니다.
결론부터 말하면 이렇습니다.
- 최근 크게 이슈가 된 원격 코드 실행(RCE) 취약점의 공격 요청에는 눈에 띄는 모양이 있고, 그 모양을 웹 방화벽에서 막을 수 있습니다.
- 다만 알려진 모양만 막습니다. 변형된 공격이나 요청 모양이 공개되지 않은 취약점은 웹 방화벽만으로 막을 수 없습니다.
- 그래서 웹 방화벽은 패치를 대신하는 것이 아니라 패치하기 전까지의 공백을 줄이는 방어층입니다.
최근 이슈가 된 취약점 세 가지
| 이름 | 대상 | 식별 번호 | 심각도 | 해결 |
|---|---|---|---|---|
| PHP2Shell | PHP 자체 (5.4 이후 여러 버전) | CVE-2026-109618, CVE-2026-109619 | 9.2 (CVSS 4.0) | 2026-10-21 패치: PHP 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 |
| React2Shell | React Server Components, Next.js 15·16 | CVE-2025-55182 (Next.js는 CVE-2025-66478) | 10.0 | 2025-12 패치: React 19.0.1 / 19.1.2 / 19.2.1, Next.js 15.0.5~16.0.7 이상의 패치본 |
| Next.js 10월 보안 업데이트 | Next.js 16 캐시 기능, 이미지(SVG) 처리 | GHSA-673q-mjm6-r5xr, CVE-2026-96889 | 치명적 2건 | 2026-10-14 패치 (자료에 따라 15.5.28 / 16.4.1 이상) |
세 가지 모두 서버에서 원격으로 코드를 실행할 수 있는 종류입니다. PHP2Shell과 React2Shell은 공개 직후 실제 공격이 관측됐습니다. PHP 8.1 이하 계열에는 공식 패치가 없다는 점도 여러 보안 업체가 지적했습니다.
공격 요청은 어떤 모양인가
웹 방화벽은 요청의 내용을 보고 판단하므로, 공격 요청이 정상 요청과 구분되는 특징이 있는지가 핵심입니다.
PHP2Shell
여러 보안 업체의 분석에서 공통적으로 확인된 특징입니다.
- 알려진 트리거가 모두 파일 업로드 형식(
multipart/form-data) 요청입니다. - 세션 업로드 진행률 필드(
PHP_SESSION_UPLOAD_PROGRESS)를 이용하는 요청, 필드 이름이 대괄호로 수십 단계 중첩되거나 비어 있는 파일 필드가 수백 개씩 들어간 요청이 관측됐습니다. - 공개된 공격 도구는 User-Agent나 multipart 경계 문자열에 이름을 드러냅니다.
React2Shell
- POST 요청에
Next-Action헤더가 있고, multipart 필드에$1:__proto__:then,$1:constructor:constructor,resolved_model같은 React 내부 구조를 흉내 낸 문자열과 Node.js 코드 실행 구문(process.mainModule등)이 중첩 객체 형태로 들어갑니다. - 이런 문자열은 정상적인 서버 액션 요청에는 나오지 않습니다.
(Datadog Security Labs 분석 기준)
Next.js 10월 보안 업데이트
- 한 건은 이미지(SVG) 처리, 다른 한 건은 캐시 기능을 악용하는 취약점입니다. 현재 공개된 자료에는 웹 방화벽이 걸러낼 만한 요청 모양이 나와 있지 않습니다.
우리 웹 방화벽에서 실제로 막힌 것
공격 요청을 직접 만들어 시험 환경의 웹 방화벽(차단 모드)에 보낸 결과입니다. 실제 공격이 성공하는지가 아니라 요청이 사이트까지 전달되는지를 확인한 시험입니다.
| 시험 요청 | 결과 |
|---|---|
PHP2Shell: 진행률 필드(PHP_SESSION_UPLOAD_PROGRESS)가 들어간 업로드 |
차단 |
| PHP2Shell: 공격 도구의 User-Agent·경계 문자열 | 차단 |
| PHP2Shell: 필드 이름이 10단계 이상 중첩 / 필드 500개 이상 / 파일 필드 100개 이상 | 차단 |
React2Shell: Next-Action 헤더 + React 내부 구조를 흉내 낸 multipart |
차단 |
Node.js 프로토타입 오염 (__proto__, constructor.prototype를 담은 JSON) |
차단 |
Node.js 명령 주입 (child_process, process.mainModule 구문) |
차단 |
| 템플릿 구문 삽입, Log4Shell, Spring4Shell 같은 기존 취약점 | 차단 |
| 정상 업로드, 정상 JSON 요청, 정상 서버 액션 요청 | 통과 |
처음 두 줄은 새 취약점이 공개된 뒤 공격 요청 모양을 분석해 규칙으로 추가한 것입니다(이를 흔히 “가상 패치”라고 부릅니다). 나머지는 기본 규칙이 이미 막고 있던 공격입니다. React2Shell은 기본 규칙에도 걸렸고, 별도 규칙을 더해 같은 모양이 계속 막히도록 이중으로 두었습니다.
규칙마다 사이트별로 끄고 켤 수 있습니다. 예를 들어 파일 업로드 진행률 표시 기능을 정상적으로 쓰는 사이트는 해당 규칙만 예외로 두고, 나머지 방어는 그대로 유지합니다.
웹 방화벽이 막지 못하는 것
이 부분이 가장 중요합니다.
- 알려지지 않은 취약점: 공격 요청 모양이 공개되지 않은 취약점(위 Next.js 10월 업데이트가 현재 그렇습니다)은 규칙을 만들 수 없습니다.
- 변형된 공격: 문자를 쪼개거나 인코딩을 바꿔 규칙을 피하는 변형은 막지 못할 수 있습니다. React2Shell도 분석 업체들이 공격 페이로드가 계속 바뀌었다고 보고했습니다.
- 정상 모양의 요청으로 일으키는 공격: 요청 자체는 평범하지만 서버의 약점을 건드리는 공격은 요청만 봐서는 구분할 수 없습니다.
- 이미 침투한 뒤의 행동: 웹쉘이 심어진 뒤의 접속은 별도의 점검이 필요합니다.
그래서 “웹 방화벽만 있으면 최신 취약점도 안전하다”는 말은 사실이 아닙니다.
그럼 어떻게 해야 하나
순서가 중요합니다.
- 패치할 수 있으면 먼저 패치합니다. PHP는 위 버전 이상으로, Next.js·React는 패치된 버전으로 올립니다. 이것이 유일한 근본 해결책입니다.
- 패치가 어려운 경우 (오래된 PHP 8.1 이하, 호환성 때문에 바로 올릴 수 없는 사이트): 웹 방화벽의 가상 패치와 서버 설정 완화(예: PHP의
session.upload_progress.enabled=0, 다만 일부 경로만 막는 임시 조치)를 함께 적용하고, 업그레이드 계획을 세웁니다. - 패치한 뒤에도 웹 방화벽은 유지합니다. 다음 취약점이 공개되고 패치가 적용되기까지는 늘 시간이 걸립니다.
- 취약한 상태로 노출됐다면 침투 여부를 점검하세요. 소스와 DB에 웹쉘이나 변조가 있는지 검사하고, 의심되면 깨끗한 새 서버로 옮기는 것을 권합니다. React2Shell은 공개 직후부터 실제 악용이 있었기 때문에 패치 전에 노출됐던 사이트는 비밀 키 교체까지 안내하는 자료도 있었습니다.
TCP-80.NET의 웹 방화벽은
사이트 앞단에서 요청을 검사하고, 위 표의 공격 유형을 차단합니다. 새 취약점이 공개되면 요청 모양을 분석해 규칙을 추가하고, 사이트별로 모드(탐지만/차단)와 예외를 조정합니다. 처음에는 탐지만 하며 오탐을 정리한 뒤 차단으로 전환하는 방식으로 운영합니다.
웹 방화벽은 해킹 복구·이전 서비스의 구성 요소로 제공합니다. 어떤 공격을 막는지는 웹 방화벽 안내에서 확인하세요. 사용 중인 PHP·Next.js 버전과 사이트 종류를 텔레그램 @tcp80net으로 알려주시면 적용 방식을 안내해 드립니다.