테마
07. 파일 업로드·다운로드·경로 탐색
파일 기능은 입력 검증, 저장소 설계, 브라우저 응답 헤더, 접근 통제가 한 번에 섞이는 영역이다. 그래서 취약점도 한 가지가 아니라 여러 층에서 함께 생긴다.
학습 목표
- 파일 업로드와 다운로드에서 왜 취약점이 자주 생기는지 설명할 수 있다.
- 안전한 저장 위치, 파일명 생성, 형식 검증, 응답 헤더 설정 원칙을 이해한다.
- 경로 탐색(Path Traversal)과 안전한 다운로드 설계 차이를 설명할 수 있다.
- 오래된 확장자 우회 사례보다 현재 실무에서 더 중요한 방어 포인트를 파악할 수 있다.
1. 파일 기능의 위험 표면
파일 기능에서 동시에 봐야 할 것은 다음과 같다.
- 업로드된 콘텐츠 자체
- 사용자가 보낸 파일명과 경로 정보
- 파일을 저장하는 위치
- 파일을 다시 내려줄 때의 응답 헤더
- 그 파일을 볼 권한이 있는지 여부
2. 안전한 업로드의 기본 원칙
2.1 서버가 파일명을 만든다
사용자가 보낸 원본 파일명은 표시용 메타데이터로만 저장하고, 실제 저장 파일명은 서버가 생성하는 편이 안전하다.
- 랜덤 ID
- UUID
- 시간 + 난수 조합
이렇게 해야 이름 충돌과 경로 조작 위험을 줄일 수 있다.
2.2 저장 위치를 격리한다
가장 중요한 원칙 중 하나다.
- 웹 루트 밖에 저장
- 실행 가능한 확장자가 동작하지 않는 저장소 사용
- 가능하면 별도 정적 도메인이나 오브젝트 스토리지를 사용
원문처럼 "업로드 경로를 잘 검증하자"도 중요하지만, 더 근본적인 방어는 실행되지 않는 위치에 저장하는 것이다.
가능하다면 메인 서비스 도메인과 업로드 서빙 도메인의 쿠키 범위도 분리하는 편이 좋다.
2.3 형식 검증은 한 겹으로 끝내지 않는다
| 검증 항목 | 이유 |
|---|---|
| 허용 확장자 | 가장 기본적인 정책 |
| MIME 타입 | 브라우저와 서버가 보는 타입 확인 |
| 매직 바이트 | 파일 내부 시그니처 확인 |
| 크기 제한 | 자원 고갈 방지 |
| 이미지 재인코딩 | 메타데이터와 숨은 페이로드 제거 도움 |
확장자만 보는 방식은 충분하지 않다.
3. 특히 주의할 파일 유형
- SVG: 이미지처럼 보여도 스크립트와 외부 참조가 들어갈 수 있다
- HTML: 다운로드가 아니라 렌더링되면 바로 XSS 면이 된다
- PDF, Office 문서: 별도 악성 콘텐츠 검토가 필요할 수 있다
- 압축 파일: 압축 폭탄, 경로 탈출, 내부 스크립트 포함 가능성 고려
즉, "이미지 업로드"라면 가능한 한 실제 비트맵 이미지 포맷만 허용하는 편이 안전하다.
정말 SVG가 필요하다면 Sanitizer 정책과 별도 서빙 정책을 함께 검토해야 한다.
4. 다운로드 기능은 경로가 아니라 식별자로 설계한다
안전한 다운로드는 보통 이렇게 설계한다.
- 사용자는 파일 ID를 요청한다
- 서버는 DB에서 파일 메타데이터를 조회한다
- 현재 사용자가 그 파일에 접근할 권한이 있는지 확인한다
- 서버가 내부 저장 경로를 결정해서 응답한다
이 방식이 중요한 이유는 사용자 입력을 파일 경로로 직접 해석하지 않기 때문이다.
반대로 위험한 방식은 다음과 같다.
?path=/uploads/...?filename=../../...- 사용자 입력을 그대로
storagePath + filename으로 연결
5. 경로 탐색을 줄이는 응답 전략
사용자 제공 파일을 내려줄 때는 브라우저가 실행하지 않게 하는 응답 헤더도 중요하다.
http
Content-Disposition: attachment
Content-Type: application/octet-stream
X-Content-Type-Options: nosniff이 조합은 특히 사용자 업로드 파일을 렌더링하지 말아야 할 때 유용하다.
attachment로 강제 다운로드octet-stream으로 일반 바이너리 취급nosniff로 브라우저 추측 실행 방지
6. basename()은 도움이 되지만 만능은 아니다
일부 프레임워크 가이드에서는 basename()처럼 파일명에서 경로 정보를 제거하는 방식을 보여 준다.
이 방법은 분명 도움은 되지만, 이것만으로 전체 설계를 대신할 수는 없다.
더 좋은 기준은 다음과 같다.
- 애초에 사용자 입력 파일명을 경로 결정에 사용하지 않기
- 내부 저장소 키는 서버가 결정하기
- 다운로드는 식별자 기반으로 매핑하기
즉, 문자열 정리 함수는 보조 수단이고, 구조적으로 경로를 노출하지 않는 설계가 더 중요하다.
7. 원문을 현재 기준으로 보완하면
| 원문 포인트 | 현재 기준 보강 |
|---|---|
| 확장자 화이트리스트 | MIME, 매직 바이트, 재인코딩까지 확장 |
| 실행 가능 경로 차단 | 웹 루트 밖 또는 별도 저장소 분리까지 포함 |
| 파일 다운로드 경로 문자 차단 | 식별자 기반 조회와 권한 검증을 우선 |
| 오래된 서버별 우회 사례 | 현재는 SVG, HTML, MIME sniffing, 정적 도메인 분리가 더 중요 |
8. PR 리뷰 체크리스트
- 실제 저장 파일명을 서버가 생성하는가
- 업로드 파일이 실행되지 않는 위치에 저장되는가
- 허용 확장자, MIME, 매직 바이트 검증이 있는가
- 이미지 업로드는 재인코딩이나 메타데이터 제거를 검토했는가
- 다운로드는 파일 ID 기반이며 권한 검증을 거치는가
- 사용자 업로드 응답에
Content-Disposition: attachment와nosniff를 검토했는가
핵심 정리
- 파일 기능은 입력 검증만이 아니라 저장 구조와 응답 헤더까지 함께 봐야 한다
- 실제 저장 파일명은 서버가 만들고, 파일은 실행되지 않는 위치에 보관하는 것이 기본이다
- 다운로드는 경로 입력이 아니라 식별자와 권한 검증 중심으로 설계해야 한다
- 현재 실무에서는 오래된 우회 기법 암기보다 격리 저장, 안전한 헤더, 형식 3중 검증이 더 중요하다