来源:juejin.cn/post/7396306532671307785
👉 欢迎加入小哈的星球,你将获得: 专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍; 《从零手撸:前后端分离博客项目(全栈开发)》 2期已完结,演示链接:http://116.62.199.48/; 截止目前,累计输出 77w+ 字,讲解图 3088+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有2600+小伙伴加入
安全漏洞
公司的测试部门会定期扫描代码,检测出安全漏洞,导出 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 说明了可以自定义日志:
https://docs.spring.io/spring-boot/docs/2.7.18/reference/html/howto.html#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
也不是不可以,需要调整配置,还存在一些限制,具体细节请看:SpringBoot2.7 霸王硬上弓 Logback1.3 → 不甜但解渴
https://juejin.cn/post/7396325195072159782
👉 欢迎加入小哈的星球,你将获得: 专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍; 《从零手撸:前后端分离博客项目(全栈开发)》 2期已完结,演示链接:http://116.62.199.48/; 截止目前,累计输出 77w+ 字,讲解图 3088+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有2600+小伙伴加入
最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。
PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。
点“在看”支持小哈呀,谢谢