開心一刻
今天上午,同事群中的劉總私聊我
劉總:你來公司多久了
我:一年了,劉總
劉總:你還年輕,機會還很多,年底了,公司要裁員
劉總語重心長的繼續說到:以後我們常聯絡,無論以後你遇到什麼困難,找我,我會盡量幫你!
我:所以了,我是被裁了嗎,呵,我爸知道嗎?
劉總:知道,今天上午保安部已經出名單了,你爸也在裡面
我:我媽知道嗎?
劉總:保潔也裁
我:劉總,你做這些事情問過我爺爺嗎?
劉總:門衛也裁
我心裡咯噔一下,這它媽噠團滅了呀
安全漏洞
公司的測試部門會定期掃描程式碼,檢測出安全漏洞,匯出 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
也不是不可以,需要調整配置,還存在一些限制,具體細節請看 SpringBoot2.7 霸王硬上弓 Logback1.3 → 不甜但解渴