這篇文章將為大家詳細講解有關Android開發中如何防范組件導出的風險,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
近年來,移動APP存在一個非常的重要的問題就是安全問題,造成的后果有可能是用戶的隱私泄露和財產損失等,對于一款成熟的APP或者是金融銀行類APP,這無疑是最致命的,所以對APP進行有效的防范也是很有必要。
近段時間,公司安排了某安全公司對我們的APP進行了全方面的安全測試,根據文檔檢測結果看,整體上看還是很安全的,其中有一項就是組件導出風險,接下來我們說說四大組件、組件導出必要性、風險以及如何防范。
從事Android開發,我們都知道Android有四大組件, 分別是:
活動(Activity),用于表現功能,是用戶操作的可視化界面,它為用戶提供了一個完成操作指令的窗口;
服務(Service),后臺運行服務,不提供界面呈現;
廣播接受者(Broadcast Receive),用于接收廣播;
內容提供者(Content Provider),支持多個應用中存儲和讀取數據,相當于數據庫。
從這些組件簡單的介紹,我們知道它們的重要性,賦予了app更加豐富的功能,所以這四大組件的安全性對我們app和用戶來說就顯得更加地重要。
什么是組件導出呢?組件導出的意思就是組件可以被外部應用調用,我們可以在這四大組件聲明的清單文件設置組件是否導出,如下:
<activity android:exported="true" android:name=".other.ComponentActivity"> </activity>
或者:
<activity android:name=".other.ComponentActivity"> <intent-filter> <action android:name="android.intent.action.VIEW"/> </intent-filter> </activity>
上面兩種方式都是Activity組件導出的方式,主要是exported的值",為true時表示導出,Activity中exported的默認值:
沒有intent filter時,默認為false;
有intent filter時,默認為true
Broadcast Receive和Service的默認值都跟Activity的一樣。
Content Provider中exported的默認值:
當minSdkVersion或者targetSdkVersion小于16時,默認為true
大于17時,默認為false
開發過程中,app會有一些特定需求會使用到三方SDK,如微信分享、支付、推送等功能,我們發現這里都有一個共同點,都會涉及到組件導出的問題,如微信的
WXEntryActivity:
<!-- 微信分享 --> <activity android:name="${applicationId}.wxapi.WXEntryActivity" android:exported="true" android:launchMode="singleTask" android:theme="@android:style/Theme.Translucent.NoTitleBar" />
這樣就會被安全機構檢測出來的,如果不設置WXEntryActivity為組件導出,微信分享等功能根本就調不起來,這是官方的寫法,我們認為這是必須要設置為組件導出,除非你把微信分享需求干掉,那業務不把你罵死;又或者是監聽網絡變化的廣播接收器(7.0版本以上只能代碼中動態注冊才能接收該廣播)、推送功能,集成過一些推送SDK都有印象,一些Service也會聲明android:exported="true"等等。
這些無可避免的組件導出,我們可以回復安全機構:微信分享、推送等功能必須設置組件導出,所以我們只有保證自己的四大組件的設置,確保其是安全的,這樣才能確保app處于比較安全的狀態,應付安全檢測,給你的領導一個交代。
前面說明了組件的重要性、組件導出,那么組件導出的風險是什么呢?
Activity作為組成Apk的四個組件之一,是Android程序與用戶交互的界面,如果Activity打開了導出權限,可能被系統或者第三方的App直接調出并使用。Activity導出可能導致登錄界面被繞過、拒絕服務攻擊、程序界面被第三方惡意調用等風險。
Broadcast Receiver作為組成Apk的四個組件之一,對外部事件進行過濾接收,并根據消息內容執行響應,如果設置了導出權限,可能被系統或者第三方的App直接調出并使用。Broadcast Receiver導出可能導致敏感信息泄露、登錄界面被繞過等風險。S
ervice作為組成Apk的四個組件之一,一般作為后臺運行的服務進程,如果設置了導出權限,可能被系統或者第三方的App直接調出并使用。Service導出可能導致拒絕服務攻擊,程序功能被第三方惡意調用等風險。
Content Provider組成Apk的四個組件之一,是應用程序之間共享數據的容器,可以將應用程序的指定數據集提供給第三方的App,如果設置了導出權限,可能被系統或者第三方的App直接調出并使用。Content Provider導出可能導致程序內部的敏感信息泄露,數據庫SQL注入等風險。
接下來以Activity導出為示例,說明下其風險,其它組件類比就好。首先Activity要在清單文件AndroidManifest.xml注冊:
<activity android:name="com.littlejerk.sample.other.WebActivity"/>
Activity的啟動通常有兩種方法
顯式啟動,需要指定啟動的Activity:
Intent intent = new Intent(getContext(),WebActivity.class); intent.putExtra("URL","https://blog.csdn.net"); startActivity(intent);
隱式啟動,Intent中不再包含需要啟動的具體的Activity類,而是通過Intent提供某些信息,系統去檢索符合啟動意圖的Activity,這里是通過意圖過濾器聲明Intent信息:動作(action)、數據(data)、分類(Category)、類型(Type),組件(Component)、和擴展信息(Extra)。
<!-- 通過隱式啟動的方式需要在AndroidManifest.xml文件聲明--> <activity android:name=".other.WebActivity"> <intent-filter> <action android:name="com.littlejerk.sample.action.VIEW_URL" /> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> //調用方式啟動WebActivity Intent intent = new Intent(); intent.setAction("com.littlejerk.sample.action.VIEW_URL"); intent.putExtra("URL","https://blog.csdn.net"); startActivity(intent);
使用Action跳轉,如果有一個程序的AndroidManifest.xml中的某一個 Activity的IntentFilter段中 定義了包含了相同的Action,那么這個Intent就與這個目標Action匹配。如果這個IntentFilter段中沒有定義 Type、Category,那么這個 Activity就匹配了。但是如果手機中有兩個以上的程序匹配,那么就會彈出一個對話框來提示說明。
上面說過有IntentFilter,如果不指定android:exported,那么該值默認為true,外部的應用通過隱式意圖的方式也能將對應的組件啟動起來。這種情況我們就是我們說的組件導出,而導出則意味著很有可能存在安全問題,接下來看下WebActivity頁面:
Intent intent = getIntent(); String url = intent.getStringExtra("URL"); UILog.e(TAG, url.charAt(0)); mTvContent.setText(url);
我們注意到WebActivity只是接收一個URL并且顯示出來(沒有加載這個URL),從這里我們可以看出URL并沒有做參數檢驗,應用可能會崩潰;因為該頁面又是可被三方應用調用的,這時候如果別人惡意傳遞一些不良的網頁信息,那你這個應用不攔截就直接加載了,則這個應用有可能就要下架了。
我們以最常見的Activity為例說明了組件導出的風險,因為這個URL參數是我們處理的,我們可以防止應用空指針異常,這沒問題,但是上面也說如果加載了不良URL呢?其實組件導出的風險最根本原因是被別人調用了,那這樣有沒有辦法控制這個別人的范圍,只允許我們信賴的人去調用。
在這里不得不提Android的權限機制,Android的Permission檢查機制是用來控制一個應用擁有哪些執行權利。例如應用擁有拍照權限才能擁有拍照權利,那么我們是否可以通過權限來控制一個應用是否有啟動WebActivity的權利呢?
Android提供了自定義權限的能力,應用可以定義自己的權限,如在清單文件中自定義一個permission:
<permission android:label="允許打開WebActivity頁面權限" android:name="com.littlejerk.sample.permission.WEB" android:protectionLevel="signature" />
label:權限的描述
name:該權限的名稱,使用該權限時通過名稱來指定使用的權限
protectionLevel:該權限受保護的等級,這很重要,它有三個等級
signature:簽名級別權限,即權限的定義方和注冊方必須具有相同的簽名才有效
system:系統級別權限,即權限的定義方和注冊方必須為系統應用
signatureOrSystem :同簽名或系統應用,上述二者具備其一即可
權限定義完成,如何用它來保護暴露的組件呢,看下面代碼:
<!-- 通過隱式啟動的方式需要在AndroidManifest.xml文件聲明--> <activity android:permission="com.littlejerk.sample.permission.WEB" android:name=".other.WebActivity"> <intent-filter> <action android:name="com.littlejerk.sample.action.VIEW_URL" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
在activity聲明時,activity標簽下有一個permission,通過permission就能指定保護該activity的權限名稱了,這樣,只有具有了該權限的activity才能啟動它(注意在定義方和使用方都要在清單文件中定義和聲明自定義的權限),在調用方的清單文件中聲明和使用該權限:
<!--調用方可不用聲明--> <permission android:label="允許打開WebActivity頁面權限" android:name="com.littlejerk.sample.permission.WEB" android:protectionLevel="signature" /> <!--調用方必須申請此權限--> <uses-permission android:name="com.littlejerk.sample.permission.WEB"/>
有了權限的控制,activity組件導出的范圍就可控了,當我們公司應用間存在相互的組件調用時,就可以使用同簽名的權限來做限制,至于其它應用因為不是相同的簽名,所以它們無法調用我們暴露出去的組件,這很有效地規避了風險。
Activity是我們最常見的一個組件了,但是BroadcastReceiver用的地方也不少,一般安全評測都有提到這個組件的,我們有必要提一提它,其實各個組件的安全控制也可通過permission來控制的。
BroadcastReceiver的注冊有兩種方式
靜態注冊,在Manifest中聲明注冊
動態注冊,在代碼中依賴其他組件,通過registerReceiver注冊
BroadcastReceiver有廣播的發送方和接收方,所以當使用permission來校驗通信的時候一般都需要雙向校驗,即廣播的方送方和接收方都需要添加權限檢驗,保證發送方只將廣播發送給信賴的接收方,同樣的接收方也只接受來自信賴方的廣播。
發送方需要在清單文件AndroidManifest.xml中聲明權限:
<permission android:label="聲明發送方權限" android:name="com.littlejerk.sample.permission.BROADCAST_SEND" android:protectionLevel="signature" />
然后使用sendBroadcast(Intent intent, String receiverPermission)方法發送廣播:
//發送廣播 Intent intent = new Intent(); intent.setAction("com.littlejerk.sample.broadcast.action.TEST"); sendBroadcast(intent, "com.littlejerk.sample.permission.BROADCAST_SEND");
從receiverPermission字面意思就知道,接收廣播方必須要申請com.littlejerk.sample.permission.BROADCAST_SEND這個自定義權限,不然,無法接收到action通知,如接收方的清單文件AndroidManifest.xml:
<!-- 接收方需申請發送方權限--> <uses-permission android:name="com.littlejerk.sample.permission.BROADCAST_SEND"/>
如果接收方的廣播接收器不控制自己的權限,則同開發者應用只監聽com.littlejerk.sample.broadcast.action.TEST這個action就行了,但是為了雙重檢驗,我們也需要給接收方聲明自己的權限。
我們定義一個廣播接收器TestReceiver:
public class TestReceiver extends BroadcastReceiver { private static final String TAG = "TestReceiver"; //接收到廣播信息的回調 @Override public void onReceive(Context context, Intent intent) { //對外來的參數應該做些合法的檢查 String action = intent.getAction(); if (TextUtils.isEmpty(action)) { return; } UILog.e(TAG, "action:" + action); } }
接著在清單文件AndroidManifest.xml中聲明控制權限:
<permission android:label="聲明接收方權限" android:name="com.littlejerk.sample.permission.BROADCAST_RECEIVER" android:protectionLevel="signature" />
然后把這個控制權限給廣播接收器,接收器有兩種注冊方式
靜態注冊方式,在清單文件AndroidManifest.xml中:
<receiver android:name=".widget.receiver.TestReceiver" android:permission="com.littlejerk.sample.permission.BROADCAST_RECEIVER"> <intent-filter> <action android:name="com.littlejerk.sample.broadcast.action.TEST"/> </intent-filter> </receiver>
然后是動態注冊方式,在你需要注冊的地方聲明:
Receiver receiver = new Receiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("com.littlejerk.sample.broadcast.action.TEST"); registerReceiver(receiver, intentFilter, "com.littlejerk.sample.permission.BROADCAST_RECEIVER", null);
這兩種注冊方式都可以,但是推薦使用動態注冊廣播的方式,因為Android O上為了App性能和功耗的考慮,對靜態注冊的廣播做了很大的限制,至于是什么限制,這里就不說了。
我們對接收器也做了權限限制,那么發送方也必須要申請這個權限才能發送action給它呀,所以發送方的清單文件AndroidManifest.xml中在原有的基礎上需要添加:
<!-- 發送方需申請接收方權限--> <uses-permission android:name="com.littlejerk.sample.permission.BROADCAST_RECEIVER"/>
至此,廣播的雙向檢驗就完成了,以上所有代碼都測試過了,沒有任何問題,很好地過濾了無關廣播,保護了組件的安全。
關于Android開發中如何防范組件導出的風險就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。