파일 업로드 기능은 서버에 임의 파일을 쓸 수 있다는 것 자체가 공격 표면이다. 검증이 허술하면 실행 가능한 스크립트(웹쉘)를 올려서 원격 코드 실행으로 직결된다.
흔한 취약 패턴
- 확장자만 검사:
.jpg.php, .php%00.jpg(null byte, 오래된 PHP 버전) 같은 우회
- Content-Type 헤더만 신뢰: 요청 헤더는 클라이언트가 마음대로 조작 가능 — 실제 파일 내용과 무관
- 매직 바이트 미검증: 확장자와 실제 파일 시그니처가 일치하는지 확인 안 함
- 업로드 경로가 웹에서 직접 실행 가능: 웹 루트 안에, 실행 권한이 있는 디렉터리에 그대로 저장
- 원본 파일명을 그대로 사용: 경로 조작이나 덮어쓰기로 이어질 수 있음
탐지 방법
- 허용된 확장자 외에
.php, .jsp, .asp, .phtml 등을 업로드 시도
- 확장자 검증이 있으면 대소문자 변경(
.PHP), 이중 확장자(.php.jpg), null byte, 특수문자 우회 시도
- 업로드된 파일이 실제로 저장되는 경로를 추정(응답에 포함되는 경우가 많음)하고 직접 접근해본다
- 접근된다면 그 경로에서 스크립트가 실행되는지 확인 (단순 정적 파일로 서빙되는지, 아니면 인터프리터가 실행하는지)
- SVG/이미지 업로드는 XSS(SVG 내
<script>)나 XXE(SVG는 XML 기반) 벡터로도 테스트
방어
- 확장자 화이트리스트 + 매직 바이트(실제 파일 헤더) 검증을 같이 한다. 둘 중 하나만으로는 불충분하다.
- 저장 파일명은 서버가 랜덤 생성한다. 사용자가 지정한 파일명/경로를 그대로 신뢰하지 않는다.
- 업로드 디렉터리는 실행 권한 없이, 가능하면 웹 루트 밖이나 별도 스토리지(S3 등)에 둔다. 정적 파일 서빙만 하고 스크립트 인터프리터가 그 경로를 절대 건드리지 않게 한다.
- 파일 크기 제한, 업로드 빈도 제한(Rate limit)도 같이 걸어 DoS성 남용을 막는다.
- 이미지라면 서버에서 재인코딩(리사이즈 등)하는 것도 임베딩된 악성 페이로드를 깨는 데 도움이 된다.