사용자 업로드 파일, 그 안에 악성 코드가 숨어 있다면? 단순한 파일 업로드 기능이 여러분의 시스템을 무너뜨릴 수 있습니다.
안녕하세요, ICT Leader입니다. 오늘은 자바(Spring Boot) 환경에서 흔히 구현되는 "파일 업로드" 기능을 통해 보안의 맹점을 짚어보려 합니다. 실제로 최근 고객사에서 파일 업로드로 인해 웹쉘(web shell)이 삽입되고 서버 전체가 위협받은 사례가 있었습니다. 문제는 이 기능이 너무 흔하게 사용되며, 기본 설정만으로는 취약점이 그대로 노출된다는 점이죠. 자바 11, Spring Boot 환경에서 어떻게 하면 파일 업로드를 안전하게 구성할 수 있을지, 함께 알아보시죠.
바로가기 목차
1. 파일 업로드 취약점이란?
파일 업로드 취약점은 사용자가 서버로 전송하는 파일에 대한 검증이 미흡할 경우 발생합니다. 공격자는 이미지, PDF처럼 보이는 파일 안에 실제로는 웹쉘, 스크립트, 실행파일 등을 삽입해 서버에서 실행되도록 유도합니다. 특히 업로드 경로가 외부에 노출되거나, 업로드된 파일을 서버에서 바로 실행하는 구조라면 치명적인 결과를 초래할 수 있습니다.
2. 실무에서 발견된 사례
구분 | 사례 설명 |
---|---|
공공기관 A | PDF 파일로 위장한 웹쉘 업로드 → JSP 파일 실행 → 내부망 접근 시도 |
스타트업 B | 이미지 확장자를 가진 파일에 JavaScript 삽입 → 프론트엔드에서 XSS 발생 |
전문 쇼핑몰 C | 업로드된 파일을 공개 폴더에 저장 → 외부 접근 통해 악성파일 유포 |
3. 취약한 업로드 코드 예제
다음은 Spring Boot에서 흔히 사용되는 파일 업로드 예제입니다. 겉보기에는 문제가 없어 보이지만, 파일에 대한 필터링이나 제한이 전혀 없기 때문에 치명적인 취약점을 유발할 수 있습니다.
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
String fileName = file.getOriginalFilename();
File dest = new File("C:/upload/" + fileName);
file.transferTo(dest);
return "업로드 성공";
}
이 코드에는 파일명 검증 없음, 확장자 필터링 없음, 업로드 경로 고정, 중복 파일 덮어쓰기 가능성이라는 네 가지 위험 요소가 존재합니다. 특히, JSP나 .exe 파일 등이 그대로 서버에 저장되고 실행될 수 있는 구조이기 때문에, 보안 상 매우 치명적입니다. 이를 방지하기 위해서는 반드시 다음 단계에서 소개할 "안전한 업로드 구현"
4. 안전한 업로드 코드 구현
이제는 취약점을 방지할 수 있는 안전한 파일 업로드 방식의 예제를 살펴보겠습니다. MIME 타입 검증과 확장자 필터링, 저장 경로 격리, UUID 난수화 파일명 적용, 중복 방지 등의 필수 보안 조치를 반영한 코드입니다.
@PostMapping("/secure-upload")
public String secureUpload(@RequestParam("file") MultipartFile file) throws IOException {
String fileName = Paths.get(file.getOriginalFilename()).getFileName().toString();
// 확장자 체크
if (!fileName.matches("(?i).*\\.(png|jpg|jpeg|pdf)$")) {
throw new IOException("허용되지 않은 파일 형식입니다.");
}
// MIMW 타입 확인
String mimeType = Files.probeContentType(file.getResource().getFile().toPath());
if (mimeType == null || !mimeType.startsWith("image/")) {
throw new IOException("유효하지 않은 MIME 타입입니다.");
}
// 저장 경로 분리
Path uploadPath = Paths.get("D:/secure/upload/");
if (!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
// UUID로 중복 방지
String newFileName = UUID.randomUUID().toString() + "_" + fileName;
Path fullPath = uploadPath.resolve(newFileName);
file.transferTo(fullPath);
return "업로드 완료";
}
위 코드는 다음과 같은 보안 요소를 포함하고 있습니다:
- 파일명에서 디렉토리 경로 제거 (경로 탐색 공격 방지)
- 확장자 및 MIME 타입 이중 필터링
- 서버 실행 영역과 다른 디렉토리로 격리
- UUID로 파일명 충돌 및 덮어쓰기 방지
Spring Boot 설정을 통해 이 디렉토리의 정적 접근을 막으면 더욱 안전한 시스템을 구성할 수 있습니다.
5. 개발자가 지켜야 할 체크리스트
점검 항목 | 설명 |
---|---|
확장자 필터링 | .exe, .jsp, .php 등 위험 확장자 차단 |
MIME 타입 확인 | 브라우저와 서버 측 모두에서 검증 수행 |
파일명 UUID 처리 | 중복 방지 및 원본 파일명 은닉 |
디렉토리 접근 제어 | 업로드 폴더는 정적 자원 경로에서 제외 |
입력값 검증 | 파일명, 크기, 컨텐츠 등 사전 필터링 수행 |
6. 조직에서 적용할 정책 가이드
- 보안개발 표준에 파일 업로드 시 필수 필터링 항목 명시
- DevOps 단계에서 정적분석 도구로 업로드 로직 검사
- 정책 변경 시 CI/CD 파이프라인 테스트 케이스 반영
- 업로드 허용 파일은 관리자 정책 승인 후 운영 배포
- 로그 기록 필수화 및 시도 횟수 초과 시 탐지 로직 구현
궁극적으로 보안은 기술만이 아닌 문화와 습관입니다. 파일 업로드 기능 하나라도 "조직 전반의 보안 체계" 속에서 운영되도록 만드는 것이 중요합니다.
7. 실무 Q&A – 파일 업로드 보안
충분하지 않습니다. 공격자는 파일명을 위조하거나 확장자 우회를 시도할 수 있기 때문에, MIME 타입과 콘텐츠 분석까지 병행해야 실질적인 방어가 가능합니다.
네, PNG나 JPG 파일에도 스크립트를 삽입할 수 있습니다. 브라우저가 이를 실행할 수 있기 때문에, 이미지 또한 MIME 타입 및 확장자 검증을 철저히 해야 합니다.
업로드 경로를 실행 가능 디렉터리 밖으로 분리하고, 웹 서버 설정(.htaccess, nginx location 등)을 통해 접근을 제한하거나 실행 권한을 제거해야 합니다.
application.properties
또는 application.yml
에서 spring.servlet.multipart.max-file-size=10MB
와 같은 설정을 통해 제한할 수 있습니다.
웹서버에서 해당 MIME 또는 확장자를 실행하지 않도록 설정하거나, 업로드된 파일에 실행 권한을 부여하지 않도록 시스템 설정을 병행해야 합니다.
지금까지 자바와 Spring Boot 환경에서의 파일 업로드 취약점 대응 방법에 대해 알아봤습니다. 보안은 소스코드 한 줄, 설정 한 줄에서부터 시작됩니다. 일상적인 기능처럼 보이는 파일 업로드도 실전에서는 기업 전체를 위협할 수 있는 보안 이슈로 확산가능합니다.
여러분의 시스템과 데이터, 그리고 고객의 신뢰를 지키기 위해 지금 이 가이드를 코드에 적용해 보세요. 댓글이나 피드백으로 여러분의 의견도 함께 나누어 주신다면 더욱 풍성한 보안 지식의 장이 열릴 것입니다.
'시큐어코딩 > JAVA' 카테고리의 다른 글
[시큐어코딩] Java에서 NullPointerException 방지하는 안전한 코딩 전략 (2) | 2025.04.09 |
---|---|
[시큐어코딩]Java에서 CSRF(크로스사이트요청위조) 취약점 완벽 대응 가이드 (0) | 2025.04.09 |
[시큐어코딩]Java에서 서버사이드 요청 위조(SSRF) 완벽 대응 가이드 (0) | 2025.04.08 |
[시큐어코딩]Java에서 XSS(크로스 사이트 스크립트) 취약점 완벽 대응 가이드 (0) | 2025.04.06 |
[시큐어코딩]Java에서 SQL Injection 대응 완벽 가이드 (0) | 2025.04.04 |