編輯:關於Android編程
Android系統內置的安全策略可以有效的降低應用程序的安全問題。所以默認創建的應用程序已經包含了一定程度的安全保護措施。
Android所包含的安全策略有:
應用程序沙箱,它可以使APP的數據、代碼與其它APP相互隔離。 應用程序框架對於常見防護措施的強大實現,比如密碼、權限以及安全的IPC機制。 一些安全技術的應用,比如ASLR, NX, ProPolice, safe_iop, OpenBSD dlmalloc, OpenBSD calloc, 以及Linux mmap_min_addr,可以降低常見的內存管理錯誤風險。 文件加密系統可以保證設備在丟失後其中的數據不被盜取。 用戶權限授予可以限制訪問系統特性以及用戶數據。 Android定義的權限可以控制程序的數據只能在程序的控制范圍之內。本文中所提及的Android安全策略對於日常開發非常重要,應養成良好的編碼習慣。在日常的編碼行為中使用以下策略可以有效降低無意中造成的安全風險。
應用程序常見的安全問題就是它們的數據是否可被其它應用程序訪問到。Android有3種基本的數據存儲方式。
默認情況下,由APP自己創建的文件只能由APP本身訪問。這種防護措施由Android框架實現,也足以應對大多數應用的安全情況。
通常應當避免為IPC文件使用MODE_WORLD_WRITEABLE模式或MODE_WORLD_READABLE模式,因為這些模式沒有對數據的訪問提供限制能力,也沒有在數據格式上做任何控制。如果需要在進程間共享數據,你應當考慮使用ContentProvider。ContentProvider對APP提供了數據讀寫權限,也可以按照實際情況動態的授予權限。
如果需要對一些較為敏感的數據提供額外的保護,你可以使用密鑰對本地文件進行加密。比如,可以將密鑰放入KeyStore,並以用戶密碼將其保護起來。不過這種方案也不是萬能的:某些破解手段可以監視用戶所輸入的密碼,不過這種方式可以對丟失的設備進行保護。
在外部存儲器上創建的文件可被全局讀寫。因為外部存儲器可被用戶移除,也可以被任何的應用程序修改,所以不應當在外部存儲器上存儲敏感的用戶數據。
正因為可能存在不可信資源,所以從外部存儲器中讀取數據時應當執行輸入驗證。我們強烈的建議不要在外部存儲器上存儲用於動態加載的可執行文件或者類文件。如果APP需要從外部存儲器上接收可執行文件,那麼這些文件應當是被簽過名的,在加載之前也應當對這些簽名進行校驗。
ContentProvider提供了一種結構化的存儲機制,這種機制可以對所有的應用程序形成一種約束。如果不打算使其它的應用程序訪問你的ContentProvider,那麼只需在程序的清單文件中標記ContentProvider的屬性android:exported=false即可。否則,設置android:exported=true便意味著其它應用程序可以訪問其中的數據。
在創建ContentProvider時默認允許其它應用程序可以訪問其中的數據,你可以對讀或者寫進行單一權限限制,也可以同時管理。
如果使用ContentProvider只是為了在自身的APP之間共享數據,更加合理的方式是將android:protectionLevel屬性值設置為”signature”。Signature權限不需要用戶確認,所以這樣可以提供良好的用戶體驗,以及更多的控制力。
ContentProvider還可以通過android:grantUriPermissions屬性提供更細粒度的訪問能力。訪問者應當在Intent中使用FLAG_GRANT_READ_URI_PERMISSION標志或FLAG_GRANT_WRITE_URI_PERMISSION標志進行訪問。這些權限的范圍可被做更進一步的限制。
當訪問ContentProvider時,使用參數化方法比如query(), update(), 及delete()可以避免不可信來源的SQL注入。
不要對寫入權限的安全擁有錯誤的認知。考慮一下,寫入權限允許執行SQL語句,這可能會使一些數據被確認。比如,一名攻擊者可能會在通話記錄中查找一個指定的電話號碼是否存在,如果這條數據存在,那麼攻擊者只需進行修改便可得知結果。如果ContentProvider的數據結構可被猜測出,那麼寫入權限就相當於也同時提供了讀取權限。
因為Android每個應用都處於沙箱之內,所以如果需要共享資源與數據的話,應用必須顯式的聲明自有權限。
我們推薦應用程序所需的權限越少越好。這樣可以降低權限濫用的風險,也更易讓用戶接受,也可以減少黑客的攻擊入口。通常情況下,如果某個權限不是必要的,那就不要去請求它。
如果應用程序可以做到不需要任何權限,那麼這是最完美的。比如,如果需要通過訪問設備信息的方式來創建唯一標識符的話,我們更推薦GUID。又比如,相比於將數據存儲於外部存儲器,我們更推薦內部存儲器。
另外在請求權限時,可以使用< permissions>來保護IPC。IPC對於安全特別敏感、薄弱,並且它會被暴露給其它應用程序,比如ContentProvider。 除了可能需要用戶確認的權限之外,我們更推薦使用訪問控制,因為這些權限可能會使用戶感到困惑、不解。比如,可以考慮對個人開發者的應用程序使用”signature“的IPC權限保護等級。
不要洩露受保護的權限數據。這種情況僅會發生在通過IPC共享數據時:因為它擁有特殊的權限,並且對於任何的IPC接口的客戶端也沒有要求出示該權限。
More details on the potential impacts, and frequency of this type of problem is provided in this research paper published at USENIX:http://www.cs.berkeley.edu/~afelt/felt_usenixsec2011.pdf
通常情況下應當盡量少的使用權限,少用權限就意味著更安全。創建權限這種事情對於大多數應用來說是用不到的,因為系統定義的權限足以涵蓋所有情況,這些權限會提供訪問檢查。
如果必須創建權限,考慮是否可以使用”signature”保護等級完成你的所需任務。”signature”會將自身完全暴露給用戶,只允許具有相同簽名的應用程序訪問。
如果要創建”dangerous”保護等級的權限,那麼有些東西需要考慮在內:
權限必須提供一段簡短的描述該權限安全的字符串。 描述權限的字符串必須提供不同地區的語言。 如果權限的描述含糊不清或者用戶認為這會為其帶來風險,那麼用戶可能會選擇不安裝應用。 如果權限的生成器沒有安裝的話,應用程序可能會請求權限。上面的每一條對於作為程序員的你都是一項重要的非技術性挑戰,這樣做可能會使用戶感到困惑,這就是為什麼我們不鼓勵使用”dangerous”權限等級的原因。
網絡傳輸本身就存在安全風險,因為它會包括用戶的隱私數據。人們越來越關心移動設備的隱私問題,尤其是執行網絡傳輸時,所以APP需要至始至終以最佳的安全方案保護用戶的數據安全。
Android所處的網絡環境與其它的Linux系統有著很大的不同。主要考慮的就是選用適用於敏感數據傳輸的協議。比如HttpsURLConnection用於安全的WEB通信。我們推薦在支持HTTPS,因為移動設備會頻繁的連接到不可信網絡,比如公共的Wi-Fi熱點。
經認證,Socket等級的加密通訊可以使用SSLSocket類簡單實現。鑒於Android設備會頻繁的連接到不安全的無線網絡,所以我們強力的建議對所有的應用程序都使用安全的網絡實現。
我們也見過一些應用在處理敏感的IPC上使用了localhost網絡端口。我們不鼓勵使用這種方式,因為這些接口能被其它應用程序訪問到,應當使用Android的IPC機制。
還有一個常見的問題就是,根證書重復用於驗證從HTTP或者其它網絡上下載的不可信數據。這也包括WebView以及HTTP請求響應的輸入驗證。
SMS(短消息服務)協議主要用於個人對個人之間的通訊,它並不適用於APP的數據傳輸。由於SMS的限制,我們強烈的推薦使用Google Cloud Messaging(GCM)及IP網絡來傳輸服務器與設備之間的數據。
要注意,SMS既沒有對網絡和設備進行加密也沒有對其進行相關驗證。尤其是,任何的SMS接收器應當考慮到會有一位惡意的用戶會發送SMS給你的應用,不要相信沒有驗證過的SMS數據,並用它們來執行一些敏感操作。還有,你應當意識到SMS可能會在網絡上被攔截並被篡改。在Android設備內,SMS消息是由廣播意圖傳送的,所以這些數據可以被擁有READ_SMS權限的應用程序讀取到。
無論程序運行在哪個平台上,沒有執行充分的輸入驗證都是影響程序安全的主要原因之一。Android提供了平台等級的安全策略,這可以降低應用程序輸入驗證問題的暴露率,應用程序應當盡可能的使用這些平台特性。還應該注意:安全性語言的選擇傾向於減少輸入驗證問題的可能性。
如果你在使用本地代碼,那麼從文件中讀取的數據、從網絡上接收的數據或從IPC接收的數據都可能會引起安全問題。最常見的問題就是緩沖區溢出,使用釋放後的對象等等。Android提供了大量的相關技術手段比如ASLR(Address Space Layout Randomization)、DEP(Data Execution Prevention),這些技術手段可以降低這些錯誤的出現頻率,但是它們不會解決根本的內在問題。你可以謹慎的處理指針或管理緩沖區來防止這些問題的出現。
如果使用SQL數據庫或者ContentProvider進行數據查詢,那麼SQL注入可能是個問題。避免這些問題最好使用參數化的查詢方法。將權限降為只讀或只寫可以降低SQL注入帶來的相關風險。
如果不能夠使用以上提及的安全建議,那麼我們強烈推薦使用結構良好的數據格式,並在使用時對這些數據格式進行驗證。
通常來說,對於用戶的數據安全最好的方法就是盡量少使用可以訪問到敏感數據或者用戶數據的API。如果可以避免存儲或者傳輸這些信息,那麼就不要傳輸這些數據。可考慮應用程序是否能夠實現這麼一種邏輯:使用數據的哈希碼或者一種不可逆的數據形態。比如,程序可能會實現這麼一種邏輯:將電子郵件地址的哈希碼作為一個關鍵的值,這樣可以避免使用原有的電子郵件地址,這可以降低暴露數據的機會,也可以降低攻擊者入侵應用程序的機會。
如果程序需要訪問用戶的個人信息,比如密碼或者用戶名等等,要記住一些組件可能會要求你提供相關的隱私政策,來解釋這些數據的使用與存儲。所以接下來數據訪問實踐可以簡化遵循規則。
你還應當考慮應用程序是否有無意中將用戶的個人數據暴露給了第三方。如果你不清楚這些第三方組件或服務為什麼要使用用戶的信息,那麼就不要提供給它們。通常降低個人信息的訪問可以降低這塊的安全風險。
如果必須要訪問敏感數據,那麼評估一下這些信息是否有必要傳輸到服務器,或者是否這些操作只需在客戶端執行就可以。考慮凡是涉及到用戶敏感數據的代碼都在客戶端執行,這樣可以避免不必要的網絡傳輸和安全洩漏問題。
還有,要確保沒有將用戶數據通過IPC、全局可讀寫文件或者網絡Socket暴露給其它應用程序。
如果需要GUID,可以創建一個大的唯一的數據將其保存下來。不要使用與電話有關的數字,比如電話號碼、IMEI,這些信息可能會與用戶信息有關。
寫入設備日志時要當心。在Android中,日志是共享資源,可被具有READ_LOGS權限的應用讀取到。盡管日志數據是臨時的,但是不恰當的用戶信息日志可能會無意中將信息洩露給其它應用程序。
因為WebView會解析像HTML和JavaScript這樣的web內容,所以不正確的使用會帶來常見的安全隱患。Android提供了大量機制來收縮這些潛在問題的范圍,比如通過限制WebView的能力來達到最低的運行需求。
如果程序沒有直接使用到WebView中的JavaScript,那麼不要調用setJavaScriptEnabled()。一些示例代碼可能會使用該方法,所以如果不需要的話,在你的程序中關閉它。默認情況下,WebView不會執行JavaScript,所以不會發生跨站腳本攻擊。
使用addJavaScriptInterface()需要特別當心,因為它會允許JavaScript調用預留給Android原生應用的方法。如果你使用它,要確認所有的Web的相關內容都是可信的。如果不可信的內容允許進入,那麼不可信的 JavaScript 可能會調用App內的相關方法。一般我們推薦在APK內包含JavaScript代碼的時候使用addJavaScriptInterface()。
如果程序通過WebView訪問敏感數據,你可能需要使用clearCache()方法來刪除存儲在本地的所有文件。我們可以使用HTTP頭部的某些屬性比如no-cache來表示應用程序不應當緩存某些特殊的內容。
Android 4.4之前版本的webkit含有大量的安全問題。如果App運行在這些版本上,應當確認WebView所渲染的內容都是可信的。如果應用必須渲染開放的Web內容,考慮實現一個獨有的渲染器,這樣可以及時更新相關的安全補丁。
通常來說,我們不推薦頻繁的要求用戶憑證,推薦使用授權令牌。
用戶名及密碼通常不應該存在本地。應當使用用戶輸入的用戶名及密碼進行初始化驗證,然後使用一個權限Token進行通信。
如果一個賬戶需要被多個程序訪問,那麼應當使用AccountManager。如果可能的話,使用AccountManager與服務進行交互,絕不要將密碼存在設備上。
如果證書只是用作於你創建的應用程序,那麼可以使用checkSignature()方法進行程序訪問驗證。如果只有一個程序使用了證書,那麼KeyStore可能更適合你。
除了數據隔離、文件系統加密、安全通信通道等保護手段之外,Android還提供了一系列通過算法加密的安全保護措施。
通常情況下,Android內置的最高等級的安全實現已經可以支持各種安全情況。如果需要從一個已知的位置接受一個文件,那麼HTTPS URI已經足夠。如果需要一條安全通道,考慮使用HttpsURLConnection或SSLSocket。
如果發現確實需要實現自己的安全協議,不建議自己實現加密算法。而應當使用已有的加密算法比如在Cipher中提供的AES加密算法或RSA加密算法。
使用安全隨機數生成器SecureRandom來初始化密鑰KeyGenerator。如果不使用由SecureRandom生成的密鑰的話,則會大大減弱加密算法的健壯性,也更容易遭受線下攻擊。
如果需要將密鑰存在本地以便後續使用,那麼可以使用KeyStore類似的加密機制。
一些App嘗試使用傳統的Linux技術比如網絡Socket和共享文件來實現IPC。我們對此推薦使用Android為IPC實現的系統功能,例如Intent,Binder,Messenger和BroadcastReceiver。Android的IPC機制允許驗證連接到你IPC的程序的身份,並且可以對每個IPC機制設置安全策略。
許多安全元素通過IPC機制共享。如果你的IPC機制的目的不是為了供其他應用程序使用,那麼請將對應元素的android:exported的屬性設置為false。這對於由相同UID的多進程組成的應用很有幫助,或者對後期再開啟該功能也有一定的幫助。
如果IPC的目的是為了可被其他應用訪問,你可以通過使用< permission>元素指定安全策略。如果IPC只是為了在持有相同key的兩個自有應用中使用,那麼android:protectionLevel=”signature”則更為適合。
Intent是異步IPC的首選機制。這取決於程序的需求,你可能會使用sendBroadcast(), sendOrderedBroadcast()或者顯式Intent來指定應用程序組件。
要注意,由於有序廣播可以被接收方消耗掉,所以這些廣播可能不會被分發給所有的應用程序。如果你發送了一個廣播,而該廣播必須被指定接收器接收的,那麼必須使用顯式Intent,並且該Intent還需指明廣播接收器的名稱。
發送Intent的一方可以驗證接收方是否允許非空的權限方法調用。只有含有該權限的應用才會接收到該Inetnt。如果廣播中的Intent的數據是敏感的,那麼應當考慮這裡所使用的權限不會被非法的應用程序攔截。在這種情況下,你應當考慮直接調用接收器,而不是使用廣播。
**Note:**Intent filters不應當被視作為一種安全特性:因為組件可能會由顯式的Intent調起。你應當在收到Intent的地方執行輸入驗證來確認該Intent是否被格式化為適用於調用廣播接收器,服務或者Activity的格式。
Service經常被用作支持其他程序的功能。每個Service必須在它的清單文件中有相應的聲明。
默認情況下,Service不是開放的,也不能夠被其它應用程序調起。然而,如果在Service的聲明中添加了IntentFilter,那麼默認就會被暴露出去。最好的辦法就是顯式的聲明android:exported屬性為你需要的屬性。Service還可以由android:permission屬性保護。如果這麼做了,那麼其它的程序則需要在它們的清單文件中聲明對應的權限才可以啟動、停止或者綁定該服務。
Service還可以保護在其權限內的IPC調用,在執行這個調用的實現之前調用checkCallingPermission()進行必要檢查。我們通常推薦使用在清單文件中聲明的權限,因為有很多漏洞會被忽略。
Binder、Messenger是遠程過程調用的首要IPC機制。它們提供了一種定義良好的接口:可以進行端對端相互認證。
我們強烈推薦以不需要特定的權限檢查的方式設計接口。由於Binder、Messenger並不是在應用的清單文件中聲明過的,因此不能對其采用特定權限。它們的權限通常來自於對應的Service或Activity所聲明的權限。如果你創建了一個用於請求驗證或者訪問控制器的接口,那麼這些控制器必須顯式的添加在Binder、Messenger的接口中。
如果創建了一個需要訪問控制器的接口,使用checkCallingPermission()驗證調用者是否含有所需的權限。這在訪問服務的遠端代理之前尤其重要,正如你的應用的身份需要傳給其它接口一樣。如果調用一個由Service提供的接口,那麼如果你沒有訪問給定服務所需的權限,那麼bindService()的調用可能會失敗。如果調用一個由自身APP提供的一個本地的接口,那麼clearCallingIdentity()則可以滿足內部安全檢查的需求。
有關更多執行與服務有關的IPC的相關信息,請參見Bound Services。
BroadcastReceiver用於處理由Intent發起的異步請求。
默認情況下,接收器可以被任何應用調起。如果你的廣播接收器的作用是給其它程序使用,那麼可能需要對接收器采取一些安全措施:在清單文件中添加相應的安全權限。這可以防止沒有正確權限的應用程序發送Intent給廣播接收器。
Dalvik是Android的運行時虛擬機。雖然Dalvik專用於Android,但是其它虛擬機上的相關安全問題也同樣適用於Android。一般不需要關心與虛擬機相關的安全問題,因為Android的應用程序運行在安全的沙箱環境中,所以系統上的其它進程訪問不到程序的代碼或者私有數據。
如果你對更深的虛擬機安全課題感興趣,那麼推薦熟悉一些現有的有關這一課題的相關文獻。其中最受歡迎的兩個資源如下:
- http://www.securingjava.com/toc.html
- asp.org/index.php/Java_Security_Resources">https://www.owasp.org/index.php/Java_Security_Resources
這篇文檔主要關注於Android的特殊之處和與其它虛擬機環境有什麼不同。對於對其它虛擬機很有經驗的開發者來說,這裡有兩個很主要的Android的不同之處:
一些虛擬機,比如JVM或.net運行時,扮演了一個安全邊界的角色,從底層操作系統將代碼、功能隔離。然而在Android中Dalvik虛擬機並沒有這樣的功能——應用程序沙箱實現與操作系統層面,所以Dalvik可以與應用的本地代碼進行交互,而沒有任何的安全限制。 由於移動設備有限的存儲空間,一些開發者可能需要通過模塊化構建應用程序,並使用動態加載技術。如果這麼做,那麼則需要考慮在哪接收應用的邏輯代碼?又應當將這些代碼存在哪?不要使用沒有經過驗證的代碼,比如從不安全的網絡資源上或者是外部存儲器中加載的代碼,因為這些代碼很有可能會被其它程序篡改。一般我們推薦使用Android SDK來進行應用程序開發,而不是使用本地代碼開發。由本地代碼構建的程序會更加復雜,也缺少了靈活性,也更容易產生像緩沖區溢出等常見的內存洩露錯誤。
因為Android建立於Linux kernel基礎之上,所以如果使用本地代碼的話,那麼Linux開發中所遇到的安全問題也同樣適用於此。由於Linux安全相關超出了本文的范圍,所以這裡提供了很受歡迎的“Linux和Unix如何安全編程”的相關資源,相關地址:http://www.dwheeler.com/secure-programs.
Android與其它大部分Linux環境最大的不同就在於程序沙箱。在Android中,所有的程序都運行在程序沙箱中,也包括那些本地代碼。在最基本的層面上,對熟悉Linux的開發者來說,不同之處就是Android的每個應用程序都有一個唯一UID,也擁有少量的權限。如果要使用本地代碼開發的話,那麼應當對應用的權限極為了解才對。
接著AppWidget基礎學習,今天是一個“進階版”的小例子,用來檢驗一下自己的學習效果。於是就做了一個擲骰子的Widget。方便大家觀看,先截圖如下: &nb
在Android中,除了使用Java.NET包下的API訪問HTTP服務之外,我們還可以換一種途徑去完成工作。Android SDK附帶了Apache的HttpClien
點擊按鈕,先自動進行下拉刷新,也可以手動刷新,刷新完後,最後就多一行數據。有四個選項卡。前兩天導師要求做一個給本科學生預定機房座位的app,出發點來自這裡。做著做著遇到很
JSON定義:JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。它基於JavaScript(Standard ECMA-262