Android軟件安全開發實踐(下)


我們討論了數據存儲、網絡通信、密碼和認證策略等安全問題和解決方案,本期將繼續從組件間通信、數據驗證和保全保護等方面來實踐Android軟件安全開發之路。

組件間通信

組件間通信的安全問題是Android所獨有的,也是目前軟件中最常出現的一種問題。

我們先回顧一下組件間通信機制。Android有四類組件:activity、service、broadcast receiver和content provider。在同一個軟件之中或不同軟件之間,前三種組件使用Intent相互調用,使用ContentResolver對象訪問content provider,共同實現軟件的功能。使用Intent,可以顯式或隱式地調用:

  • 顯式(explicit):調用者知道要調用誰,通過組件名指定具體的被調用者;
  • 隱式(implicit):調用者不知道要調用誰,只知道執行的動作,由系統選擇組件處理這個請求。

如下面的代碼所示:

無論是顯式還是隱式,如果要跨應用調用,還需要被調用的組件是對外暴露的。默認情況下,service、broadcast receiver和content provider是暴露的,申明了Intent-filter的actvity也是暴露的。

抽象地說,組件A要調用組件B,以期待B完成某個功能;它可以發送一些數據給組件B,也可以獲得B執行后的返回結果。在這個模型中,問題出現在A和B之間不一定互相可信。

如果B是暴露的,任何軟件都可以調用它,包括攻擊者編寫的軟件。攻擊者可能但並非總能成功:

  • 直接調用暴露的B,以獲得其執行結果;
  • 構造特定的數據,並用於調用暴露的B,從而試圖影響B的執行;
  • 調用暴露的B,並獲取它執行完返回的結果。

如果A用的是隱式調用,任何軟件都可以實現它的action從而響應調用。攻擊者可能(但並非總能成功):

  • 構造偽造的組件C,響應A的Intent,以讀取A要發給B的數據;
  • 構造偽造的組件C,響應A的Intent,彈出虛假的用戶界面以展開進一步攻擊(例如釣魚);
  • 構造偽造的組件C,響應A的Intent,返回偽造的執行結果。

這樣說可能比較抽象。下面我們對這兩種情況分別討論。

組件暴露的問題

看一個例子。在一個第三方深度定制的ROM中,預裝了名為Cit.apk的軟件,用於手機的硬件測試。它的AndroidManifest.xml局部如下:

可以看到,它申明一個名為.CitBroadcastReceiver的receiver,響應名為android.provider.Telephony.SECRET_CODE的action,並且指定了URI格式。

再來看這個receiver的代碼片段(下面的代碼是我反編譯得到的,不一定與軟件源碼完全一致):

可以看到,當調用這個receiver,並且提供的URI中host字段為284時,會以root權限調用本地的bugreport工具,並將結果輸出至m_logFileName指定的文件中。

默認情況下receiver是暴露的,因此這個receiver可以被其他軟件調用,代碼如下:

當這四行代碼執行時,就會觸發CitBroadcast-Receiver的那段代碼。從上下文看,輸出文件m_logFileName位於SD卡,任何軟件都可以隨意讀寫。因此,攻擊者可以獲得bugreport的輸出結果,其中包含大量系統數據和用戶數據。

請注意,在這個例子中,攻擊者的軟件不需要任何特殊權限,尤其是不需要root權限。這種由於組件暴露獲得額外權限的攻擊,被稱之為permission re-delegation(權限重委派)。

怎么避免由於組件暴露產生的安全問題?有的組件必須暴露,例如入口activity,或者確實對外提供服務或跨軟件協作;但也有的組件沒必要暴露。接下來我們分別討論。

不需要暴露的組件

再次回顧,默認情況下,service、broadcast receiver和content provider是暴露的,申明了Intent-filter的actvity也是暴露的。如果它們只被同一個軟件中的代碼調用,應該設置為不暴露。很容 易做到—在AndroidManifest.xml中為這個組件加上屬性android:exported=”false”即可。

需要暴露的組件

如果組件需要對外暴露,應該通過自定義權限限制對它的調用。

首先,在實現了被調用組件的軟件的Android-Manifest.xml中自定義一個權限:

接下來,為被調用組件添加這個權限限制,即在AndroidManifest.xml中為這個組件添加android:permission屬性:

另一種方法是在組件的實現代碼中使用Context.checkCallingPermission()檢查調用者是否擁有這個權限。

最后,要調用這個暴露的組件,調用者所在的軟件應該申明使用這個權限,即在AndroidManifest.xml中添加相應的use-permission申明。

進一步地,還可以將這種組件暴露的需求分為兩種情況。

  • 如果這個組件只打算給自己開發的其他軟件使用,而不希望暴露給第三方軟件,在定義權限時,protectionLevel字段應該選擇signature。 這種設置要求權限使用者(即調用者)與權限定義者(即被調用者)必須由相同的證書進行簽名,因此第三方無法使用該權限,也就無法調用該組件。
  • 如果這個組件要暴露給第三方,則protection-Level應使用normal或dangerous。此時,任何軟件都可以使用該權限,只在安裝時會 通知用戶。考慮到用戶一般會忽略權限提示,此時自定義權限實際失去了保護效果,我們依然要仔細審查該組件的代碼,避免出現能力泄露或權限重委派等問題。

此外,對content provider,可以更細粒度地為讀取數據和寫入數據設置不同的權限,對應的manifest標簽分別為android:readPermission和android:writePermission。

隱式調用的問題

隱式調用的主要問題是被劫持,或Intent攜帶的數據被讀取。

為了劫持調用,攻擊者可以實現一個惡意的組件,申明相同的Intent-filter。在多個組件都可以響應同一個Intent的情況下,如果是調用 activity,系統會彈出界面要求用戶對多個軟件做出選擇,攻擊者可以模仿真實軟件的圖標和名稱吸引用戶點擊;如果是調用service,系統會隨機 選擇一個service;如果是調用receiver,系統會逐一地將Intent發給這些receiver。

劫持了調用后,攻擊者可以(但並非總能成功):

  • 啟動一個虛假的軟件界面,展開釣魚攻擊(例如要求用戶輸入賬戶密碼)
  • 讀取Intent攜帶的數據
  • 攔截broadcast的進一步發送,導致真正的receiver無法收到消息,從而拒絕服務
  • 如果是用startActivityForResult()調用了虛假的activity,可以返回惡意或虛假的結果給調用者
  • 執行其他惡意代碼

限於篇幅,對這些情形我們不提供示例代碼。來看一下怎么解決這類問題。

不需要隱式調用

除了基於Intent類中已有ACTIONs的隱式調用,絕大部分隱式調用都屬於這兩種情況:同一軟件中不同組件的調用;同一開發者不同軟件間的調用。

這兩種情況下,事實上,開發時都已可以確定要調用的組件是哪個。因此可以避免隱式調用,改為基於組件名的顯式調用。

需要隱式調用

發送broadcast除了使用sendBroadcast(Intent),還有一個方法是sendBroadcast(Intent, String),它的第二個參數可以指定接受者需要的權限。

如果是調用activity或者service,目前我所知,還沒有簡單的方法實現接收者的權限限制。在Android文檔中提出可以自定義Binder和AIDL實現通信雙方的互相驗證,但真正實現並不容易,也不為官方所推薦。

數據驗證

無論是客戶端還是服務器,在處理外部獲得的數據之前,都應先判斷和驗證數據的有效性。這里主要指是否包含畸形的數據。

在 Web開發中,服務器需要對用戶提交的數據進行有效性驗證,否則很容易出現眾所周知的SQL注入等攻擊。在移動開發中也不例外。雖然客戶端與服務器在底層 通信協議上對用戶是透明、不可見的,但開發者不應因此就假設雙方傳輸的數據永遠會和預先設計的一致。類似的,在讀取用戶從UI元素輸入的輸入、讀取存儲在 本地的數據后,使用前也應進行有效性驗證。

軟件版權保護

攻擊者對Android軟件進行逆向分析,除了尋找其中的安全漏洞,還可以直接攻擊軟件本身。

  • 破解軟件的收費機制、License驗證或功能限制;
  • 修改軟件代碼,去掉廣告庫,或者修改廣告庫(一般是改變推介ID字段),或者增加廣告庫,然后重新打包並分發;
  • 重新打包,植入惡意代碼並分發;
  • 逆向分析,學習軟件特色功能的實現方法,或者獲得可復用的代碼;

可以采取多種措施來增加破解、修改和逆向分析的難度,減少被攻擊的可能。這些措施包括:

  • 使用代碼混淆工具,例如SDK帶的ProGuard以及其他Java混淆器。
  • 采用NDK開發核心模塊。
  • 使用官方或第三方的軟件保護方案,例如SDK的android.drm包、Google Play的軟件許可(Application Licensing)支持。
  • 去掉開發時的調試代碼,關閉調試開關,刪除多余的Log代碼。

然而,采取了這些措施並不等於就萬無一失了。例如,用NDK開發的Native代碼,也可以使用IDA Pro及其插件來反匯編、反編譯和調試;用Google的DRM方案,也有AntiLVL這樣的破解工具。理論上,現有防御手段都可能找到方法繼續攻擊, 其價值只是提高攻擊難度和成本。

總結

已出現和將要出現的威脅

到 目前為止,在學術研究以外,針對Android軟件漏洞的攻擊只出現一起—劫持國外多個社交網站客戶端登陸會話的黑客工具FaceNiff。但隨着攻擊者 制造傳播惡意代碼的成本增加和收益降低,以及移動終端隱私數據逐漸成為地下產業鏈的交易資源,針對Android流行軟件漏洞的攻擊在未來幾年之內幾乎一 定會出現並爆發。

統一的安全模型

限於篇幅,本文只介紹了幾種常見且簡單的安全問題,還存在許多我們知道的、還不知道的漏洞。如何找到和解決這些問題?

回顧已介紹的內容,我們可以發現它們有類似的安全模型:通信雙方的信任問題。

  • 在數據存儲中,讀寫數據的代碼和存儲在本地的數據互相不可信。
  • 在數據通信中,發送者和接受者互相不可信。
  • 在登錄認證中,發起認證請求的用戶的和接受認證請求的服務器互相不可信。
  • 在組件間通信中,發起Intent的組件與接收Intent的組件互相不可信。
  • 在數據驗證中,處理數據的模塊不能相信產生數據的源。

面對將來的問題,我們也可以嘗試抽象出這種模型,區分互相不可信的實體,然后在不可信、不安全的基礎上,盡可能地實現相對的可信和安全。

進一步學習和行動

Android 的開發文檔Best Practices: Designing for Security和源碼文檔Tech Info: Security分別從開發和系統實現的角度介紹了系統的安全機制。另外,viaForensics提供了名為42+ Best Practices: Secure mobile development for iOS and Android的在線教程,更詳細地介紹了移動軟件面臨的安全威脅,並給出了安全開發實踐策略。

社區方面,從Android安全開發的角 度,Stack-Overflow並不一定是很好的選擇—其中一些最佳回答沒有考慮安全,直接使用可能產生問題。Google Group的anroid-security-discuss討論組則更為專業和准確。OWASP成立了一個Mobile Security工作組,目前已發布Top Ten Mobile Risks等多份白皮書,並舉辦了AppSec會議。這個工作組的效率雖然不高,但產出質量非常棒。

學術方面,2011和2012年的四大會議及其work-shop上均有移動軟件漏洞挖掘和攻擊阻止的論文出現,從它們的related works部分可以綜合快速地了解學術界的思路。

目前的移動開發還沒有形成如此成熟的體系,這也許與其輕快敏捷的互聯網產品開發風格有關。但我相信,真正實效的移動軟件安全開發,最終依然會融合到需求分析、系統設計、開發實現、測試驗證、部署運維等每一個環節,從而與PC平台的SDL殊途同歸。

作者肖梓航,安天實驗室高級研究員,主要方向是移動反病毒和移動軟件安全,發起或參與了多個移動安全開源項目。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM