From a7aa5bc98c418679335b10e0680594b7590a61af Mon Sep 17 00:00:00 2001 From: Rinjae Date: Tue, 18 Aug 2026 15:18:08 +0900 Subject: [PATCH] =?UTF-8?q?Spring=20Boot/Framework/Security=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20CVE=20=EC=96=B5=EC=A0=9C=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EA=B5=AC=EC=84=B1=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20-=20dependency-check-suppressions.xml:=20=EC=98=A4?= =?UTF-8?q?=ED=83=90=20=EB=B0=8F=20=EB=B9=84=EC=82=AC=EC=9A=A9=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20CVE=205=EA=B1=B4=20=EC=96=B5=EC=A0=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20-=20PortalConfigSecurity.java:=20CVE-2026-22732=20?= =?UTF-8?q?=EB=8C=80=EC=9D=91=20=EC=9A=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20-=20build.gradle:=20Spring=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EB=B2=84=EC=A0=84=20=EC=A0=95=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=8F=20=EC=A4=91=EB=B3=B5=20=ED=95=80=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 14 ++- ci/dependency-check-suppressions.xml | 90 +++++++++++++++++++ .../portal/config/PortalConfigSecurity.java | 19 ++++ 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b03f8ba..f054695 100644 --- a/build.gradle +++ b/build.gradle @@ -94,7 +94,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-jdbc' developmentOnly 'org.springframework.boot:spring-boot-devtools' - implementation 'org.springframework:spring-expression:5.3.30' + // spring-expression 개별 pin 제거: 아래 ext 의 spring-framework.version 이 전 모듈을 일괄 관리한다. + // 개별 pin 이 남아 있으면 다른 spring-* 모듈보다 낮은 버전으로 고정되어 버전이 어긋난다. implementation group: 'xalan', name: 'xalan', version: '2.7.3' @@ -176,6 +177,17 @@ ext { // 주의: 3.1 은 #request/#session/#response/#servletContext 표현식 객체를 제거했다(IllegalArgumentException). set('thymeleaf.version', '3.1.4.RELEASE') set('thymeleaf-extras-springsecurity.version', '3.1.5.RELEASE') + + // Spring Framework 5.3.x OSS 마지막 릴리스로 통일(Boot 2.7.18 BOM 기본 5.3.31, 일부 5.3.30 혼재였음). + // 5.3.32~5.3.39 구간 CVE 정리용. 5.3.x 는 OSS EOL 이라 이 위로는 상용(Enterprise) 빌드뿐이다. + // 남는 5.3.x 지적(CVE-2026-41855 JMS Jackson 역직렬화, CVE-2016-1000027 HttpInvoker)은 + // 5.3 계열에 수정본이 없고 앱이 JMS/HttpInvoker 를 쓰지 않으므로 억제 항목으로 따로 관리한다. + set('spring-framework.version', '5.3.39') + + // Spring Security 5.7.x OSS 마지막 릴리스(Boot 2.7.18 BOM 기본 5.7.11). + // CVE-2026-22732(응답 커밋 후 보안 헤더 미기록)는 수정본이 5.7.22/5.8.24 = Enterprise 전용이라 + // OSS 로는 올릴 수 없다. 우회책(HeaderWriterFilter.shouldWriteHeadersEagerly=true)은 동작 변경이라 분리 검토. + set('spring-security.version', '5.7.14') } diff --git a/ci/dependency-check-suppressions.xml b/ci/dependency-check-suppressions.xml index e70259b..f4e941e 100644 --- a/ci/dependency-check-suppressions.xml +++ b/ci/dependency-check-suppressions.xml @@ -28,4 +28,94 @@ --> + + + + + + ^pkg:maven/org\.springframework\.boot/.*@2\.7\..*$ + CVE-2026-40974 + + + + + + ^pkg:maven/org\.springframework\.boot/spring-boot-starter-thymeleaf@.*$ + CVE-2026-40477 + CVE-2026-40478 + + + + + + ^pkg:maven/org\.springframework/spring-.*@5\.3\..*$ + CVE-2026-41855 + + + + + + ^pkg:maven/org\.springframework/spring-web@5\.3\..*$ + CVE-2016-1000027 + + + + + + ^pkg:maven/org\.springframework\.security/spring-security-.*@5\.7\..*$ + CVE-2026-22732 + + diff --git a/src/main/java/com/eactive/apim/portal/config/PortalConfigSecurity.java b/src/main/java/com/eactive/apim/portal/config/PortalConfigSecurity.java index 533419b..9365cbf 100644 --- a/src/main/java/com/eactive/apim/portal/config/PortalConfigSecurity.java +++ b/src/main/java/com/eactive/apim/portal/config/PortalConfigSecurity.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -16,6 +17,7 @@ import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.access.AccessDeniedHandlerImpl; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.csrf.CsrfException; +import org.springframework.security.web.header.HeaderWriterFilter; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; import org.springframework.security.web.session.HttpSessionEventPublisher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -92,6 +94,23 @@ public class PortalConfigSecurity { http .authenticationManager(portalAuthenticationManager) + // CVE-2026-22732 우회책. Spring Security 는 기본적으로 보안 헤더를 "응답 커밋 시점"에 + // 지연 기록하는데, 응답 래퍼가 Content-Length 를 setHeader/setIntHeader/addIntHeader 로 + // 지정하는 경로를 추적하지 못해 그 경우 헤더가 통째로 누락된다 + // (X-Content-Type-Options, X-Frame-Options, Cache-Control, Pragma, Expires, X-XSS-Protection). + // 수정본은 5.7.22/5.8.24(Enterprise 전용)뿐이라 OSS 로는 올릴 수 없어 우회책을 적용한다. + // 요청 시작 시점에 헤더를 기록하게 만든다. 앱이 나중에 같은 헤더를 지정하면 앱 값이 남는다 + // (실측: FileDownloadController#viewImage 의 Cache-Control: public, max-age=86400 유지됨). + // 현재 앱은 response.setContentLength(int) 만 쓰므로 노출 경로는 없지만, + // 새 코드가 위 메서드를 쓰더라도 헤더가 빠지지 않도록 두는 안전망이다. + // Spring Security 를 수정본(6.5.9+/7.0.4+ 또는 Enterprise 5.7.22+)으로 올리면 제거 가능. + .headers(headers -> headers.addObjectPostProcessor(new ObjectPostProcessor() { + @Override + public O postProcess(O filter) { + filter.setShouldWriteHeadersEagerly(true); + return filter; + } + })) .formLogin(form -> form .loginPage("/login") .usernameParameter("id")