멀티파트 업로드 및 의존성 보안 업데이트
- MultipartConfig: partHeaderSizeMax 설정 추가(CVE-2025-48976 대응) - build.gradle: commons-fileupload 1.6.0 및 commons-beanutils 1.11.0 업그레이드
This commit is contained in:
+10
-2
@@ -115,7 +115,12 @@ dependencies {
|
|||||||
implementation 'org.apache.commons:commons-collections4:4.4'
|
implementation 'org.apache.commons:commons-collections4:4.4'
|
||||||
|
|
||||||
implementation 'commons-net:commons-net:3.9.0'
|
implementation 'commons-net:commons-net:3.9.0'
|
||||||
implementation('commons-beanutils:commons-beanutils:1.9.4') {
|
// CVE-2025-48734 (PropertyUtilsBean 이 enum 의 declaredClass 프로퍼티 노출 → ClassLoader 접근/RCE).
|
||||||
|
// 1.11.0 부터 SuppressPropertiesBeanIntrospector 가 기본 활성이라 declaredClass 접근이 차단된다.
|
||||||
|
// 이 앱의 호출부(PasswordMatchValidator / PasswordRuleValidator / AuthNumberValidator)는
|
||||||
|
// 어노테이션에 박힌 고정 프로퍼티명만 넘기므로 외부 입력 경로는 없지만 버전은 올려 둔다.
|
||||||
|
// 1.11.0 = Java 8 바이트코드(major 52), PropertyUtils.getProperty/getNestedProperty API 동일.
|
||||||
|
implementation('commons-beanutils:commons-beanutils:1.11.0') {
|
||||||
// exclude group: 'commons-collections', module: 'commons-collections'
|
// exclude group: 'commons-collections', module: 'commons-collections'
|
||||||
}
|
}
|
||||||
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
|
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
|
||||||
@@ -132,7 +137,10 @@ dependencies {
|
|||||||
implementation 'net.bytebuddy:byte-buddy:1.14.5'
|
implementation 'net.bytebuddy:byte-buddy:1.14.5'
|
||||||
|
|
||||||
// Commons FileUpload (WAS 독립적인 multipart 처리)
|
// Commons FileUpload (WAS 독립적인 multipart 처리)
|
||||||
implementation 'commons-fileupload:commons-fileupload:1.5'
|
// CVE-2025-48976 (멀티파트 파트 헤더 크기 제한 부재 → DoS). 1.6.0 에서 partHeaderSizeMax 도입.
|
||||||
|
// 주의: 1.6 부터 파트 헤더 총량 기본 상한이 10240 → 512 바이트로 줄었다(DEFAULT_PART_HEADER_SIZE_MAX).
|
||||||
|
// 한글 파일명은 UTF-8 로 3바이트/자라 Content-Disposition 이 길어질 수 있어 실측으로 여유를 확인했다.
|
||||||
|
implementation 'commons-fileupload:commons-fileupload:1.6.0'
|
||||||
implementation 'commons-io:commons-io:2.15.1'
|
implementation 'commons-io:commons-io:2.15.1'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.eactive.apim.portal.config;
|
|||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.fileupload.FileItemFactory;
|
||||||
|
import org.apache.commons.fileupload.FileUpload;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
|
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
|
||||||
@@ -18,6 +20,18 @@ import org.springframework.web.multipart.commons.CommonsMultipartResolver;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class MultipartConfig {
|
public class MultipartConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 멀티파트 파트 1개의 헤더 구간 최대 바이트.
|
||||||
|
* <p>
|
||||||
|
* commons-fileupload 1.6.0(CVE-2025-48976 수정)부터 이 상한이 생겼고 기본값이 512 바이트다.
|
||||||
|
* 한글 파일명은 UTF-8 로 3바이트/자라 Content-Disposition 이 금방 커진다 — 실측상 기본값 512 로는
|
||||||
|
* 한글 약 137자 이상 파일명에서 업로드가 실패하고, 예외도 "Maximum upload size exceeded" 로 감싸져
|
||||||
|
* 원인 파악이 어렵다. OS 파일명 상한(255자)이 전부 한글이어도 통과하도록 2048 로 둔다.
|
||||||
|
* (1.5 이전에는 사실상 10240 이었으므로 이 값도 그보다 5배 엄격하다.)
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
private static final int PART_HEADER_SIZE_MAX = 2048;
|
||||||
|
|
||||||
private final PortalProperties portalProperties;
|
private final PortalProperties portalProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,7 +45,14 @@ public class MultipartConfig {
|
|||||||
*/
|
*/
|
||||||
@Bean(name = "filterMultipartResolver")
|
@Bean(name = "filterMultipartResolver")
|
||||||
public CommonsMultipartResolver filterMultipartResolver() {
|
public CommonsMultipartResolver filterMultipartResolver() {
|
||||||
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
|
CommonsMultipartResolver resolver = new CommonsMultipartResolver() {
|
||||||
|
@Override
|
||||||
|
protected FileUpload newFileUpload(FileItemFactory fileItemFactory) {
|
||||||
|
FileUpload fileUpload = super.newFileUpload(fileItemFactory);
|
||||||
|
fileUpload.setPartHeaderSizeMax(PART_HEADER_SIZE_MAX);
|
||||||
|
return fileUpload;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
long maxSizeBytes = portalProperties.getFile().getMaxSizeBytes();
|
long maxSizeBytes = portalProperties.getFile().getMaxSizeBytes();
|
||||||
resolver.setMaxUploadSize(maxSizeBytes);
|
resolver.setMaxUploadSize(maxSizeBytes);
|
||||||
@@ -39,8 +60,8 @@ public class MultipartConfig {
|
|||||||
resolver.setMaxInMemorySize((int) Math.min(maxSizeBytes, Integer.MAX_VALUE));
|
resolver.setMaxInMemorySize((int) Math.min(maxSizeBytes, Integer.MAX_VALUE));
|
||||||
resolver.setDefaultEncoding("UTF-8");
|
resolver.setDefaultEncoding("UTF-8");
|
||||||
|
|
||||||
log.info("CommonsMultipartResolver configured with maxUploadSize: {} bytes ({})",
|
log.info("CommonsMultipartResolver configured with maxUploadSize: {} bytes ({}), partHeaderSizeMax: {} bytes",
|
||||||
maxSizeBytes, portalProperties.getFile().getMaxSize());
|
maxSizeBytes, portalProperties.getFile().getMaxSize(), PART_HEADER_SIZE_MAX);
|
||||||
|
|
||||||
return resolver;
|
return resolver;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user