From 5c61fc3f24e20598fd417bf407bb34538952aa34 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Tue, 18 Aug 2026 16:56:26 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A9=80=ED=8B=B0=ED=8C=8C=ED=8A=B8=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20=EB=B0=8F=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EB=B3=B4=EC=95=88=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20-=20MultipartConfig:=20partHeaderSizeMax=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80(CVE-2025-48976=20?= =?UTF-8?q?=EB=8C=80=EC=9D=91)=20-=20build.gradle:=20commons-fileupload=20?= =?UTF-8?q?1.6.0=20=EB=B0=8F=20commons-beanutils=201.11.0=20=EC=97=85?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 12 +++++++-- .../apim/portal/config/MultipartConfig.java | 27 ++++++++++++++++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index f054695..36acfa1 100644 --- a/build.gradle +++ b/build.gradle @@ -115,7 +115,12 @@ dependencies { implementation 'org.apache.commons:commons-collections4:4.4' 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' } implementation 'org.mapstruct:mapstruct:1.5.5.Final' @@ -132,7 +137,10 @@ dependencies { implementation 'net.bytebuddy:byte-buddy:1.14.5' // 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' diff --git a/src/main/java/com/eactive/apim/portal/config/MultipartConfig.java b/src/main/java/com/eactive/apim/portal/config/MultipartConfig.java index 41a5f27..d4fd788 100644 --- a/src/main/java/com/eactive/apim/portal/config/MultipartConfig.java +++ b/src/main/java/com/eactive/apim/portal/config/MultipartConfig.java @@ -2,6 +2,8 @@ package com.eactive.apim.portal.config; import lombok.RequiredArgsConstructor; 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.Configuration; import org.springframework.web.multipart.commons.CommonsMultipartResolver; @@ -18,6 +20,18 @@ import org.springframework.web.multipart.commons.CommonsMultipartResolver; @RequiredArgsConstructor public class MultipartConfig { + /** + * 멀티파트 파트 1개의 헤더 구간 최대 바이트. + *

+ * 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배 엄격하다.) + *

+ */ + private static final int PART_HEADER_SIZE_MAX = 2048; + private final PortalProperties portalProperties; /** @@ -31,7 +45,14 @@ public class MultipartConfig { */ @Bean(name = "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(); resolver.setMaxUploadSize(maxSizeBytes); @@ -39,8 +60,8 @@ public class MultipartConfig { resolver.setMaxInMemorySize((int) Math.min(maxSizeBytes, Integer.MAX_VALUE)); resolver.setDefaultEncoding("UTF-8"); - log.info("CommonsMultipartResolver configured with maxUploadSize: {} bytes ({})", - maxSizeBytes, portalProperties.getFile().getMaxSize()); + log.info("CommonsMultipartResolver configured with maxUploadSize: {} bytes ({}), partHeaderSizeMax: {} bytes", + maxSizeBytes, portalProperties.getFile().getMaxSize(), PART_HEADER_SIZE_MAX); return resolver; }