公司的测试部门会定期扫描代码,检测出安全漏洞,导出 Excel
放到群里,各个项目的负责人针对性去修复(升级组件版本),因为某些原因不能修复的,需要给出原因(有些组件版本依赖更高的 JDK
版本,而 JDK
又不能升)。而我负责的项目是基于 Spring Boot 2.7.18
,它依赖的 logback
版本是 1.2.12
,存在安全漏洞 CVE-2023-6378
我本意是非常拒绝修这玩意的,修的时候得评估影响点,测的时候需要都覆盖到;核心组件的升级不亚于一次重构,开发和测试都得全量测。重点是,修好不算产出,修坏了可是要背锅的,我可是经历过血的教训的:都说了能不动就别动,非要去调整,出生产事故了吧,总之还是那句话
能不动就不要动,改好没绩效,改出问题要背锅,吃力不讨好,又不是不能跑
纵使我有万般的不愿,但也不得不修,公司对安全漏洞这一块非常重视,毕竟要给客户留下非常专业的形象。既然避无可避,那就坦然接受,充分评估影响点,做好全面的测试
如何修复,想必大家都知道,剔除掉 spring-boot-starter-logging
依赖,引入新版本依赖,如下所示
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
升级到哪个版本,就值得仔细斟酌一番了。反正都要升级,那何不升级到最新版?安全漏洞少,甚至暂时没漏洞。那也不是,因为 logback
依赖 JDK
版本,官方说明如下
因为项目依赖的 JDK
版本是 8,所以我们将 logback
升级到 1.3 的最新版是最合适的;logback 1.3.x
依赖的 SLF4J
版本是 2.0.x
,所以最终 pom.xml
调整成如下
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<logback.version>1.3.14</logback.version>
<slf4j.version>2.0.7</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>
貌似挺简单的,对吧?编译也不报错,一切都很顺利;一旦你运行,最烦人的 bug
就来了
Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/impl/StaticLoggerBinder
at org.springframework.boot.logging.logback.LogbackLoggingSystem.getLoggerContext(LogbackLoggingSystem.java:304)
at org.springframework.boot.logging.logback.LogbackLoggingSystem.beforeInitialize(LogbackLoggingSystem.java:118)
at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationStartingEvent(LoggingApplicationListener.java:238)
at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:220)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:178)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:171)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:145)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:133)
at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:79)
at org.springframework.boot.SpringApplicationRunListeners.lambda$starting$0(SpringApplicationRunListeners.java:56)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:120)
at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:56)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:299)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1289)
at com.qsl.Application.main(Application.java:15)
Caused by: java.lang.ClassNotFoundException: org.slf4j.impl.StaticLoggerBinder
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 17 more
此时,你们会怎么办?本着快速解决 bug
的原则,我们也只能上网查问题
java.lang.ClassNotFoundException: org.slf4j.impl.StaticLoggerBinder
看能不能找到解决办法;可一通查下来,各种尝试,该问题都得不到解决,除非将 logback
版本降到 1.2.x
,可最新的 1.2.13
是有不少安全漏洞的
那没别的办法了,只能去追查问题产生的原因了,找到原因就好对症下药了。问题又来了,如何去查原因了,最直接、最有效的办法就是从异常堆栈信息入手
鼠标左击 LogbackLoggingSystem.java:304
,然后就来到 spring-boot-2.7.18
的源码
这里用到了 StaticLoggerBinder
,在往上滑到 LogbackLoggingSystem
的import
部分,StaticLoggerBinder
的全类路径是
org.slf4j.impl.StaticLoggerBinder
logback 1.2.12
是有这个类的
但 logback 1.3.14
不仅没有该类,连 org
包都不存在了
所以,原因是不是找到了?
spring-boot-2.7.18 依赖 org.slf4j.impl.StaticLoggerBinder,而 logback 1.3.14 没有该类
那如何对症下药了?不仅你们懵,我也懵
调整下思路,这个问题我们肯定不是第一个遇到的,对吧,肯定有人在 spring-boot
的官方提问,我们去搜搜 org/slf4j/impl/StaticLoggerBinder
点进去,里面有官方人员给出的答复,我给大家翻译一下;提问者是 LSmyrnaios
,他做了一下背景介绍
logback 1.3.x 基于 Java 8,1.4.x 基于 Java-11,而 Spring Boot 只在 3.x.x 中集成了 logback 1.4.x(基于 Java-11) Java-8 用户被遗忘了 根据 logback 文档说明,logback 同时维护 1.3.x 和 1.4.x,也就是说,logback 1.3.x 是 '活跃的',Spring Boot 2.7.x 应该集成它 请考虑以下案例: 我有一个Java-8应用程序,使用 logback v.1.3.6,运行没问题 现在,我想将该应用程序集成到 Spring Boot v.2.7.9,运行的时候胞如下错误: (异常堆栈跟我们遇到的一样,不展示了) 看起来像是 Spring Boot 用的 slf4j 1.7.x,但是 logback 1.3.x 用的 slf4j 2.0.x,所以 StaticLoggerBinder 类不见了 所以,你们能够在 Spring Boot >= 2.7.x and < 3 的版本中支持 logback 1.3.x 吗 先谢谢了.
这么看来,这哥们遇到的问题跟我们的一样,提出的诉求也跟我们一样,是不是看到了希望?
官方人员 scottfrederick
给出了回复
翻译过来就是
LSmyrnaios 感谢你的联系。 Spring Boot 2.7.x 依赖 Logback 1.2.x。 已经在第三方升级政策中说明过了,我们不会在 2.7.x 的版本中升级 Logback到 1.3.x。正如你提到的,我们不仅仅要升级 Logback 到 1.3.x,还需要将 SLF4J 升级到 2.0.x,这有一个关于我们为什么不在 2.7.x 升级的讨论,所以我们做补丁发布
第三方依赖升级说明如下
简单点来说就是:第三方依赖的补丁级别的修复,可以在 Spring Boot
的补丁版本中升级,而第三方依赖的次要或者主要版本的升级,则只能在 Spring Boot
的次要或主要版本中升级。不能在 Spring Boot
的补丁版本中升级第三方依赖的次要或者主要版本。这里的补丁版本可以理解成小版本,也就是 1.2.x
中的 x
,而次要或者重要版本,则是 1.x.x
中的第一个 x
,也就是我们所说的大版本
关于 scottfrederick
,他可不是 Spring Boot
的普通 Contributor
,人家可是榜六大哥
他说的还是很有权威的;关于他提到的讨论,我们后面在看,先把当前的看完。提问者 LSmyrnaios
又说了
翻译过来就是
scottfrederick,我们接着刚刚的讨论,我想问的是,是否有可能在 Spring Boot 的下一个大版本(比如 2.8.0,如果在计划中的话)将 SLF4J 升级到 2.0.x,logback 升级到 1.3.x 这对于大量的 Java 8 用户来说非常重要,他们希望为生产系统提供最新的安全和错误修复 先谢谢了
scottfrederick
说的就很符合我们的期望,我们接着往下看。wilkinsona
给出了回复
翻译过来就是
目前没有 Spring Boot 2.8 的计划
言简意赅,弦外之音就是 Spring Boot 2.x.x
就是不支持 Logback 1.3.x
,满满的任性感。你们是不是很好奇这任性的哥们是谁?人家可是 Spring Boot
的榜一大哥!!!
是不是觉得他任性的理所当然了?我们继续往下看,ASarco
说了一句
翻译过来就是
现在的问题是 logback 1.2.12 存在安全漏洞 cve-2023-6378,对于 2.7.x 的煞笔用户却没有一个结论
这哥们表达了自己得愤懑,都直接飙国粹(SB
)了,那是相当的气愤呀
SB 是 Spring Boot 的简写,并非国粹,大家别被误导了!!!
针对 ASarco
的问题,后面有人给了回复,可以升级 Logback
到 1.2.13
来修复漏洞 cve-2023-6378
,而我们也没得选了,只能将 Logback
从 1.3.14
降到 1.2.13
,最终的 pom.xml
如下所示
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<logback.version>1.2.13</logback.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
</dependencies>
所以,1.2.13
的安全漏洞仍是存在的,下次扫描出来后我们直接说明如下
修复不了,Spring Boot 2.7.x 官方不打算支持 Logback 1.3.x,除非升级 Spring Boot 到 3.x.x(集成的是 Logback 1.4.x),但同时需要将 JDK 升级到 11
还记得前面提到的那个讨论吗,因为比较长,我挑一些重点给大家翻译下
1、wilkinsona
提到了 Logback
的一次 commit,这次提交移除了 StaticLoggerBinder
从 1.3.0-alpha0
版本就移除了 StaticLoggerBinder
,所以 Spring Boot 2.7.x
不能集成 Logback 1.3.x
的任何一个版本
2、snicoll
(榜二大哥) 提到了一个很重要的点
Spring Boot
的 LoggingSystem
是可以禁用或者改变的
-Dorg.springframework.boot.logging.LoggingSystem=none
3、wilkinsona
提到了 spring.factories
中的 ApplicationListener
,其优先级高于 org.springframework.boot.context.logging.LoggingApplicationListener
,可以用来设置系统属性以响应 ApplicationStartingEvent
4、wilkinsona
针对 cmuchinsky
提出的
对于 spring boot 2.7,是否有可能更新 ogbackLoggingSystemlogback 来兼容 Logback 1.3 与 1.2,例如反射
给出了回答,他认为这不太可能,支持 Logback 1.4
所需的更改范围太广,无法通过反射并行支持 1.2 和 1.3/1.4
5、zhaolj214
通过读源代码,找到了一种解决方案
@SpringBootApplication
public class Spring5Application {
public static void main(String[] args) {
System.setProperty("org.springframework.boot.logging.LoggingSystem", "none");
SpringApplication.run(Spring5Application.class, args);
}
}
至于正确与否,我们下篇再试
6、wilkinsona
说明了可以自定义日志:howto.logging
总结下来就是:针对 Spring Boot 2.7.x
,官方不会支持 Logback 1.3.x
,但还是可以通过自定义的方式去支持 Logback 1.3.x
,具体如何自定义,以及效果如何,且听下回分解
Logback
1.3 依赖 JDK 8,1.4 依赖 JDK 11;Spring Boot
2.7.x 依赖 Logback 1.2.x
,而 3.x.x 依赖 Logback 1.4.x
。也就说 Spring Boot
跳过了 Logback 1.3.x
Spring Boot
官方也给出了答复,根据第三方依赖政策(小版本升级小版本,大版本升级大版本),2.7.x 不会支持 Logback 1.3.x
,而 3.x.x 索性直接支持 Logback 1.4.x
Spring Boot 2.7.x
支持 Logback 1.3.x
也不是不可以,需要调整配置,还存在一些限制,具体细节请看下篇