Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 源碼系列之(二)從安全的角度深入理解BroadcastReceiver(上)

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

編輯:關於Android編程

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

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

定義自己的BroadcastReceiver並實現onReceive()方法在AndroidManifest.xml中靜態注冊或者在代碼中動態注冊調用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的廣播,代碼如下:


    

    
        
            
                
                
            
        
        
        
            
                
            
        
    

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

<framelayout android:background="#aabbcc" android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android"></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);
	}
}
以上步驟完成之後就可以運行項目了,打開模擬器或者鏈接手機,運行一下,效果如下圖所示:\

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

到這裡我們可以說只是掌握了對廣播的使用,如果在項目中我們也是按照同樣的方式使用了廣播,那麼你的項目可能會存在風險。為什麼這麼說,下面我們來假設一個場景,假如別人反編譯了我們的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打印出來了,如下圖所示:\

 

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

給廣播接收器添加export屬性

    
        
    
在上邊的代碼中我們只是在注冊我們的CustomBroadReceiver的時候添加了export屬性,並把該屬性的值設置為false,難道僅僅就添加一個屬性就OK啦?可能有的童靴還是會產生疑問,為了打消童靴們的疑慮,我們再運行一下代碼,查看一下結果,運行結果如下圖所示:
\\
通過分別運行工程A和工程B下的logcat輸出日志可以看出,給我們自定義的receiver添加了export屬性後確實不能通過這種方式來使我們的廣播接收器發生響應了。那這個屬性到底是干嘛使的了?可能有的童靴又會有這樣的疑問了,我們來看一下官方文檔對這個export屬性是怎麼解釋的。
\
英語水平有限,大致翻譯一下官方文檔:
當前的廣播接收器是否可以從外部應用接受消息,如果為true表示可以從外部應用接受消息,如果為false,表示當前BroadcastReceiver只能從當前應用或者是擁有相同userID的應用接收廣播。默認值是根據當前BroadcastReceiver是否包含有intent-filter來決定的,如果沒有任何intent-filter的話,只能通過類名來調起,此時默認值為false,如果包含有intent-filter,這默認值為true。不僅這個屬性可以指定BroadcastReceiver是否暴露給其他用戶,你也可以使用permission來限制外部應用給當前應用的receiver發送消息。
在官方文檔結尾又說了除了使用export屬性外,我們還可以以添加權限的方式來限制外部引用給我們的receiver發送消息,那我們就接著往下看是如何使用權限的。給廣播接收器添加自定義權限
a)首先自定義權限,代碼如下:
b)其次給receiver添加permission權限,代碼如下:

    
        
    
c)運行代碼,查看結果如下:
\\
通過觀察確實發現了通過添加自定義權限的方式那麼B程序確實是調用不起來A程序中的廣播的,呵呵,看到這裡我們高興的小心髒撲通撲通直跳。如果是這樣,那你就錯了,因為在A程序中我們確實給我們自定義的廣播添加了自定義權限,就是說擁有該權限的應用才可以,那假如我們在B程序中也同樣定義了同樣的權限,那結果會是神馬樣子呢?我們繼續來做實驗驗證一下,這次我們也把B程序添加同樣的權限,然後再使用權限,代碼如下:


    

    
    
    
    
        
            
                
                
            
        
    

我們通過這樣方式後再分別運行一下程序,分別運行一下,運行結果如下:
\\
唉,fuck,B程序又可以調起我們的廣播,這時候你可能心中有一萬匹草泥馬在奔騰了,這廣播還讓人不讓人用了,呵呵,我們這片文章就是從安全的角度來講解BroadcastReceiver的,肯定是有對策啦,還記得我們在A程序中定義的權限麼?如果你不記得了,代碼如下:
定義我們自己的權限時我們給權限使用的級別是normal,如果你對有關權限的級別不是太理解或不屬性,可以去官網查看,這裡不再詳述了,通過查閱官網可知,權限級別有個為signature的,也就是說如果我們在自定義權限的時候把權限級別設置為signature時,只有擁有相同簽名的APP才能調起我們的應用,這就相對的保證了安全,因為我們發版的時候肯定用的是自己的簽名,別人一般情況下是無法獲取到我們的簽名的,所以這種方法是可行的。這時候你肯定心裡樂開了花,真是魔高一尺道高一丈呀,別著急,我們還有另外一種更可靠高效的解決方式,這也是我開發中一直用到的,請繼續往閱讀(*^__^*) ……
使用官方推薦的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()方法,我們來運行一下代碼,運行結果如下圖所示:
\
通過運行結果可知使用LocalBroadcastManager是正常的,那究竟為什麼說使用這種方式是安全高效的了?由於本篇博文篇幅有點長,在下一篇文章中我會從源碼的角度來分析為什麼說使用LocalBroadcastManager是安全和高效的,敬請期待......

 

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved