天天看點

Android 源碼系列之<二>從安全的角度深入了解BroadcastReceiver(上)

        轉載請注明出處:http://blog.csdn.net/llew2011/article/details/51014743

        提起BroadcastReceiver大家都很熟悉,它和Activity,Service以及ContentProvider并稱為Android的四大元件(四大金剛),可見BroadcastReceiver的重要性,今天我們主要從安全的角度來講解稱為四大元件之一的BroadcastReceiver。可能有的童靴看到這裡會有疑問,BroadcastReceiver有啥好講的,不就是先定義自己的廣播接收器然後在manifest.xml檔案中注冊,在需要發送廣播的地方調用Context的sendBroadcast()方法或者是sendOrderBroadcast(),最後在我們自定義的的廣播接收器的onReceive()方法中做相應邏輯麼?恩,這樣使用BroadcastReceiver的總體流程是非常OK的,也說明你對廣播這塊的使用掌握的是非常熟悉的,但今天是從安全的角度來講解BroadcastReveiver的,我相信你閱讀完本文後會有所收獲(*^__^*) ……

        BroadcastReceiver的使用很廣泛也很簡單,它的使用可以概括為三步走:

  1. 定義自己的BroadcastReceiver并實作onReceive()方法
  2. 在AndroidManifest.xml中靜态注冊或者在代碼中動态注冊
  3. 調用Context的sendBroadcast()方法發送廣播

       這裡先對廣播的兩種注冊方式做一下說明,廣播注冊分為動态注冊和靜态注冊兩種方式。靜态注冊指的是常駐型廣播,無論我們的應用程式在不在運作,隻要有符合條件的廣播發來我們的應用程式都可以接受的到,動态注冊指的是隻有在我們的應用程式在運作的時候才可以接受到符合條件的廣播。是以當我們要使用廣播時要做一下區分:是用靜态還是用動态。

        按照以上步驟我們先定義自己的廣播接收器CustomBroadcastReceiver,定義自己的廣播接收器需要繼承自BroadcastReceiver類,該類是abstract類型的,是以我們需要實作onReceive()方法,代碼如下:

public class CustomBroadcastReceiver extends BroadcastReceiver {

	public CustomBroadcastReceiver() {
	}

	@Override
	public void onReceive(Context context, Intent intent) {
		Log.e(this.getClass().getSimpleName(), "current time is :" + System.currentTimeMillis());
	}
}
           

        我們自定義的廣播接收器比較簡單,僅僅在onReceive()方法中列印了一句話而已。接下來我們在AndroidManifest.xml檔案中配置CustomBroadcastReceiver,我們讓該Receiver接受指定action的廣播,代碼如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.llew.seetao.a"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity android:name="com.llew.seetao.a.MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <receiver android:name="com.llew.seetao.a.CustomBroadcastReceiver">
            <intent-filter>
                <action android:name="com.llew.seetao.customaction"/>
            </intent-filter>
        </receiver>
    </application>

</manifest>
           

        好了,在AndroidManifest.xml檔案配置完我們的廣播接收器後,我們可以發送一個廣播了。先在布局檔案activity_main.xml加入一個可以響應事件的Button,代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#aabbcc" >

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="sendBroadcast"
        android:text="測試廣播A程式" />

</FrameLayout>
           

        在布局檔案中我們使用了View的onClick屬性(若你對該屬性的用法不太清楚請自行查閱),MainActivity的代碼如下:

public class MainActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
	
	public void sendBroadcast(View v) {
		Intent intent = new Intent(MainActivity.this, CustomBroadcastReceiver.class);
		this.sendBroadcast(intent);
	}
}
           

        以上步驟完成之後就可以運作項目了,打開模拟器或者連結手機,運作一下,效果如下圖所示:

Android 源碼系列之&lt;二&gt;從安全的角度深入了解BroadcastReceiver(上)

        點選按鈕,會發現在logcat下有一行日志輸出,如下圖所示:

Android 源碼系列之&lt;二&gt;從安全的角度深入了解BroadcastReceiver(上)

        到這裡我們可以說隻是掌握了對廣播的使用,如果在項目中我們也是按照同樣的方式使用了廣播,那麼你的項目可能會存在風險。為什麼這麼說,下面我們來假設一個場景,假如别人反編譯了我們的APK包,看到了我們在配置檔案中的這個廣播,那他就可以在自己的應用中也發送一個廣播,因為我們配置的廣播含有intent-filter,是以可以隐式的調起這個廣播,那麼我們的APK會不會做出反應呢?為了做這個測試,我們建立一個工程B(把剛剛的稱為工程A),隻要B包名不和A包名相同就行,這樣這倆工程就可以同時跑在一個裝置上了,是以在B工程中通過隐式發送廣播的方式,代碼如下:

public class MainActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
	
	public void sendBroadcast(View v) {
		Intent intent = new Intent("com.llew.e.customaction");
		this.sendBroadcast(intent);
	}
}
           

        我們運作運作一下B程式,點選按鈕,通過檢視logcat的輸出,oh,my god,果真工程A中的log列印出來了,如下圖所示:

Android 源碼系列之&lt;二&gt;從安全的角度深入了解BroadcastReceiver(上)

        從logcat的列印結果可以看出,我們通過隐式的發送廣播确實可以讓A工程的廣播接收器做出響應,這樣的APP如果發版了确實是存在安全隐患的,那有沒有解決方式呢?答案是肯定的,我們接下來就看一下3種解決方式:

  1. 給廣播接收器添加export屬性
    <receiver android:name="com.llew.seetao.a.CustomBroadcastReceiver" android:exported="false">
        <intent-filter>
            <action android:name="com.llew.seetao.customaction"/>
        </intent-filter>
    </receiver>
               
            在上邊的代碼中我們隻是在注冊我們的CustomBroadReceiver的時候添加了export屬性,并把該屬性的值設定為false,難道僅僅就添加一個屬性就OK啦?可能有的童靴還是會産生疑問,為了打消童靴們的疑慮,我們再運作一下代碼,檢視一下結果,運作結果如下圖所示:
    Android 源碼系列之&lt;二&gt;從安全的角度深入了解BroadcastReceiver(上)
    Android 源碼系列之&lt;二&gt;從安全的角度深入了解BroadcastReceiver(上)
            通過分别運作工程A和工程B下的logcat輸出日志可以看出,給我們自定義的receiver添加了export屬性後确實不能通過這種方式來使我們的廣播接收器發生響應了。那這個屬性到底是幹嘛使的了?可能有的童靴又會有這樣的疑問了,我們來看一下官方文檔對這個export屬性是怎麼解釋的。
    Android 源碼系列之&lt;二&gt;從安全的角度深入了解BroadcastReceiver(上)

            英語水準有限,大緻翻譯一下官方文檔:

            目前的廣播接收器是否可以從外部應用接受消息,如果為true表示可以從外部應用接受消息,如果為false,表示目前BroadcastReceiver隻能從目前應用或者是擁有相同userID的應用接收廣播。預設值是根據目前BroadcastReceiver是否包含有intent-filter來決定的,如果沒有任何intent-filter的話,隻能通過類名來調起,此時預設值為false,如果包含有intent-filter,這預設值為true。不僅這個屬性可以指定BroadcastReceiver是否暴露給其他使用者,你也可以使用permission來限制外部應用給目前應用的receiver發送消息。

            在官方文檔結尾又說了除了使用export屬性外,我們還可以以添權重限的方式來限制外部引用給我們的receiver發送消息,那我們就接着往下看是如何使用權限的。

  2. 給廣播接收器添加自定義權限

            a)首先自定義權限,代碼如下:

    <permission android:name="com.llew.seetao.permission.customaction" android:protectionLevel="normal"></permission>
               
            b)其次給receiver添加permission權限,代碼如下:
    <receiver android:name="com.llew.seetao.a.CustomBroadcastReceiver" android:permission="com.llew.seetao.permission.customaction">
        <intent-filter>
            <action android:name="com.llew.seetao.customaction"/>
        </intent-filter>
    </receiver>
               
            c)運作代碼,檢視結果如下:
    Android 源碼系列之&lt;二&gt;從安全的角度深入了解BroadcastReceiver(上)
    Android 源碼系列之&lt;二&gt;從安全的角度深入了解BroadcastReceiver(上)
            通過觀察确實發現了通過添加自定義權限的方式那麼B程式确實是調用不起來A程式中的廣播的,呵呵,看到這裡我們高興的小心髒撲通撲通直跳。如果是這樣,那你就錯了,因為在A程式中我們确實給我們自定義的廣播添加了自定義權限,就是說擁有該權限的應用才可以,那假如我們在B程式中也同樣定義了同樣的權限,那結果會是神馬樣子呢?我們繼續來做實驗驗證一下,這次我們也把B程式添加同樣的權限,然後再使用權限,代碼如下:
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.llew.seetao.b"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-sdk
            android:minSdkVersion="8"/>
    
        <permission android:name="com.llew.seetao.permission.customaction" android:protectionLevel="normal"></permission>
        <uses-permission android:name="com.llew.seetao.permission.customaction"/>
        
        <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity android:name="com.llew.seetao.b.MainActivity"
                      android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
               
            我們通過這樣方式後再分别運作一下程式,分别運作一下,運作結果如下:
    Android 源碼系列之&lt;二&gt;從安全的角度深入了解BroadcastReceiver(上)
    Android 源碼系列之&lt;二&gt;從安全的角度深入了解BroadcastReceiver(上)
            唉,fuck,B程式又可以調起我們的廣播,這時候你可能心中有一萬匹草泥馬在奔騰了,這廣播還讓人不讓人用了,呵呵,我們這片文章就是從安全的角度來講解BroadcastReceiver的,肯定是有對策啦,還記得我們在A程式中定義的權限麼?如果你不記得了,代碼如下:
    <permission android:name="com.llew.seetao.permission.customaction" android:protectionLevel="normal"></permission>
               
            定義我們自己的權限時我們給權限使用的級别是normal,如果你對有關權限的級别不是太了解或不屬性,可以去官網檢視,這裡不再詳述了,通過查閱官網可知,權限級别有個為signature的,也就是說如果我們在自定義權限的時候把權限級别設定為signature時,隻有擁有相同簽名的APP才能調起我們的應用,這就相對的保證了安全,因為我們發版的時候肯定用的是自己的簽名,别人一般情況下是無法擷取到我們的簽名的,是以這種方法是可行的。這時候你肯定心裡樂開了花,真是魔高一尺道高一丈呀,别着急,我們還有另外一種更可靠高效的解決方式,這也是我開發中一直用到的,請繼續往閱讀(*^__^*) ……
  3. 使用官方推薦的LocalBroadcastManager

            LocalBroadcastManager是Android v4包中的(如果你不想引入v4包,可以直接從源碼中把這個類拷貝出來),它比起全局廣播有以下優勢:

                    (1).廣播隻會在你的應用内發送,無需擔心資料洩露,更加安全

                    (2).其他應用無法發送廣播給你的應用,不用擔心你的應用存在安全漏洞

                    (3).相比全局廣播,它不需要發送給整個系統,是以更加高效

            好了,說了這麼多,我們趕緊看看這個神一般的LocalBroadcastManager到底怎麼用吧?還是使用上文定義的CustomBroadcastReceiver,在onReceive()方法中我們僅僅列印了目前時間,代碼如下:

    public class CustomBroadcastReceiver extends BroadcastReceiver {
    
    	public static final String LOCAL_CUSTOM_ACTION = "com.llew.seetao.customaction";
    	
    	public CustomBroadcastReceiver() {
    	}
    
    	@Override
    	public void onReceive(Context context, Intent intent) {
    		Log.e(this.getClass().getSimpleName(), "current time is :" + System.currentTimeMillis());
    	}
    }
               
            然後我們看一下在MainActivity中的代碼,如下所示:
    public class MainActivity extends Activity {
    	
    	private LocalBroadcastManager mBroadcastManager;
    	private CustomBroadcastReceiver mLocalReceiver;
    	
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		
    		registerBroadcastReceiver();
    	}
    	
    	private void registerBroadcastReceiver() {
    		mBroadcastManager = LocalBroadcastManager.getInstance(MainActivity.this);
    		mLocalReceiver = new CustomBroadcastReceiver();
    		IntentFilter filter = new IntentFilter(CustomBroadcastReceiver.LOCAL_CUSTOM_ACTION);
    		mBroadcastManager.registerReceiver(mLocalReceiver, filter);
    	}
    	
    	public void sendBroadcast(View v) {
    		Intent intent = new Intent(CustomBroadcastReceiver.LOCAL_CUSTOM_ACTION);
    		mBroadcastManager.sendBroadcast(intent);
    	}
    	
    	@Override
    	protected void onDestroy() {
    		super.onDestroy();
    		if(null != mBroadcastManager) {
    			mBroadcastManager.unregisterReceiver(mLocalReceiver);
    		}
    	}
    }
               
            我們在MainActivity的onCreate()方法中通過LocalBroadcastManger.getInstance()的方式執行個體化了mBroadcastManager對象,然後通過mBroadcastManager對象調用register()方法來注冊我們的廣播接收器,最後發送廣播的代碼是調用了mBroadcastManger的sendBroadcast()方法,在onDestroy()方法中又調用了mBroadcastManager的unregisterReceiver()方法,我們來運作一下代碼,運作結果如下圖所示:
    Android 源碼系列之&lt;二&gt;從安全的角度深入了解BroadcastReceiver(上)
            通過運作結果可知使用LocalBroadcastManager是正常的,那究竟為什麼說使用這種方式是安全高效的了?由于本篇博文篇幅有點長,在下篇文章Android源碼系列之<三>從安全的角度深度了解BroadcaseReceiver(下)來分析為什麼說使用LocalBroadcastManager是安全和高效的,敬請期待......