Spring Boot/Framework/Security 관련 CVE 억제 규칙 추가 및 구성 수정
- dependency-check-suppressions.xml: 오탐 및 비사용 기능 CVE 5건 억제 추가 - PortalConfigSecurity.java: CVE-2026-22732 대응 우회 로직 추가 - build.gradle: Spring 의존성 버전 정리 및 중복 핀 제거
This commit is contained in:
+13
-1
@@ -94,7 +94,8 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
|
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
|
||||||
|
|
||||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
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'
|
implementation group: 'xalan', name: 'xalan', version: '2.7.3'
|
||||||
|
|
||||||
@@ -176,6 +177,17 @@ ext {
|
|||||||
// 주의: 3.1 은 #request/#session/#response/#servletContext 표현식 객체를 제거했다(IllegalArgumentException).
|
// 주의: 3.1 은 #request/#session/#response/#servletContext 표현식 객체를 제거했다(IllegalArgumentException).
|
||||||
set('thymeleaf.version', '3.1.4.RELEASE')
|
set('thymeleaf.version', '3.1.4.RELEASE')
|
||||||
set('thymeleaf-extras-springsecurity.version', '3.1.5.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')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,4 +28,94 @@
|
|||||||
</suppress>
|
</suppress>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<!-- ================================================================================
|
||||||
|
Spring 계열 Critical 5건 (2026-08-18 리포트 기준).
|
||||||
|
|
||||||
|
공통 배경: Spring Framework 5.3.x / Spring Boot 2.7.x / Spring Security 5.7.x 는 모두 OSS EOL 이라
|
||||||
|
남은 수정본이 상용(Enterprise/Tanzu)뿐이다. JDK 8 유지 제약상 Boot 3 + Spring 6 이관 전까지는
|
||||||
|
버전 상향으로 못 없앤다. 아래는 "코드에 트리거 경로가 없음"을 근거로 한 억제이며 만료일을 둔다.
|
||||||
|
버전 범위를 5.3.x / 2.7.x / 5.7.x 로 고정해 두었으므로, 이관 후에는 억제가 자동으로 풀린다.
|
||||||
|
재검토 시 확인할 것: (1) 각 근거 grep 이 여전히 0건인지 (2) OSS 수정본이 나왔는지.
|
||||||
|
================================================================================ -->
|
||||||
|
|
||||||
|
<!-- 1. Spring Boot: Cassandra SSL 호스트명 미검증 -->
|
||||||
|
<suppress until="2027-02-28Z">
|
||||||
|
<notes><![CDATA[
|
||||||
|
CVE-2026-40974 는 Spring Boot 의 Cassandra SSL 자동설정이 SSL 번들의 호스트명 검증 설정을
|
||||||
|
드라이버에 전달하지 않는 문제다. 이 앱은 Cassandra 를 쓰지 않는다
|
||||||
|
(runtimeClasspath 에 cassandra/datastax 계열 jar 0건, spring-boot-starter-data-cassandra 미선언).
|
||||||
|
수정본 2.7.33 은 상용(Enterprise) 릴리스라 OSS 로는 올릴 수 없다.
|
||||||
|
devtools/actuator/spring-boot-admin jar 는 스캔 대상(runtimeClasspath)에는 있으나
|
||||||
|
배포 산출물에서는 build.gradle 의 localOnlyLibPrefixes 로 제외되어 WAR 에 실리지 않는다.
|
||||||
|
확인: Rinjae / 2026-08-18 / grep -ri cassandra 0건, WAR 내 devtools/actuator jar 0건.
|
||||||
|
]]></notes>
|
||||||
|
<packageUrl regex="true">^pkg:maven/org\.springframework\.boot/.*@2\.7\..*$</packageUrl>
|
||||||
|
<cve>CVE-2026-40974</cve>
|
||||||
|
</suppress>
|
||||||
|
|
||||||
|
<!-- 2. Thymeleaf SSTI 2건: starter jar 이름 기준 오탐 -->
|
||||||
|
<suppress>
|
||||||
|
<notes><![CDATA[
|
||||||
|
오탐. CVE-2026-40477 / CVE-2026-40478 은 thymeleaf 본체 3.1.3.RELEASE 이하의 표현식 샌드박스
|
||||||
|
우회 문제이고 3.1.4.RELEASE 에서 수정됐다. 이 프로젝트는 build.gradle 의 ext 에서
|
||||||
|
thymeleaf.version=3.1.4.RELEASE / thymeleaf-extras-springsecurity.version=3.1.5.RELEASE 로
|
||||||
|
올려 두었으므로 실제 실리는 jar 는 thymeleaf-3.1.4.RELEASE.jar 다.
|
||||||
|
Dependency-Check 가 spring-boot-starter-thymeleaf-2.7.18.jar(의존만 선언한 빈 starter)에
|
||||||
|
thymeleaf CPE 를 잘못 매칭한 결과다. thymeleaf 본체 jar 에 대한 탐지는 억제하지 않는다.
|
||||||
|
확인: Rinjae / 2026-08-18 / WAR 내 thymeleaf-3.1.4.RELEASE.jar, thymeleaf-spring5-3.1.4.RELEASE.jar.
|
||||||
|
]]></notes>
|
||||||
|
<packageUrl regex="true">^pkg:maven/org\.springframework\.boot/spring-boot-starter-thymeleaf@.*$</packageUrl>
|
||||||
|
<cve>CVE-2026-40477</cve>
|
||||||
|
<cve>CVE-2026-40478</cve>
|
||||||
|
</suppress>
|
||||||
|
|
||||||
|
<!-- 3. Spring Framework: JMS Jackson 역직렬화 -->
|
||||||
|
<suppress until="2027-02-28Z">
|
||||||
|
<notes><![CDATA[
|
||||||
|
CVE-2026-41855 는 org.springframework.jms.support.converter.MappingJackson2MessageConverter /
|
||||||
|
JacksonJsonMessageConverter 가 임의 클래스 인스턴스화를 허용하는 문제다(신뢰할 수 없는 JMS 환경 전제).
|
||||||
|
이 앱은 JMS 를 쓰지 않는다: runtimeClasspath 에 spring-jms 0건, 소스에 javax.jms /
|
||||||
|
JmsTemplate / MappingJackson2MessageConverter 참조 0건.
|
||||||
|
5.3.x 는 OSS 수정본이 없다(6.2.19 / 7.0.8 에서만 수정). 이미 5.3.x OSS 마지막인 5.3.39 로 올려 둔 상태다.
|
||||||
|
확인: Rinjae / 2026-08-18 / grep -rn "javax.jms|JmsTemplate|MappingJackson2MessageConverter" src 0건.
|
||||||
|
]]></notes>
|
||||||
|
<packageUrl regex="true">^pkg:maven/org\.springframework/spring-.*@5\.3\..*$</packageUrl>
|
||||||
|
<cve>CVE-2026-41855</cve>
|
||||||
|
</suppress>
|
||||||
|
|
||||||
|
<!-- 4. spring-web: HttpInvoker 역직렬화 -->
|
||||||
|
<suppress until="2027-02-28Z">
|
||||||
|
<notes><![CDATA[
|
||||||
|
CVE-2016-1000027 은 HttpInvokerServiceExporter 를 노출했을 때만 성립한다. 해당 클래스는
|
||||||
|
Spring 6.0 에서 제거됐고 5.3.x 에는 수정본이 없다(= 5.3.x 를 쓰는 한 계속 탐지된다).
|
||||||
|
이 앱은 HttpInvoker 계열을 쓰지 않는다: 소스에 HttpInvoker 참조 0건이고 외부 호출은
|
||||||
|
RestTemplate / HttpClient 기반이다.
|
||||||
|
근본 해결은 Spring 6(Boot 3, JDK 17) 이관. 이관 전까지 억제한다.
|
||||||
|
확인: Rinjae / 2026-08-18 / grep -rn HttpInvoker src 0건.
|
||||||
|
]]></notes>
|
||||||
|
<packageUrl regex="true">^pkg:maven/org\.springframework/spring-web@5\.3\..*$</packageUrl>
|
||||||
|
<cve>CVE-2016-1000027</cve>
|
||||||
|
</suppress>
|
||||||
|
|
||||||
|
<!-- 5. Spring Security: 응답 커밋 시 보안 헤더 미기록 -->
|
||||||
|
<suppress until="2027-02-28Z">
|
||||||
|
<notes><![CDATA[
|
||||||
|
CVE-2026-22732 는 응답이 커밋된 뒤 Spring Security 가 보안 헤더를 기록하지 못하는 문제다.
|
||||||
|
트리거는 Content-Length 를 setHeader / setIntHeader / addIntHeader 로 지정하는 경로다
|
||||||
|
(OnCommittedResponseWrapper 는 setContentLength / setContentLengthLong / addHeader 만 추적한다).
|
||||||
|
(1) 이 코드베이스에는 트리거가 없다: portal / elink-portal-common / elink-online-core-jpa 전체에
|
||||||
|
setIntHeader, addIntHeader, setHeader("Content-Length") 0건. 파일 다운로드는
|
||||||
|
response.setContentLength(int) 를 쓰며 실측상 보안 헤더가 정상 기록된다.
|
||||||
|
(2) 그럼에도 안전망으로 PortalConfigSecurity 에서
|
||||||
|
HeaderWriterFilter.setShouldWriteHeadersEagerly(true) 를 적용해(spring.io 권고 우회책)
|
||||||
|
요청 시작 시점에 헤더를 기록하도록 했다.
|
||||||
|
수정본 5.7.22 / 5.8.24 는 Enterprise 전용이라 OSS 로는 올릴 수 없다. 이미 5.7.x OSS 마지막인
|
||||||
|
5.7.14 로 올려 둔 상태다.
|
||||||
|
확인: Rinjae / 2026-08-18 / 동일 스택(Boot 2.7.18 + Security 5.7.14 + Tomcat 9.0.120) 프로브 앱 실측 —
|
||||||
|
우회책 미적용 시 setHeader/setIntHeader/addIntHeader 경로에서 헤더 누락 재현, 적용 후 정상 기록.
|
||||||
|
]]></notes>
|
||||||
|
<packageUrl regex="true">^pkg:maven/org\.springframework\.security/spring-security-.*@5\.7\..*$</packageUrl>
|
||||||
|
<cve>CVE-2026-22732</cve>
|
||||||
|
</suppress>
|
||||||
|
|
||||||
</suppressions>
|
</suppressions>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
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.security.config.annotation.ObjectPostProcessor;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
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.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
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.access.AccessDeniedHandlerImpl;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
import org.springframework.security.web.csrf.CsrfException;
|
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.csrf.HttpSessionCsrfTokenRepository;
|
||||||
import org.springframework.security.web.session.HttpSessionEventPublisher;
|
import org.springframework.security.web.session.HttpSessionEventPublisher;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
@@ -92,6 +94,23 @@ public class PortalConfigSecurity {
|
|||||||
|
|
||||||
http
|
http
|
||||||
.authenticationManager(portalAuthenticationManager)
|
.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<HeaderWriterFilter>() {
|
||||||
|
@Override
|
||||||
|
public <O extends HeaderWriterFilter> O postProcess(O filter) {
|
||||||
|
filter.setShouldWriteHeadersEagerly(true);
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
}))
|
||||||
.formLogin(form -> form
|
.formLogin(form -> form
|
||||||
.loginPage("/login")
|
.loginPage("/login")
|
||||||
.usernameParameter("id")
|
.usernameParameter("id")
|
||||||
|
|||||||
Reference in New Issue
Block a user