Skip to content

Commit 91e7296

Browse files
authored
Merge pull request #141 from prgrms-aibe-devcourse/feat/auth
feat: 악의적 파일 업로드 방지 구현
2 parents 5b15ae3 + 82c2aa5 commit 91e7296

4 files changed

Lines changed: 73 additions & 15 deletions

File tree

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ dependencies {
7575
// WebClient
7676
implementation 'com.fasterxml.jackson.core:jackson-databind'
7777

78+
// https://mvnrepository.com/artifact/org.apache.tika/tika-core
79+
implementation 'org.apache.tika:tika-core:3.2.0' // 파일 형식 감지
80+
7881
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'
7982
}
8083

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package store.lastdance.service.common;
2+
3+
import org.springframework.web.multipart.MultipartFile;
4+
5+
public interface FileValidationService {
6+
public void validateImageFile(MultipartFile file);
7+
8+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package store.lastdance.service.common;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.apache.tika.Tika;
5+
import org.springframework.stereotype.Service;
6+
import store.lastdance.exception.CustomException;
7+
import store.lastdance.exception.ErrorCode;
8+
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
import java.util.List;
12+
13+
@Service
14+
@Slf4j
15+
public class FileValidationServiceImpl implements FileValidationService{
16+
private static final Tika tika = new Tika();
17+
private static final List<String> ALLOWED_FILE_TYPES = List.of(
18+
"image/jpeg",
19+
"image/png",
20+
"image/jpg",
21+
"image/gif",
22+
"image/webp"
23+
);
24+
@Override
25+
public void validateImageFile(org.springframework.web.multipart.MultipartFile file) {
26+
if(file == null || file.isEmpty()) {
27+
return;
28+
}
29+
30+
try (InputStream inputStream = file.getInputStream()) {
31+
String mimeType = tika.detect(inputStream);
32+
log.info("Detected MIME type: {} for file: {}", mimeType, file.getOriginalFilename());
33+
34+
if(!ALLOWED_FILE_TYPES.contains(mimeType)) {
35+
log.warn("Attempt to upload an invalid file type. Detected: {}, Filename: {}", mimeType, file.getOriginalFilename());
36+
throw new CustomException(ErrorCode.INVALID_FILE_TYPE);
37+
}
38+
} catch (IOException e) {
39+
log.error("File validation failed for file: {}", file.getOriginalFilename(), e);
40+
throw new CustomException(ErrorCode.FILE_UPLOAD_FAILED);
41+
}
42+
}
43+
}

src/main/java/store/lastdance/service/image/ImageServiceImpl.java

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import store.lastdance.exception.CustomException;
1515
import store.lastdance.exception.ErrorCode;
1616
import store.lastdance.repository.common.ImageFileRepository;
17+
import store.lastdance.service.common.FileValidationService;
1718

1819
import java.time.Duration;
1920
import java.util.UUID;
@@ -26,6 +27,7 @@ public class ImageServiceImpl implements ImageService {
2627
private final ImageFileRepository imageFileRepository;
2728
private final S3Operations s3Operations;
2829
private final S3Presigner s3Presigner;
30+
private final FileValidationService fileValidationService;
2931

3032
@Value("${spring.cloud.aws.s3.bucket}")
3133
private String bucketName;
@@ -117,21 +119,23 @@ private void validateImageFile(MultipartFile file, int maxSize) {
117119
throw new CustomException(ErrorCode.FILE_SIZE_EXCEEDED);
118120
}
119121

120-
String contentType = file.getContentType();
121-
if (contentType == null || !contentType.startsWith("image/")) {
122-
throw new CustomException(ErrorCode.INVALID_FILE_TYPE);
123-
}
124-
125-
String filename = file.getOriginalFilename();
126-
if (filename == null || !isAllowedExtensions(filename)) {
127-
throw new CustomException(ErrorCode.INVALID_FILE_EXTENSION);
128-
}
129-
}
130-
131-
private boolean isAllowedExtensions(String filename) {
132-
String extension = getFileExtension(filename).toLowerCase();
133-
return extension.equals(".jpg") || extension.equals(".jpeg") ||
134-
extension.equals(".png") || extension.equals(".gif");
122+
fileValidationService.validateImageFile(file);
123+
124+
// String contentType = file.getContentType();
125+
// if (contentType == null || !contentType.startsWith("image/")) {
126+
// throw new CustomException(ErrorCode.INVALID_FILE_TYPE);
127+
// }
128+
//
129+
// String filename = file.getOriginalFilename();
130+
// if (filename == null || !isAllowedExtensions(filename)) {
131+
// throw new CustomException(ErrorCode.INVALID_FILE_EXTENSION);
132+
// }
133+
// }
134+
//
135+
// private boolean isAllowedExtensions(String filename) {
136+
// String extension = getFileExtension(filename).toLowerCase();
137+
// return extension.equals(".jpg") || extension.equals(".jpeg") ||
138+
// extension.equals(".png") || extension.equals(".gif");
135139
}
136140

137141
}

0 commit comments

Comments
 (0)