Android開(kāi)發(fā)中關(guān)于組件導(dǎo)出的風(fēng)險(xiǎn)及防范
前言
近年來(lái),移動(dòng)APP存在一個(gè)非常的重要的問(wèn)題就是安全問(wèn)題,造成的后果有可能是用戶(hù)的隱私泄露和財(cái)產(chǎn)損失等,對(duì)于一款成熟的APP或者是金融銀行類(lèi)APP,這無(wú)疑是最致命的,所以對(duì)APP進(jìn)行有效的防范也是很有必要。
近段時(shí)間,公司安排了某安全公司對(duì)我們的APP進(jìn)行了全方面的安全測(cè)試,根據(jù)文檔檢測(cè)結(jié)果看,整體上看還是很安全的,其中有一項(xiàng)就是組件導(dǎo)出風(fēng)險(xiǎn),接下來(lái)我們說(shuō)說(shuō)四大組件、組件導(dǎo)出必要性、風(fēng)險(xiǎn)以及如何防范。
一、四大組件
從事Android開(kāi)發(fā),我們都知道Android有四大組件, 分別是:
- 活動(dòng)(Activity),用于表現(xiàn)功能,是用戶(hù)操作的可視化界面,它為用戶(hù)提供了一個(gè)完成操作指令的窗口;
- 服務(wù)(Service),后臺(tái)運(yùn)行服務(wù),不提供界面呈現(xiàn);
- 廣播接受者(Broadcast Receive),用于接收廣播;
- 內(nèi)容提供者(Content Provider),支持多個(gè)應(yīng)用中存儲(chǔ)和讀取數(shù)據(jù),相當(dāng)于數(shù)據(jù)庫(kù)。
從這些組件簡(jiǎn)單的介紹,我們知道它們的重要性,賦予了app更加豐富的功能,所以這四大組件的安全性對(duì)我們app和用戶(hù)來(lái)說(shuō)就顯得更加地重要。
二、組件導(dǎo)出必要性
什么是組件導(dǎo)出呢?組件導(dǎo)出的意思就是組件可以被外部應(yīng)用調(diào)用,我們可以在這四大組件聲明的清單文件設(shè)置組件是否導(dǎo)出,如下:
<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組件導(dǎo)出的方式,主要是exported的值",為true時(shí)表示導(dǎo)出,Activity中exported的默認(rèn)值:
- 沒(méi)有intent filter時(shí),默認(rèn)為false;
- 有intent filter時(shí),默認(rèn)為true
Broadcast Receive和Service的默認(rèn)值都跟Activity的一樣。
Content Provider中exported的默認(rèn)值:
- 當(dāng)minSdkVersion或者targetSdkVersion小于16時(shí),默認(rèn)為true
- 大于17時(shí),默認(rèn)為false
開(kāi)發(fā)過(guò)程中,app會(huì)有一些特定需求會(huì)使用到三方SDK,如微信分享、支付、推送等功能,我們發(fā)現(xiàn)這里都有一個(gè)共同點(diǎn),都會(huì)涉及到組件導(dǎo)出的問(wèn)題,如微信的
WXEntryActivity:
<!-- 微信分享 --> <activity android:name="${applicationId}.wxapi.WXEntryActivity" android:exported="true" android:launchMode="singleTask" android:theme="@android:style/Theme.Translucent.NoTitleBar" />
這樣就會(huì)被安全機(jī)構(gòu)檢測(cè)出來(lái)的,如果不設(shè)置WXEntryActivity為組件導(dǎo)出,微信分享等功能根本就調(diào)不起來(lái),這是官方的寫(xiě)法,我們認(rèn)為這是必須要設(shè)置為組件導(dǎo)出,除非你把微信分享需求干掉,那業(yè)務(wù)不把你罵死;又或者是監(jiān)聽(tīng)網(wǎng)絡(luò)變化的廣播接收器(7.0版本以上只能代碼中動(dòng)態(tài)注冊(cè)才能接收該廣播)、推送功能,集成過(guò)一些推送SDK都有印象,一些Service也會(huì)聲明android:exported="true"等等。
這些無(wú)可避免的組件導(dǎo)出,我們可以回復(fù)安全機(jī)構(gòu):微信分享、推送等功能必須設(shè)置組件導(dǎo)出,所以我們只有保證自己的四大組件的設(shè)置,確保其是安全的,這樣才能確保app處于比較安全的狀態(tài),應(yīng)付安全檢測(cè),給你的領(lǐng)導(dǎo)一個(gè)交代。
三、組件導(dǎo)出風(fēng)險(xiǎn)
前面說(shuō)明了組件的重要性、組件導(dǎo)出,那么組件導(dǎo)出的風(fēng)險(xiǎn)是什么呢?
- Activity作為組成Apk的四個(gè)組件之一,是Android程序與用戶(hù)交互的界面,如果Activity打開(kāi)了導(dǎo)出權(quán)限,可能被系統(tǒng)或者第三方的App直接調(diào)出并使用。Activity導(dǎo)出可能導(dǎo)致登錄界面被繞過(guò)、拒絕服務(wù)攻擊、程序界面被第三方惡意調(diào)用等風(fēng)險(xiǎn)。
- Broadcast Receiver作為組成Apk的四個(gè)組件之一,對(duì)外部事件進(jìn)行過(guò)濾接收,并根據(jù)消息內(nèi)容執(zhí)行響應(yīng),如果設(shè)置了導(dǎo)出權(quán)限,可能被系統(tǒng)或者第三方的App直接調(diào)出并使用。Broadcast Receiver導(dǎo)出可能導(dǎo)致敏感信息泄露、登錄界面被繞過(guò)等風(fēng)險(xiǎn)。S
- ervice作為組成Apk的四個(gè)組件之一,一般作為后臺(tái)運(yùn)行的服務(wù)進(jìn)程,如果設(shè)置了導(dǎo)出權(quán)限,可能被系統(tǒng)或者第三方的App直接調(diào)出并使用。Service導(dǎo)出可能導(dǎo)致拒絕服務(wù)攻擊,程序功能被第三方惡意調(diào)用等風(fēng)險(xiǎn)。
- Content Provider組成Apk的四個(gè)組件之一,是應(yīng)用程序之間共享數(shù)據(jù)的容器,可以將應(yīng)用程序的指定數(shù)據(jù)集提供給第三方的App,如果設(shè)置了導(dǎo)出權(quán)限,可能被系統(tǒng)或者第三方的App直接調(diào)出并使用。Content Provider導(dǎo)出可能導(dǎo)致程序內(nèi)部的敏感信息泄露,數(shù)據(jù)庫(kù)SQL注入等風(fēng)險(xiǎn)。
接下來(lái)以Activity導(dǎo)出為示例,說(shuō)明下其風(fēng)險(xiǎn),其它組件類(lèi)比就好。首先Activity要在清單文件AndroidManifest.xml注冊(cè):
<activity android:name="com.littlejerk.sample.other.WebActivity"/>
Activity的啟動(dòng)通常有兩種方法
- 顯式啟動(dòng),需要指定啟動(dòng)的Activity:
Intent intent = new Intent(getContext(),WebActivity.class); intent.putExtra("URL","https://blog.csdn.net"); startActivity(intent);
- 隱式啟動(dòng),Intent中不再包含需要啟動(dòng)的具體的Activity類(lèi),而是通過(guò)Intent提供某些信息,系統(tǒng)去檢索符合啟動(dòng)意圖的Activity,這里是通過(guò)意圖過(guò)濾器聲明Intent信息:動(dòng)作(action)、數(shù)據(jù)(data)、分類(lèi)(Category)、類(lèi)型(Type),組件(Component)、和擴(kuò)展信息(Extra)。
<!-- 通過(guò)隱式啟動(dòng)的方式需要在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> //調(diào)用方式啟動(dòng)WebActivity Intent intent = new Intent(); intent.setAction("com.littlejerk.sample.action.VIEW_URL"); intent.putExtra("URL","https://blog.csdn.net"); startActivity(intent);
使用Action跳轉(zhuǎn),如果有一個(gè)程序的AndroidManifest.xml中的某一個(gè) Activity的IntentFilter段中 定義了包含了相同的Action,那么這個(gè)Intent就與這個(gè)目標(biāo)Action匹配。如果這個(gè)IntentFilter段中沒(méi)有定義 Type、Category,那么這個(gè) Activity就匹配了。但是如果手機(jī)中有兩個(gè)以上的程序匹配,那么就會(huì)彈出一個(gè)對(duì)話(huà)框來(lái)提示說(shuō)明。
上面說(shuō)過(guò)有IntentFilter,如果不指定android:exported,那么該值默認(rèn)為true,外部的應(yīng)用通過(guò)隱式意圖的方式也能將對(duì)應(yīng)的組件啟動(dòng)起來(lái)。這種情況我們就是我們說(shuō)的組件導(dǎo)出,而導(dǎo)出則意味著很有可能存在安全問(wèn)題,接下來(lái)看下WebActivity頁(yè)面:
Intent intent = getIntent(); String url = intent.getStringExtra("URL"); UILog.e(TAG, url.charAt(0)); mTvContent.setText(url);
我們注意到WebActivity只是接收一個(gè)URL并且顯示出來(lái)(沒(méi)有加載這個(gè)URL),從這里我們可以看出URL并沒(méi)有做參數(shù)檢驗(yàn),應(yīng)用可能會(huì)崩潰;因?yàn)樵擁?yè)面又是可被三方應(yīng)用調(diào)用的,這時(shí)候如果別人惡意傳遞一些不良的網(wǎng)頁(yè)信息,那你這個(gè)應(yīng)用不攔截就直接加載了,則這個(gè)應(yīng)用有可能就要下架了。
四、如何防范
我們以最常見(jiàn)的Activity為例說(shuō)明了組件導(dǎo)出的風(fēng)險(xiǎn),因?yàn)檫@個(gè)URL參數(shù)是我們處理的,我們可以防止應(yīng)用空指針異常,這沒(méi)問(wèn)題,但是上面也說(shuō)如果加載了不良URL呢?其實(shí)組件導(dǎo)出的風(fēng)險(xiǎn)最根本原因是被別人調(diào)用了,那這樣有沒(méi)有辦法控制這個(gè)別人的范圍,只允許我們信賴(lài)的人去調(diào)用。
在這里不得不提Android的權(quán)限機(jī)制,Android的Permission檢查機(jī)制是用來(lái)控制一個(gè)應(yīng)用擁有哪些執(zhí)行權(quán)利。例如應(yīng)用擁有拍照權(quán)限才能擁有拍照權(quán)利,那么我們是否可以通過(guò)權(quán)限來(lái)控制一個(gè)應(yīng)用是否有啟動(dòng)WebActivity的權(quán)利呢?
Android提供了自定義權(quán)限的能力,應(yīng)用可以定義自己的權(quán)限,如在清單文件中自定義一個(gè)permission:
<permission android:label="允許打開(kāi)WebActivity頁(yè)面權(quán)限" android:name="com.littlejerk.sample.permission.WEB" android:protectionLevel="signature" />
label:權(quán)限的描述
name:該權(quán)限的名稱(chēng),使用該權(quán)限時(shí)通過(guò)名稱(chēng)來(lái)指定使用的權(quán)限
protectionLevel:該權(quán)限受保護(hù)的等級(jí),這很重要,它有三個(gè)等級(jí)
- signature:簽名級(jí)別權(quán)限,即權(quán)限的定義方和注冊(cè)方必須具有相同的簽名才有效
- system:系統(tǒng)級(jí)別權(quán)限,即權(quán)限的定義方和注冊(cè)方必須為系統(tǒng)應(yīng)用
- signatureOrSystem :同簽名或系統(tǒng)應(yīng)用,上述二者具備其一即可
權(quán)限定義完成,如何用它來(lái)保護(hù)暴露的組件呢,看下面代碼:
<!-- 通過(guò)隱式啟動(dòng)的方式需要在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聲明時(shí),activity標(biāo)簽下有一個(gè)permission,通過(guò)permission就能指定保護(hù)該activity的權(quán)限名稱(chēng)了,這樣,只有具有了該權(quán)限的activity才能啟動(dòng)它(注意在定義方和使用方都要在清單文件中定義和聲明自定義的權(quán)限),在調(diào)用方的清單文件中聲明和使用該權(quán)限:
<!--調(diào)用方可不用聲明--> <permission android:label="允許打開(kāi)WebActivity頁(yè)面權(quán)限" android:name="com.littlejerk.sample.permission.WEB" android:protectionLevel="signature" /> <!--調(diào)用方必須申請(qǐng)此權(quán)限--> <uses-permission android:name="com.littlejerk.sample.permission.WEB"/>
有了權(quán)限的控制,activity組件導(dǎo)出的范圍就可控了,當(dāng)我們公司應(yīng)用間存在相互的組件調(diào)用時(shí),就可以使用同簽名的權(quán)限來(lái)做限制,至于其它應(yīng)用因?yàn)椴皇窍嗤暮灻?,所以它們無(wú)法調(diào)用我們暴露出去的組件,這很有效地規(guī)避了風(fēng)險(xiǎn)。
Activity是我們最常見(jiàn)的一個(gè)組件了,但是BroadcastReceiver用的地方也不少,一般安全評(píng)測(cè)都有提到這個(gè)組件的,我們有必要提一提它,其實(shí)各個(gè)組件的安全控制也可通過(guò)permission來(lái)控制的。
BroadcastReceiver的注冊(cè)有兩種方式
- 靜態(tài)注冊(cè),在Manifest中聲明注冊(cè)
- 動(dòng)態(tài)注冊(cè),在代碼中依賴(lài)其他組件,通過(guò)registerReceiver注冊(cè)
BroadcastReceiver有廣播的發(fā)送方和接收方,所以當(dāng)使用permission來(lái)校驗(yàn)通信的時(shí)候一般都需要雙向校驗(yàn),即廣播的方送方和接收方都需要添加權(quán)限檢驗(yàn),保證發(fā)送方只將廣播發(fā)送給信賴(lài)的接收方,同樣的接收方也只接受來(lái)自信賴(lài)方的廣播。
廣播發(fā)送方
發(fā)送方需要在清單文件AndroidManifest.xml中聲明權(quán)限:
<permission android:label="聲明發(fā)送方權(quán)限" android:name="com.littlejerk.sample.permission.BROADCAST_SEND" android:protectionLevel="signature" />
然后使用sendBroadcast(Intent intent, String receiverPermission)方法發(fā)送廣播:
//發(fā)送廣播 Intent intent = new Intent(); intent.setAction("com.littlejerk.sample.broadcast.action.TEST"); sendBroadcast(intent, "com.littlejerk.sample.permission.BROADCAST_SEND");
從receiverPermission字面意思就知道,接收廣播方必須要申請(qǐng)com.littlejerk.sample.permission.BROADCAST_SEND這個(gè)自定義權(quán)限,不然,無(wú)法接收到action通知,如接收方的清單文件AndroidManifest.xml:
<!-- 接收方需申請(qǐng)發(fā)送方權(quán)限--> <uses-permission android:name="com.littlejerk.sample.permission.BROADCAST_SEND"/>
如果接收方的廣播接收器不控制自己的權(quán)限,則同開(kāi)發(fā)者應(yīng)用只監(jiān)聽(tīng)com.littlejerk.sample.broadcast.action.TEST這個(gè)action就行了,但是為了雙重檢驗(yàn),我們也需要給接收方聲明自己的權(quán)限。
廣播接收方
我們定義一個(gè)廣播接收器TestReceiver:
public class TestReceiver extends BroadcastReceiver { private static final String TAG = "TestReceiver"; //接收到廣播信息的回調(diào) @Override public void onReceive(Context context, Intent intent) { //對(duì)外來(lái)的參數(shù)應(yīng)該做些合法的檢查 String action = intent.getAction(); if (TextUtils.isEmpty(action)) { return; } UILog.e(TAG, "action:" + action); } }
接著在清單文件AndroidManifest.xml中聲明控制權(quán)限:
<permission android:label="聲明接收方權(quán)限" android:name="com.littlejerk.sample.permission.BROADCAST_RECEIVER" android:protectionLevel="signature" />
然后把這個(gè)控制權(quán)限給廣播接收器,接收器有兩種注冊(cè)方式
靜態(tài)注冊(cè)方式,在清單文件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>
然后是動(dòng)態(tài)注冊(cè)方式,在你需要注冊(cè)的地方聲明:
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);
這兩種注冊(cè)方式都可以,但是推薦使用動(dòng)態(tài)注冊(cè)廣播的方式,因?yàn)锳ndroid O上為了App性能和功耗的考慮,對(duì)靜態(tài)注冊(cè)的廣播做了很大的限制,至于是什么限制,這里就不說(shuō)了。
我們對(duì)接收器也做了權(quán)限限制,那么發(fā)送方也必須要申請(qǐng)這個(gè)權(quán)限才能發(fā)送action給它呀,所以發(fā)送方的清單文件AndroidManifest.xml中在原有的基礎(chǔ)上需要添加:
<!-- 發(fā)送方需申請(qǐng)接收方權(quán)限--> <uses-permission android:name="com.littlejerk.sample.permission.BROADCAST_RECEIVER"/>
至此,廣播的雙向檢驗(yàn)就完成了,以上所有代碼都測(cè)試過(guò)了,沒(méi)有任何問(wèn)題,很好地過(guò)濾了無(wú)關(guān)廣播,保護(hù)了組件的安全。
總結(jié)
文章主要講了四大組件的含義及其重要性,然后闡明為什么會(huì)組件導(dǎo)出及導(dǎo)出風(fēng)險(xiǎn),有些組件導(dǎo)出時(shí)必須的,因?yàn)橐獙?shí)現(xiàn)一些特定功能,但是對(duì)于可控的組件,盡量設(shè)置不導(dǎo)出。如果需要導(dǎo)出組件,我們需要嚴(yán)格地做參數(shù)檢驗(yàn),防止崩潰;除此之外,最好地防范就是添加權(quán)限,這樣才能有效地防止被惡意調(diào)用,造成不必要的損失。
到此這篇關(guān)于Android開(kāi)發(fā)中關(guān)于組件導(dǎo)出的風(fēng)險(xiǎn)及防范的文章就介紹到這了,更多相關(guān)Android 組件導(dǎo)出內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android開(kāi)發(fā)之保存圖片到相冊(cè)的三種方法詳解
這篇文章主要介紹了Android開(kāi)發(fā)實(shí)現(xiàn)的保存圖片到相冊(cè)功能的三種方法,文中的示例代碼講解詳細(xì),有一定的參考價(jià)值,感興趣的可以了解一下2022-04-04RecyclerView實(shí)現(xiàn)查看更多及收起
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)查看更多及收起,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01Ubuntu中為Android系統(tǒng)實(shí)現(xiàn)內(nèi)置Java應(yīng)用程序測(cè)試Application Frameworks層的硬件服務(wù)
本文主要介紹Ubuntu中為Android系統(tǒng)內(nèi)置應(yīng)用訪(fǎng)問(wèn)Application Frameworks層的硬件服務(wù),這里提供了詳細(xì)的流程和代碼實(shí)例,有興趣的朋友可以參考下2016-08-08Android不壓縮圖片實(shí)現(xiàn)高清加載巨圖實(shí)例
這篇文章主要為大家介紹了Android不壓縮圖片實(shí)現(xiàn)高清加載巨圖實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Android App實(shí)現(xiàn)應(yīng)用內(nèi)部自動(dòng)更新的最基本方法示例
這篇文章主要介紹了實(shí)現(xiàn)Android App內(nèi)部自動(dòng)更新的最基本方法示例,包括IIS服務(wù)器端的簡(jiǎn)單布置,需要的朋友可以參考下2016-03-03Android入門(mén)之onTouchEvent觸碰事件的示例詳解
今天給大家?guī)?lái)的是TouchListener與OnTouchEvent的比較,以及多點(diǎn)觸碰的知識(shí)點(diǎn)!?文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-12-12Android編程獲取手機(jī)后臺(tái)運(yùn)行服務(wù)的方法
這篇文章主要介紹了Android編程獲取手機(jī)后臺(tái)運(yùn)行服務(wù)的方法,涉及Android針對(duì)系統(tǒng)服務(wù)的相關(guān)操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-12-12Android開(kāi)發(fā)TextView內(nèi)的文字實(shí)現(xiàn)自動(dòng)換行
這篇文章主要為大家介紹了Android開(kāi)發(fā)TextView內(nèi)的文字實(shí)現(xiàn)自動(dòng)換行,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Android seekbar實(shí)現(xiàn)可拖動(dòng)進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了Android seekbar實(shí)現(xiàn)可拖動(dòng)進(jìn)度條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01