android permission權限與安全機制解析(上)

  總結整理了一下android權限相關的知識,因爲篇幅過長,分爲兩篇博客來寫,上篇博客主要是詳解權限和安全,下篇主要是介紹android6.0權限適配問題:

  android permission權限與安全機制解析(下)html

uses-permission

 用法爲

 爲了保證application的正常運行,須要系統授予app的權限聲明。這個權限是在用戶安裝應用的時候授予的。

 android:name的值能夠是其餘app經過 聲明的(用於兩個應用之間的交互),也能夠是系統的權限名稱,例如

android.permission.CAMERA或android.permission.READ_CONTACTS等等。須要注意的一點是uses-permission的權限要求說明,可能會引發app在Android Market中的過濾。

android:maxSdkVersion用來標註該權限所支持的最大api版本號,若是當從某個特定版本時,不須要該權限時就能夠加上該限制。

 系統權限列表有不少,各自的用途也不同,有幾個連接能夠參考一下:

  blog.csdn.net/zjl5211314/…

  developer.android.com/reference/a…

  developer.android.com/guide/topic…

  developer.android.com/guide/topic…
java

自定義permission


permission標籤



     android:icon=」drawable resource」
   android:label=」string resource」
   android:name=」string」
   android:permissionGroup=」string」
   android:protectionLevel=[「normal」 | 「dangerous」 |
            「signature」 | 「signatureOrSystem」] /> <
  android:description:對權限的描述,比lable更加的詳細,介紹該權限的相關使用狀況,好比當用戶被詢問是否給其餘應用該權限時。注意描述應該使用的是string資源,而不是直接使用string串。

  android:icon:用來標識該權限的一個圖標。

  android:label:權限的一個給用戶展現的簡短描述。方便的來講,這個能夠直接使用一個string字串來表示,可是若是要發佈的話,仍是應該使用string資源來表示。

  android:name:權限的惟一名字,因爲獨立性,通常都是使用包名加權限名,該屬性是必須的,其餘的可選,未寫的系統會指定默認值。

  android:permissionGroup: 權限所屬權限組的名稱,而且須要在這個或其餘應用中使用 標籤提早聲明該名稱,若是沒有聲明,該權限就不屬於該組。

  android:protectionLevel:權限的等級
linux


  • normal

  • 低風險權限,只要申請了就可使用(在AndroidManifest.xml中添加 標籤),安裝時不須要用戶確認;
  • dangerous

  • 高風險權限,安裝時須要用戶的確認纔可以使用;
  • signature

  • 只有當申請權限的應用程序的數字簽名與聲明此權限的應用程序的數字簽名相同時(若是是申請系統權限,則須要與系統簽名相同),才能將權限授給它;
  • signatureOrSystem

  • 簽名相同,或者申請權限的應用爲系統應用(在system image中),與signature相似,只是增長了rom中自帶的app的聲明 ,儘可能不要使用該選項,由於signature已經適合絕大部分的狀況。

  對於普通和危險級別的權限,咱們稱之爲低級權限,應用申請即授予。其餘兩級權限,咱們稱之爲高級權限或系統權限。當應用試圖在沒有權限的狀況下作受限操做,應用將被系統殺掉以警示。系統應用可使用任何權限。權限的聲明者可無條件使用該權限。

  下面經過指定一個BroadcastReceiver的權限來實驗,首先建立了兩個app:app A ,app B 。app A中註冊了一個BroadcastReceiver ,app B 發送消息,app A的manifest文件: android


 



















複製代碼


  app B 的manifest 文件內容 git


 












複製代碼


這樣app B 給app A 發送消息,A就能夠收到了,若未在app B的manifest文件中聲明使用相應的權限,app B發送的消息,A是收不到的。 另外,也可在app B 的manifest文件中聲明權限時,添加android:protectionLevel=「signature」,指定app B只能接收到使用同一證書籤名的app 發送的消息。 github


permission-tree標籤



       android:label=」string resource」
    android:name=」string」 />
    

  該標籤包含於在< manifest >標籤中。
  該標籤聲明權限樹的基礎名稱。 應用程序擁有樹中的全部名稱。 能夠經過調用 PackageManager.addPermission() 在權限樹中動態添加新的權限。 樹中的名稱以句點(’.’)分隔。 好比,假定基礎名稱爲com.example.project.taxes,則可加入相似如下格式的權限:

  com.example.project.taxes.CALCULATE
  com.example.project.taxes.deductions.MAKE_SOME_UP
  com.example.project.taxes.deductions.EXAGGERATE
注意本元素並非聲明權限,而只是爲後續要加入的權限定義一個命名空間。
數據庫

  android:icon
  表明樹中全部權限的圖標。 本屬性必須設爲對 Drawable 資源的引用。
  android:label
  供用戶閱讀的權限組名稱。 爲了方便起見能夠將其直接設爲字符串, 但在應用程序準備發佈時,應該設爲對字符串的引用,以便對其進行本地化。
  android:name
  權限樹的基礎名稱,用做樹中全部權限的前綴。 爲了保證名稱的惟一性,應該採用 Java 風格的域名規則。 名稱的路徑必須至少包含兩個句點分割的字段 — 好比:com.example.base 能夠,但 com.example 就不行。api

permission-group標籤

  Android在定義 permission 時, 爲每一個Permission都進行了分組,一個權限主要包含三個方面的信息:權限的名稱;屬於的權限組;保護級別。一個權限組是指把權限按照功能分紅的不一樣的集合。每個權限組包含若干具體 權限,例如在 COST_MONEY 組中包含 android.permission.SEND_SMS , android.permission.CALL_PHONE 等和費用相關的權限。具體以下SDK所示:
  developer.android.com/reference/a…
  再來看看源碼(在frameworks/base/core/res /AndroidManifest.xml):數組


 




複製代碼


  能夠看到,這邊先定義了一個permissiongroup : android.permission-group.COST_MONEY, 而後又定義了兩個permission :android.permission.SEND_SMS 和 android.permission.CALL_PHONE , 須要注意的是,這兩個權限中都有一個android:permissionGroup屬性,這個屬性就指定了這個權限所屬的PermissionGroup。瀏覽器

  android:description
  這個屬性用於給權限組定義一個用戶可讀的說明性文本。這個文本應該比標籤更長、更詳細。這個屬性不該該直接使用字串,而應該使用字符串引用。
  android:icon
  這個屬性定義了一個表明權限的圖標,這個屬性應該使用資源文件來定義。
  android:label
  這個屬性給權限組定義了一個用戶可讀的名稱。
  android:name
  這個屬性定義了權限組的名稱,它是可以分配給 元素的permissionGroup屬性的名稱。
  


安全機制


組件權限


  Android是一個權限分離的系統,這是利用Linux已有的權限管理機制,經過爲每個Application分配不一樣的uid和gid,從而使得不一樣的Application之間的私有數據和訪問(native以及java層經過這種sandbox機制,均可以)達到隔離的目的 。與此同時,Android 還在此基礎上進行擴展,提供了permission機制,它主要是用來對Application 能夠執行的某些具體操做進行權限細分和訪問控制,同時提供了per-URI permission 機制,用來提供對某些特定的數據塊進行ad-hoc方式的訪問。
  經過AndroidManifest.xml文件能夠設置高級權限,以限制訪問系統的全部組件或者使用應用程序。全部的這些請求都包含在你所須要的組件中的 android:permission屬性,命名這個權限能夠控制訪問此組件。


  1. Activity 權限 (使用 標籤) 限制可以啓動與Activity權限相關聯的組件或應用程序。在Context.startActivity()和Activity.startActivityForResult()期間檢查;

  2. Service 權限(應用 標籤)限制啓動、綁定關聯服務的組件或應用程序。此權限在Context.startService(),Context.stopService() 和 Context.bindService() 期間要通過檢查;

  3. BroadcastReceiver 權限(應用 標籤)可以爲相關聯的接收者組件或應用程序設置限制。在 Context.sendBroadcast() 返回後此權限將被檢查,同時系統設法將廣播遞送至相關接收者。所以,權限失敗將會致使拋回給調用者一個異常;它將不能遞送到目的地。爲了接收你的廣播,你請求一個接收器的應用程序必須持有要求的那個權限(如上面例子所示),並且sendBroadcast的相關幾個函數中也能夠加入permission參數用來指定只帶有該permission的接受者能夠接受該廣播。

  4. ContentProvider 權限(使用 標籤)用於限制可以訪問 ContentProvider 中的數據的組件或應用程序。若是調用者沒有請求權限,那麼會爲調用拋出一個安全異常( SecurityException )。數據庫自己的讀寫能夠處理多線程問題,可是數據的前後能夠考慮同步問題,設置android:multiprocess=」true」屬性來保證數據的正確性

  對於組件而言,除了使用權限限制與外部交互的實體外,還有一個屬性也具備該功能,那就是android:exported,這個屬性用於指示該服務是否可以被其餘應用程序組件調用或跟它交互。若是設置爲true,則可以被調用或交互,不然不能。設置爲false時,只有同一個應用程序的組件或帶有相同用戶ID的應用程序才能啓動或綁定該服務。

  它的默認值依賴於該服務所包含的過濾器。沒有過濾器則意味着該服務只能經過指定明確的類名來調用,這樣就是說該服務只能在應用程序的內部使用(由於其餘外部使用者不會知道該服務的類名),所以這種狀況下,這個屬性的默認值是false。另外一方面,若是至少包含了一個過濾器,則意味着該服務能夠給外部的其餘應用提供服務,所以默認值是true。

權限檢測

  首先是root用戶和system用戶擁有全部的接口調用權限,而後對於其它用戶可使用如下這幾個函數來檢測 :

  PackageManager.checkPermission (String permName, String pkgName)

  用來檢測一個package中是否授予了指定permission。

  Context.checkCallingOrSelfPermission (String permission)

  用來檢測本身或者調用進程中是否授予了指定permission。

  Context.checkCallingOrSelfUriPermission (Uri uri, int modeFlags)

  用來檢測本身或者調用進程中是否授予了一個uri經過modeFlags指定的permission。
  Context.checkCallingPermission (String permission)
   檢查正在處理的調用者進程是否授予指定permission 權限,若是調用者是本身那麼返回 。
  Context.checkCallingUriPermission (Uri uri, int modeFlags)
  用來檢測調用進程中是否授予了一個uri經過modeFlags指定的permission。
  Context.checkPermission (String permission, int pid, int uid)
  用來檢測指定uid和pid的進程中是否授予了指定的permission。

  checkSelfPermission (String permission)
  23版本api添加,用來檢測本身是否授予了指定permission

  Context.checkUriPermission (Uri uri, int pid, int uid, int modeFlags)
  用來檢測指定uid和pid的進程中是否授予了一個uri經過modeFlags指定的permission。

  Context.checkUriPermission (Uri uri, String readPermission, String writePermission, int pid, int uid, int modeFlags)

  比上面的方法多了一個檢測permission的功能,至關於同時調用checkPermission(String, int, int)和checkUriPermission(Uri, int, int, int)。
-------------
   下面這一組和上面一一對應,區別就在於若是遇到檢查不經過時,會拋出異常,打印消息 :
  Context.enforceCallingOrSelfPermission(String permission, String message)
  Context.enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message)
  Context.enforceCallingPermission (String permission, String message)
  Context.enforceCallingUriPermission (Uri uri, int modeFlags, String message)
  Context.enforcePermission (String permission, int pid, int uid, String message)

  Context.enforceUriPermission (Uri uri, int pid, int uid, int modeFlags, String message)

  Context.enforceUriPermission (Uri uri, String readPermission, String writePermission, int pid, int uid, int modeFlags, String message)
-----------
  Context.grantUriPermission (String toPackage, Uri uri, int modeFlags)
  爲指定package添加訪問指定uri 的讀或者寫權限
  Context.revokeUriPermission (Uri uri, int modeFlags)
  爲指定package刪除訪問指定uri 的讀或者寫權限。
  以上函數中check開頭的,只作檢查。enforce開頭的,不單檢查,沒有權限的還會拋出異常。
  checkPermission相關函數

  1. 若是傳入的 permission 名稱爲 null ,那麼返回PackageManager.PERMISSION_DENIED 。

  2. 判斷調用者uid是否符合要求 。

  • 若是uid爲0,說明是root權限的進程,對權限不作控制。

  • 若是uid爲system server進程的uid,說明是system server,對權限不做控制。
  • 若是是 ActivityManager 進程自己,對權限不做控制。
  • 若是調用者uid與參數傳入的req uid不一致,那麼返回PackageManager.PERMISSION_DENIED。
  • 若是經過 2 的檢查後,再調用 PackageManagerService.checkUidPermission ,判斷這個uid是否擁有相應的權限,分析以下 :
    • 首先它經過調用getUserIdLP,去PackageManagerService.Setting.mUserIds數組中,根據uid查找uid(也就是package)的權限列表。一旦找到,就表示有相應的權限。
  • 若是沒有找到,那麼再去PackageManagerService.mSystemPermissions中找。這些信息是啓動時,從/system/etc/permissions/platform.xml中讀取的。這裏記錄了一些系統級的應用的 uid 對應的 permission 。
  • 返回結果 。
  •   CheckUriPermission函數
    1. 若是uid爲0,說明是root用戶,那麼不控制權限。

  • 不然,在ActivityManagerService維護的mGrantedUriPermissions這個表中查找這個uid是否含有這個權限,若是有再檢查其請求的是讀仍是寫權限。
  • URI權限


      到目前爲止咱們討論的標準的permission系統對於content provider來講是不夠的。一個content provider可能想保護它的讀寫權限,而同時與它對應的直屬客戶端也須要將特定的URI傳遞給其它應用程序,以便其它應用程序對該URI進行操做。一個典型的例子就是郵件程序處理帶有附件的郵件。進入郵件須要使用permission來保護,由於這些是敏感的用戶數據。然而,若是有一個指向圖片附件的URI須要傳遞給圖片瀏覽器,那個圖片瀏覽器是不會有訪問附件的權利的,由於他不可能擁有全部的郵件的訪問權限。
      針對這個問題的解決方案就是per-URI permission:當啓動一個activity或者給一個activity返回結果的時候,呼叫方能夠設置Intent.FLAG_GRANT_READ_URI_PERMISSION和/或 Intent.FLAG_GRANT_WRITE_URI_PERMISSION ( 另一篇intent博客中也有介紹到)。這會使接收該intent的activity獲取到進入該Intent指定的URI的權限,而不論它是否有權限進入該intent對應的content provider。
      這種機制容許一個一般的capability-style模型,這種模型是以用戶交互(如打開一個附件, 從列表中選擇一個聯繫人)爲驅動,特別獲取fine-grained permissions(更細粒化的權限)。這是一種減小沒必要要權限的重要方式,這種方式主要針對的就是那些和程序的行爲直接相關的權限。
      這些URI permission的獲取須要content provider(包含那些URI)的配合。強烈推薦在content provider中提供這種能力,並經過android:grantUriPermissions或者 標籤來聲明支持。

    android:sharedUserId


      安裝在設備中的每個apk文件,Android給每一個APK進程分配一個單獨的用戶空間,其manifest中的userid(userid的特色: 做爲APK身份的標識 ;userid對應一個Linux用戶,因此不一樣APK(用戶)間互相訪問數據默認是禁止的)就是對應一個Linux用戶都會被分配到一個屬於本身的統一的Linux用戶ID,而且爲它建立一個沙箱,以防止影響其餘應用程序(或者其餘應用程序影響它)。用戶ID 在應用程序安裝到設備中時被分配,而且在這個設備中保持它的永久性。因此讓兩個apk使用相同的userID,這樣它們就能夠看到對方的文件,若是在此基礎之上再加上相同的android:process,他們就能夠運行在一個進程中,可以作更多的事情了。爲了節省資源,具備相同ID的apk也能夠在相同的linux進程中進行(注意,並非必定要在一個進程裏面運行),共享一個虛擬機。
      因此當兩個應用公用一個userid的時候就可使用SharedPreference之類的機制進行文件數據共享:

    //第一個應用程序爲的menifest文件代碼以下:
    
       
       
       
    
      
       
      //第二個應用程序的menifest文件代碼以下: 
     
        
    
      複製代碼
      上面給出了兩個程序的menifest定義,二者共用一個ShareUserId,下面咱們看看從一個程序裏面如何獲取另一個程序的Context。假設咱們從package=「com.android.demo_A」的程序獲取package=」com.android.demo_B」的程序的context:
    Context ct=this.createPackageContext("com.android.demo_B", Context.CONTEXT_IGNORE_SECURITY);複製代碼

      這樣咱們就可以獲取到另一個應用的Application context了,獲取到上下文以後就可以實現數據共享和通訊,具體能夠查看個人 IPC通訊博客

    總結

      只要signature相同,就算不顯式聲明 也能access設定了normal或dangerous權限設定的數據或功能。
      擁有system級別權限的使用者能夠access其餘普通signature權限聲明設定過的功能。因此,設定爲擁有system級別權限便可。
      應用程序安裝的時候,應用程序請求的permissions是經過package installer來批准獲取的。package installer是經過檢查該應用程序的簽名來肯定是否給予該程序request的權限。在用戶使用過程當中不會去檢查權限,也就是說要麼在安裝的時候就批准該權限,使其按照設計可使用該權限;要麼就不批准,這樣用戶也就根本沒法使用該feature,也不會有任何提示告知用戶嘗試失敗。
    例如高級權限用有system級別權限設定的api時,須要使其apk擁有system權限。好比在 android 的API中有供給SystemClock.setCurrentTimeMillis()函數來修改系統時間。有兩個方法:
      第1個方法簡單點,不過須要在Android系統源碼的狀況下用make來編譯:

    • 在應用程序的AndroidManifest.xml中的manifest節點中插手android:sharedUserId=」android.uid.system」這個屬性。
    • 修改Android.mk文件,插手LOCAL_CERTIFICATE := platform這一行
    • 使用mm命令來編譯,生成的apk就有修改系統時間的職權範圍了

    • 第2個方法麻煩點,不過不用開虛擬機跑到源碼環境下用make來編譯:

      • 同上,插手android:sharedUserId=」android.uid.system」這個屬性。
      • 使用eclipse編譯出apk文件,可是這個apk文件是不能用的。
      • 用壓縮軟件打開apk文件,刪掉META-INF目錄下的CERT.SF和CERT.RSA兩個文件。
      • 使用目標系統的platform密鑰來從新給apk文件簽名。這步比較麻煩,首先找到密鑰文件,在個人Android源碼目錄中的位置是」build/target/product/security」,下面的platform.pk8和platform.x509.pem兩個文件。而後用Android提供的Signapk工具來簽名,signapk的源代碼是在」build/tools/signapk」下,用法爲」signapk platform.x509.pem platform.pk8 input.apk output.apk」,文件名最好使用絕對路徑防止找不到,也能夠修改源代碼直接使用。

      • 引用文章:


        blog.csdn.net/xyz_lmn/art…

        blog.csdn.net/t12x3456/ar…

        yelinsen.iteye.com/blog/101274…

        berdy.iteye.com/blog/178285…

        my.oschina.net/u/589963/bl…

        blog.csdn.net/vshuang/art…

        www.chawenti.com/articles/12…

        ee.ofweek.com/2012-04/ART…

        blog.csdn.net/liujian885/…

        maoruibin.github.io/%E6%8A%80%E…

        www.zhihu.com/question/37…

        blog.csdn.net/wirelessqa/…

        相關文章
        相關標籤/搜索