PHP 웹셸 취약점, 왜 계속 터질까 — React·TypeScript·PostgreSQL 기반 LMS가 구조적으로 안전한 이유
PHP 웹셸(PHPshell)은 업로드된 .php 파일이 서버에서 그대로 실행되면서 시작됩니다. 공격이 성립하는 4단계를 기술적으로 풀고, React·TypeScript·PostgreSQL로 만든 WEBHEADS LMS가 각 단계를 어떻게 끊어 내는지 설명합니다.
한 줄 요약: PHP 웹셸 공격은 '공격자가 올린 파일이 웹 서버에서 코드로 실행되는 것'에서 시작합니다. WEBHEADS(웹헤즈) LMS는 React·TypeScript로 만든 화면을 정적 파일로 배포하고, 데이터는 PostgreSQL과 서버 함수로만 다루기 때문에, 웹 서버가 업로드 파일을 PHP 코드로 실행하는 경로 자체가 없습니다. 이 글은 보안 담당자가 그대로 검토할 수 있도록 공격 흐름과 방어 구조를 단계별로 정리합니다.
웹셸이란 무엇인가: 웹셸은 웹 서버에 올려진 작은 스크립트로, 브라우저에서 명령을 보내면 서버 권한으로 실행해 주는 뒷문입니다. PHP 환경에서는 eval(), system(), passthru(), assert() 같은 함수 한 줄만 있어도 웹셸이 됩니다. 확장자를 .php.jpg, .phtml, .phar로 바꾸거나, 이미지 파일 끝에 코드를 붙이는 방식으로 업로드 검사를 우회하는 사례가 꾸준히 보고됩니다. 실제로 저희 사이트에도 /gnu/bbs/, sns_send.php 같은 그누보드 계열 주소를 찾는 자동 스캐너 접속이 한 달 사이 수천 건 들어왔고, 이는 지금도 PHP 기반 사이트를 노리는 공격이 활발하다는 뜻입니다.
공격이 성립하는 4단계: ① 업로드 — 게시판·과제 제출·프로필 이미지 같은 업로드 기능으로 스크립트 파일을 올립니다. ② 저장 위치 노출 — 파일이 웹 루트(/data/file/ 등) 아래에 저장되어 URL로 바로 접근할 수 있습니다. ③ 실행 — Apache mod_php나 PHP-FPM이 확장자나 설정에 따라 그 파일을 PHP 코드로 해석해 실행합니다. ④ 확산 — 웹 서버 계정 권한으로 설정 파일(DB 접속 정보)을 읽고, DB 전체를 덤프하거나 같은 서버의 다른 사이트까지 건드립니다. 네 단계 중 하나만 끊어도 공격은 실패합니다. 문제는 전통적인 PHP 구조에서는 ②와 ③이 기본 동작이라는 점입니다.
WEBHEADS LMS가 각 단계를 끊는 방식: ① 업로드 — 파일은 웹 서버 디스크가 아니라 분리된 객체 저장소로 바로 들어가고, 저장할 때 파일 이름을 무작위 값으로 바꿉니다. ② 저장 위치 — 저장소는 공개 웹 루트와 분리되어 있고, 비공개 자료는 짧은 시간만 유효한 서명 주소로만 내려받을 수 있습니다. ③ 실행 — 저장소는 파일을 '내려주기만' 할 뿐 어떤 파일도 실행하지 않습니다. 화면을 그리는 서버 역시 미리 빌드된 HTML·JS 파일만 전달하는 정적 배포라, PHP 해석기가 아예 존재하지 않습니다. ④ 확산 — 브라우저는 DB 접속 비밀번호를 갖지 않고, 모든 요청은 PostgreSQL의 행 단위 보안(RLS) 정책을 통과해야 하므로, 설령 한 계정이 탈취되어도 그 계정이 볼 수 있는 행 이상은 읽을 수 없습니다.
TypeScript가 보안에 주는 효과: TypeScript는 그 자체가 보안 도구는 아니지만, 요청 값의 형태를 컴파일 단계에서 강제하기 때문에 '숫자가 와야 할 자리에 문자열이 들어와 쿼리가 깨지는' 류의 실수를 크게 줄입니다. 여기에 서버 함수에서 입력 검증 스키마를 한 번 더 적용하고, DB 접근은 매개변수 바인딩 방식만 사용해 문자열을 이어 붙이는 동적 SQL을 쓰지 않습니다. PHP 레거시에서 흔한 mysql_query("... '$_GET[id]'") 같은 패턴이 구조적으로 들어올 자리가 없습니다.
React가 막아 주는 것과 막지 못하는 것: React는 화면에 값을 출력할 때 기본으로 HTML 이스케이프를 하기 때문에 저장형 XSS의 상당 부분을 기본값으로 막습니다. 다만 dangerouslySetInnerHTML처럼 원문 HTML을 넣는 곳은 예외이므로, WEBHEADS는 강의 소개·공지처럼 서식이 필요한 영역에만 허용 목록 기반 정화(sanitize)를 거친 뒤 출력합니다. '프레임워크가 다 막아 준다'고 말하지 않고, 예외 지점을 목록으로 관리하는 것이 실제 보안 운영입니다.
보안 담당자를 위한 점검 질문: 도입하려는 LMS가 어떤 기술이든 아래 다섯 가지를 확인하면 웹셸 위험을 빠르게 가늠할 수 있습니다. ① 업로드 파일이 웹 루트 아래에 저장되는가 ② 업로드 경로에서 스크립트 실행이 꺼져 있는가 ③ DB 접속 정보가 웹 서버 파일에 평문으로 있는가 ④ 고객사마다 서버·DB가 분리되어 있는가 ⑤ 관리자 화면에 IP 제한과 2단계 인증이 있는가. WEBHEADS는 다섯 항목 모두 '아니오-예-아니오-예-예'로 답하며, 상세 기준과 제공 범위는 보안 센터 페이지에 공개해 두었습니다.
자주 묻는 질문 — PHP를 쓰면 무조건 위험한가요? 그렇지 않습니다. 최신 PHP와 잘 관리된 프레임워크는 충분히 안전하게 운영할 수 있습니다. 다만 10년 넘은 레거시 PHP 솔루션은 업로드 경로 실행, 평문 설정 파일, 공용 서버 같은 위험이 기본값으로 남아 있는 경우가 많아 관리 부담이 큽니다. WEBHEADS가 React·TypeScript·PostgreSQL을 택한 이유는 '안전한 상태가 기본값'이 되는 구조를 원했기 때문입니다.
관련 글
주식회사 웹헤즈 · 대표전화 02-540-4337 · 도입 문의