這是個什麼漏洞

最近(2020年2月20日)Apache Tomcat爆出一個高危的服務器文件包含漏洞(CVE-2020-1938),據國家信息安全漏洞共享平臺上的漏洞描述來看,攻擊者可以利用這個漏洞讀取或包含 Tomcat 上所有 webapp 目錄下的任意文件。

這次的漏洞引起了軒然大波,漏洞被定爲高危可能僅僅是因爲沒有比這更加高的漏洞危害等級了,我彷彿聞到了類似PM2.5爆表的味道。

漏洞評分

是任意文件讀取啊!

從漏洞描述來看,這個漏洞允許攻擊者讀取Tomcat上所有webapp目錄下的任意文件。敲黑板,劃重點,注意是讀取webapp目錄下的任意文件,而你用java開發的應用程序的war包自然是放在webapp目錄下,當然也能夠被攻擊者讀取到。這意味着,如果你把數據庫用戶名密碼、連接其他後端服務的賬號、JWT簽名secret、OAuth AppSecret等密鑰信息放在properties文件裏的話,那麼,攻擊者可能現在也拿到了這些信息,並且正在試着入侵你的服務器。

典型的war包結構,properties及class文件都在裏面

泄露的不僅僅只是密鑰,war包其實就是個壓縮文件,解壓後不僅能拿到properties文件,還能獲得class文件,因此攻擊者還能逆向獲取到應用程序源碼,進而從源代碼中挖掘出更多其他漏洞加以利用。比如某些隱藏API或者參數、業務邏輯漏洞等,在有源代碼的情況,能夠極大的縮短攻擊者找到這些漏洞的時間。對了,如果你把密鑰硬編碼到源碼裏(希望你早就不這麼幹了),同樣也會泄露。

還有一種可能很少遇到但影響確實很大的場景:如果某家公司對外提供的服務主要依賴於其投入巨資打造的算法,這個算法是公司的核心競爭力,現在有可能因爲這個漏洞而泄露了源代碼,進而導致核心算法流失,那麼這造成的損失恐怕已經不能用“巨大”這個詞來形容了。

不硬編碼密鑰,並且密鑰抽離到配置文件,這麼做還遠遠不夠

因爲這個漏洞而泄露源代碼的情況不是這篇文章要討論的重點,我們收回來,把關注點放到密鑰泄露上面。

現如今的應用程序,尤其是企業級應用程序通常都會和其他系統進行交互,尤其是微服務的盛行,後端系統的數量變得更爲龐大。應用程序在集成這些內部或外部系統的時候,通常都需要賬號或者密鑰。與此同時,如果應用程序涉及加解密、簽名功能的話,還需要對應的密鑰。

爲方便描述,讓我們把這些賬號、Key、密碼、密鑰等統稱爲密鑰。不要把密鑰硬編碼到源代碼裏,這是人盡皆知的共識,一方面是擔心密鑰隨着源碼泄露而泄露,另一方面也是便於維護,輪換密鑰可以更加容易一些。

既然密鑰不硬編碼到源代碼,那這些密鑰總要有一個地方存放吧,大多數時候密鑰會被存放在一個properties文件裏,並且和源代碼存放於同一個代碼倉庫。當然也有團隊把所有密鑰抽離到一個單獨properties文件,並將其存放到一個單獨的代碼倉庫,在部署的時候再讀取出來並且和應用程序合併到一起。

把密鑰放到properties文件裏是非常常見的做法

以上做法看上去似乎有助於保護密鑰不泄露,但這次的Apache Tomcat 文件包含漏洞估計讓不少團隊嚇出一身冷汗:不管你是硬編碼還是費盡心思的通過劃分代碼倉庫來管理properties文件,都沒用,一旦war包被攻擊者讀取到,密鑰也就泄露了。 以上做法只能對防止開發階段的密鑰泄露有一定的幫助。

那你說怎麼辦?

爲了更好的保護密鑰不泄露,建議使用專門的密鑰管理服務。你可以選擇雲服務提供商的密鑰管理服務(比如AWS KMS、Azure KeyVault、騰訊和阿里等國內雲服務提供商也有對應的密鑰管理服務),也可以自己基於開源軟件搭建(比如HashiCorp Vault)。限於篇幅原因,我們就不展開講如何具體配置、使用這些密鑰管理服務了。

那爲什麼密鑰管理服務就能化解這個問題呢?原因在於,密鑰管理服務將密鑰加密後存儲在專門的安全存儲空間裏,而不是放置在應用程序裏,比如說war包或jar包中的properties文件裏。在應用程序啓動時,應用程序會從密鑰管理服務讀取到對應的密鑰,然後再使用。如此一來,應用程序的properties文件中不再有任何密鑰出現,就算攻擊者拿到了這個文件,也無法讀取到密鑰。

安全原則有時候說不定能救你一命

如果你的架構設計遵循了安全原則,就算你把密鑰硬編碼到源代碼或者放到了properties文件裏,那也不意味着就一定會被攻擊者獲取到(排除運氣成分)。

首先是最小權限原則。此次漏洞是因爲Apache Tomcat的AJP協議的問題,這個協議默認使用8009端口,但其實絕大多數時候你都用不到這個協議。雖然Apache Tomcat默認開啓這個協議,但如果你基於最小權限原則,只對外開放必要協議及端口(比如就開HTTP 8080),把其他的統統都禁用掉,那麼你也並不會受此次漏洞影響。

然後是縱深防禦原則。就算疏忽大意沒有關閉AJP 8009端口,但如果你的應用程序運行在Docker容器裏,那麼容器和宿主機之間的網絡映射就是一層防禦,默認只開啓必要服務的端口,比如只開HTTP 8080。儘管容器中實際運行的是一個受此次漏洞影響的Tomcat,並且還開啓了AJP 8009端口,但因爲這一層網絡映射的存在,攻擊者也無法從外部連接到Tomcat。

至少可以有3層網絡防禦

再進一步,通常後端應用都會有一個Ngnix反向代理頂在最前面,而在它前面可能還有網絡防火牆,這兩者也有管理網絡映射、路由的功能。因此,在這種場景下,Tomcat服務器至少受到了3層防禦工事的保護。除非這3層防禦都配置失誤……

小結

Apache Tomcat CVE-2020-1938這個漏洞確實兇猛,攻擊者可以讀取到webapp目錄下的任意文件,包括war包。而war包裏有properties文件,不少開發團隊都把連接數據庫的用戶名密碼、JWT 簽名secret、加解密密鑰等重要信息放在這個文件裏。這個漏洞的存在,允許攻擊者可以最終讀取到這些密鑰數據,當然源碼也是能通過反編譯war包裏的class文件得到的。

爲了避免密鑰泄露,常規做法(不要硬編碼密鑰到源代碼、密鑰單獨放置在properties文件並且和源代碼分別存儲在不同的代碼倉庫)並不奏效,更爲妥善的辦法是使用密鑰管理服務,你可以直接使用雲服務提供商的密鑰管理服務,也可以自己搭建一個。

安全原則是很重要的,尤其是最小權限和縱深防禦原則。在這個漏洞案例中,就算你使用的Tomcat有問題,但由於相關端口已經關閉,而且還有好幾層的網絡映射和路由配置的防禦,所以也不會受到影響。當然,第三方組件安全管理、安全補丁管理、實施端口監控等手段也有助於減輕或避免這個漏洞帶來的影響,但這就是另一個話題了,我們下次再聊。

相關文章