自從去年Google扶正了Kotlin,使之成為了Android官方的推薦開發語言,Kotlin便迎來了春天。很多團隊/產品也紛紛加入了Kotlin的支援,大廠的應用也很多,社群對Kotlin也是讚不絕口。正在看此文的同學,也相信要麼用上Kotlin,要麼準備使用Kotlin。
這裡,筆者要給大家提個醒:舊專案引入Kotlin,如配置不當,會導致原始碼洩露!。這意味著我們所做的混淆、加固之類的防護操作都白費了!這可是相當嚴重的安全問題!
如圖。這是筆者在網上閒逛的時候,偶然發現的某應用包內竟然包含了Kotlin的原始檔!
妥妥的如假包換的原始檔,程式碼、註釋都是全套的。
原因分析
當筆者發現這個情況的時候,內心暗喜:難道筆者不小心挖到了一個Kotlin的大新聞,只要使用了Kotlin,就會被開源?
筆者嘗試在本地復現這個場景,但遺憾的是,不管是新版本的Kotlin Plugin還是舊版本的Kotlin Plugin,打出來的包都不包含.kt
檔案。隨後筆者又嘗試去應用寶抓取Top50的應用,發現使用了Kotlin的應用雖然多,但也沒出現圖上的應用一般的程式碼洩露情況。無法復現,還怎麼定位問題的原因?此時陷入了僵局。
後來,筆者檢視自家的應用時,發現自家應用引入了Kotlin後,也出現了這樣的問題。那這就不是小事了。
排查和定位過程按下不表,直觀的結果就是,打出來的APK包裡,混入了原始碼目錄下的.kt
原始檔。
我們知道,打包過程中,有一個步驟叫做Jar包資源合併,這個步驟會把Jar包內資源合併到一起,打包到apk中。既然APK裡包含的.kt
檔案,那極有可能是合併資源的任務出了問題。
可惜,比對了有問題的專案和正常的專案的build.gradle
之後,無奈的發現,即便是同樣的Gradle
、Kotlin Plugin
、build tools
版本,同樣的配置,依然無法在正常的專案下復現這個問題。
也就說,gradle
的Jar包資源合併任務並不是導致問題的元凶。也是,Gradle如果有問題,社群也早就發現了。
但問題還是得解決,既然是資源被合併打包了,那就找方法做指定資源的排除唄。
這部分的工作是由java plugin
乾的,那就到文件: The Java Plugin
裡找答案。
首先看到這個任務:
從描述可知,這個任務就是我們要找的,拷貝資源到jar包的資源目錄。
再往下看,專案的預設目錄結構:
資源目錄是放置在src/main/resources
,或對應的src/<程式碼集>/resources
目錄內。
如果要自定義資源目錄的配置,看這個:
也就是說,這個配置指定了程式碼集的資源目錄的位置。它會排除掉目錄內的.java
檔案,其他的外掛,也可能從這裡移除掉額外的檔案型別。
看到這裡,大家也差不多明白了吧?
講道理,預設生成的專案的目錄結構中,資源目錄和程式碼目錄是區分開的。既然APK裡包含了.kt
原始碼,那必然是資源目錄和程式碼目錄是同一個的情況。
由於java plugin
預設會移除資源目錄內的.java
檔案,因此,在以往的打包中,原始碼能夠被安全的移除掉,不至於導致洩露。
而現在,我們引入Kotlin的時候,會依舊保持原有的程式碼目錄結構,此時程式碼目錄中就混入的.kt
檔案,由於不屬於資源剔除的範圍,自然就被當做資原始檔打包進APK裡了。
在出現了原始碼洩露的應用裡,我還發現了.aidl
檔案,也就是IPC
通訊時定義的介面檔案。這類檔案的洩露也是同樣的原因造成的。
至此,這個原始碼洩露案,也就告破了。
當然,說告破還有點早,畢竟上面給出的是推測,就算是真理,也是需要檢驗的。
回到自家工程的build.gradle
裡一查:
android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
}
}
}
複製程式碼
果不其然,資原始檔所在的目錄和原始碼所在的目錄配置成了同一個。
至於出現這個問題原因,筆者反思了一下。這個專案屬於比較早期的專案,程式碼目錄結構遵循的是Eclipse
時代的規範,後面擁抱變化轉Android Studio
時,Gradle
預設的那套專案佈局規範和Eclipse
相差較大,為了避免對專案造成大的改動,所以採取了自定義目錄配置的做法,用較小的代價,完成了遷移。時過境遷,到了擁抱Kotlin
變化的時候,就出問題了。
修復方案
明瞭問題的所在,定解決方案自然也就方便了:
- 方案一:直接移除
resources.srcDirs = ['src']
配置。 - 方案二:為資源目錄增加新的提出型別
.kt
。 - 方案三:將資源目錄和程式碼目錄區分開來,不再混雜在一起。
這些方案裡,方案一有點簡單粗暴。對Android
專案來說,影響倒還好,因為很多資源都是放在assets
目錄裡,工程內很少會放這種資原始檔了。
方案二適用於那些資原始檔裡除了.kt
檔案之外,還有別的資原始檔的情況,傳統java開發可能會有這種場景。
方案三是最穩妥,最合乎規範的推薦做法。將資源和程式碼分開,使得相關檔案在物理上保持了隔離,對整個專案來說,整體結構更加清晰。
對筆者這種情況,更好的方式是做一次專案結構的改造,使之符合Gradle
的規範。
但歸根結底,這個問題屬於技術債務。很多時候,我們在開發的過程中,會為了貪圖方便,本著儘可能的減少影響面的想法,採取了一種折中的妥協的做法,度過了一時,但往往就埋下了禍根。
短期內沒時間,確實可以這麼做,但從長期來看,還是要把債務還上的。只是時間的問題罷了。
最後的最後
最後的最後,建議大家自查一下,避免出現和筆者統一的情況。尤其是那些從Eclipse
時代轉過來的Android專案!
感謝閱讀到最後,希望本文對你有所幫助。如果覺得筆者的文章對你有所幫助,還請給個喜歡/感謝/贊。如有紕漏,也請不吝賜教。歡迎大家留言一起討論。:-)
後記
本文發出後,有朋友留言,指本文有標題黨的嫌疑。恕筆者無法苟同。誠然,如有些朋友所言,不就是自己一個配置錯了,關Kotlin什麼事情,Kotlin乃無辜躺槍。
但筆者認為,這固然不是程式語言的鍋,但卻是Kotlin工具鏈上的Kotlin Plugin
沒兼顧到這種場景導致的安全漏洞。與之相對的Java Plugin
對這種場景就處理得周到得多。
雖然最後筆者將問題的原因歸根於專案配置導致的問題,但筆者仍然認為,這種情況的發生,Kotlin Plugin
也有一定的責任。完善的工具鏈,能為開發和使用帶來極大的便利,Kotlin Plugin
也不例外。筆者在Kotlin接入的配置教程裡,看到很多人在宣揚Kotlin
的引入很方便,方便的背後,卻可能會不小心帶來這麼大的安全隱患,筆者認為,是很有必要提醒使用了Kotlin
的各位的。
另外,安全問題的產生,往往都是小細節上的失誤導致的,而失誤,往往來自於人。愛因斯坦這樣說道:“只有兩種事物是無窮盡的——宇宙和人類的愚蠢”。
能遇上這個問題的專案,基本都是從Eclipse
時代過來的,這樣的專案,其具有的價值,料想是不言而喻的。筆者看到,有的同學認為,原始碼洩露就洩露,愛誰看誰看。個人認為,這是對專案的一種不負責任。
以上。
再更新
之前的標題《使用Kotlin或導致原始碼洩漏!》,評論指出有蹭熱度的嫌疑,參考了承香墨影的推送,改成《配置不當,或導致Kotlin原始碼洩漏!》,更直觀和開門見山。:-)