파일 업로드·경로·명령 처리 보안
외부 파일과 경로·명령 입력의 검증 및 권한 경계를 이해합니다.
1. 세 취약점의 차이
| 구분 | 외부 입력이 바꾸는 것 | 대표 결과 |
|---|---|---|
| 제한 없는 파일 업로드 | 저장되는 내용·형식·위치 | 악성 파일 저장, Web Shell, 저장공간 고갈 |
| Path Traversal | 읽거나 쓸 실제 파일 위치 | 허용 영역 밖 파일 읽기·쓰기·삭제 |
| OS Command Injection | 실행할 명령 구조와 인자 | 임의 명령 실행, 데이터 변조, 서비스 중단 |
이들은 연쇄될 수 있다. 업로드한 파일이 실행 가능한 Web Root에 저장되면 서버 코드 실행으로 이어질 수 있고, 사용자 파일명을 Shell 명령에 연결하면 파일명 입력이 Command Injection으로 바뀔 수 있다.
2. 확장자·Content-Type·파일 시그니처는 단독 방어가 아니다
- 확장자 Allow-list는 필요한 형식만 허용하는 기본 통제다.
Content-Type은 클라이언트가 제공하므로 신뢰할 수 없다.- 파일 시그니처는 형식 확인에 도움을 주지만 파일 전체가 안전함을 보장하지 않는다.
안전한 검증은 여러 신호를 함께 사용한다.
업무상 허용 형식 확인
↓
크기·개수 제한
↓
Server-side 형식과 구조 검사
↓
필요 시 악성코드 검사 또는 안전한 재인코딩
↓
Server가 생성한 저장 Key 사용
↓
실행 불가능한 위치에 저장
↓
인가를 거친 Download Handler로 제공
3. 저장 위치와 제공 방식을 분리한다
다음 원칙이 중요하다.
- 가능한 한 Web Root 밖 또는 별도 저장소에 둔다.
- 업로드 디렉터리에서 스크립트 실행을 허용하지 않는다.
- 사용자가 제시한 파일명은 표시용 Metadata로만 보관한다.
- 실제 저장명은 UUID 등 서버가 생성한 Key를 사용한다.
- 다운로드 시 원본 파일명을 응답 Header에 안전하게 설정한다.
- 파일을 직접 URL로 공개하기보다 서버 측 인가를 거친다.
파일명 변경만으로 악성 내용이 안전해지는 것은 아니다. 저장 위치·실행 권한·제공 MIME·인가를 함께 통제해야 한다.
4. 다운로드에서는 경로 검증과 객체 인가가 모두 필요하다
안전한 흐름은 다음과 같다.
외부 File ID
↓
Server-side Metadata에서 저장 Key 조회
↓
현재 사용자의 소유권·권한 확인
↓
허용 Storage Root 안의 실제 경로 계산
↓
파일을 열어 응답
파일 ID가 길고 예측하기 어렵더라도 객체 권한검사를 대신하지 않는다. 반대로 경로가 허용 Root 안에 있어도 현재 사용자가 그 파일을 읽을 권한이 있다는 뜻은 아니다.
5. Path Traversal
Path Traversal은 외부 입력이 ../, 절대경로, Encoding 변형 등을 통해 허용 디렉터리 밖의 파일을 가리키게 하는 문제다.
취약한 예는 다음과 같다.
base = /srv/files/
input = ../../etc/passwd
result = /srv/files/../../etc/passwd
방어 우선순위는 다음과 같다.
- 외부에서 경로 문자열을 직접 받지 않고 File ID를 사용한다.
- 필요한 경우 정식 경로 API로 Decode·정규화·실제 경로를 계산한다.
- 계산된 경로가 허용 Root 내부인지 구조적으로 확인한다.
- Symlink를 허용할지 정책을 정한다.
- 읽기·쓰기 작업에 최소 권한 계정을 사용한다.
단순히 ../ 문자열을 삭제하는 방식은 중첩 Encoding, 구분자 차이, 절대경로와 Symlink를 빠뜨릴 수 있다.
6. OS Command Injection
OS Command Injection은 외부 입력이 Shell이나 프로그램 인자로 전달되어 명령 구조 또는 옵션 의미를 바꾸는 취약점이다.
가장 강한 대응은 운영체제 명령 호출을 제거하고 언어가 제공하는 Library API를 사용하는 것이다.
파일 복사 → File API
압축 해제 → Archive Library
이미지 변환 → 안전한 Library 또는 격리된 전용 Service
외부 프로그램 호출이 불가피하면 다음을 적용한다.
- 실행 파일을 고정하고 가능하면 절대경로 사용
- 하나의 명령 문자열이 아니라 Argument 배열로 전달
- Shell 해석을 사용하지 않음
- 사용자 선택값은 Allow-list로 제한
- 파일명과 옵션을 구분하고 옵션 주입을 방지
- Timeout·출력 크기·작업 디렉터리·환경변수 제한
- 저권한 전용 계정으로 실행
shell=False는 Shell Metacharacter 해석을 줄이지만, 외부 값이 대상 프로그램의 위험한 옵션이 되는 것까지 자동으로 막지는 않는다.
개념 확인 문제
문제를 누르면 바로 아래에서 정답과 해설을 확인할 수 있습니다.
01Content-Type이 image/png인 업로드 파일은 안전한 이미지인가?
클라이언트가 지정한 값만으로 알 수 없다. 서버에서 형식·구조·크기를 검사하고 안전한 저장·제공 방식과 권한을 적용한다.
02파일이 허용 저장소 안에 있으면 다운로드 인가를 생략할 수 있는가?
아니다. 경로가 허용 영역 안인지와 현재 사용자가 그 파일을 읽을 권한이 있는지는 별도 검사다.