webhacking.kr에서 문제를 풀다 php wrapper와 LFI를 이용하여 푸는 문제(old-25)가 있어서 개념을 한 번 정리해 보려고 합니다.
php에서 래퍼(Wrapper)란, 다양한 종류의 데이터 소스(파일, 압축 파일, 웹 주소 등)를 다룰 때 일관된 방법으로 접근할 수 있도록 감싸주는 인터페이스 또는 핸들러를 의미합니다.
PHP에는 파일을 읽거나 쓸 때 사용하는 fopen(), include() 같은 함수들이 있습니다. 원래 이 함수들은 서버(로컬)에 있는 일반적인 파일을 다루기 위해 만들어 졌습니다.
하지만 개발을 하다 보면 인터넷에 있는 다른 웹 페이지의 내용, 혹은 메모리상의 데이터를 읽어오고 싶을 때가 있습니다. 이때 데이터 소스마다 복잡하게 다른 함수를 새로 만드는 대신, 앞에 특정 프로토콜을 붙여주면, 그 규칙에 맞는 '래퍼'가 알아서 데이터를 가져와 일반 파일처럼 읽을 수 있게 만들어 주는 시스템이 스트림 래퍼(Stream Wrapper) 입니다.
자세한 내용은 php 공식 웹사이트에서 확인하실 수 있습니다. (https://www.php.net/manual/en/wrappers.php)
| 이름 | 기능 |
|---|---|
| file:// | 로컬 파일 시스템 접근 |
| http:// | HTTP(s) URL 접근 |
| ftp:// | FTP(s) URL 접근 |
| php:// | 다양한 입출력(I/O) 스트림 접근 |
| zlib:// | 압축 스트림 |
| data:// | 데이터 (RFC 2397) |
| glob:// | 패턴과 일치하는 경로명 검색 |
| phar:// | PHP 아카이브 |
| ssh2:// | 보안 셸 2 연결 |
| rar:// | RAR 압축 파일 접근 |
| ogg:// | 오디오 스트림 접근 |
| expect:// | 프로세스 상호작용 스트림 |
가장 실무에서 빈번하게 쓰이는 형태입니다. 클라이언트(Vue, React 등 프론트엔드)나 외부 결제 서비스(웹훅)가 application/json 형태로 데이터를 POST 전송할 때, PHP의 기본 $_POST 배열로는 이 데이터를 읽을 수 없습니다. 이때 가공되지 않은 순수 요청 본문(Raw Body)을 읽어오기 위해 사용합니다.
// 외부에서 전송된 JSON 데이터를 읽어서 배열로 변환
$rawData = file_get_contents('php://input');
$requestData = json_decode($rawData, true);
echo "요청받은 사용자 ID: " . $requestData['user_id'];
서버의 하드 디스크에 파일을 굳이 저장하지 않고, 메모리상에서 생성한 데이터를 사용자의 브라우저로 즉시 스트리밍(다운로드)할 때 사용합니다. 서버의 디스크 용량을 절약하고 처리 속도를 크게 높일 수 있습니다.
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="report.csv"');
// 디스크가 아닌 브라우저 출력 스트림을 직접 엽니다.
$output = fopen('php://output', 'w');
// 데이터를 출력 스트림에 직접 씁니다 (바로 다운로드 됨)
fputcsv($output, ['이름', '이메일', '가입일']);
fputcsv($output, ['홍길동', 'hong@test.com', '2026-03-31']);
fclose($output);
수십, 수백 개의 PHP 파일로 이루어진 라이브러리나 프로그램을 하나의 실행 가능한 .phar (PHP Archive) 파일로 묶어서 배포할 때 씁니다.
// phar 파일 압축을 풀지 않고도 내부의 설정 파일을 바로 읽음
$config = include 'phar://my-app.phar/config/database.php';
TCP Wrapper를 이용한 LFI 취약점은 다음 포스트에서 이어서 정리해 보겠습니다!