第九章 Android安全访问机制


Android是一个多进程系统,在这个系统中,应用程序(或者系统的部分)会在自己的进程中运行。系统和应用之间的安全性通过Linux的facilities(工具,功能)在进程级别来强制实现的,比如会给应用程序分配user ID和Group ID。更细化的安全特性是通过"Permission"机制对特定的进程的特定的操作进行限制,而"per-URI permissions"可以对获取特定数据的access专门权限进行限制。 所以,应用程序之间的一般是不可以互相访问的,但是anroid提供了一种permission机制,用于应用程序之间数据和功能的安全访问。


9.1 安全架构


Android安全架构中一个中心思想就是:应用程序在默认的情况下不可以执行任何对其他应用程序,系统或者用户带来负面影响的操作。这包括读或写用户的私有数据(如联系人数据或email数据),读或写另一个应用程序的文件,网络连接,保持设备处于非睡眠状态。

一个应用程序的进程就是一个安全的沙盒。它不能干扰其它应用程序,除非显式地声明了“permissions”,以便它能够获取基本沙盒所不具备的额外的能力。它请求的这些权限“permissions”可以被各种各样的操作处理,如自动允许该权限或者通过用户提示或者证书来禁止该权限。应用程序 需要的那些“permissions”是静态的在程序中声明,所以他们会在程序安装时被知晓,并不会再改变。
所有的Android应用程序(。apk文件)必须用证书进行签名认证,而这个证书的私钥是由开发者保有的。该证书可以用以识别应用程序的作者。该证书也不需要CA签名认证(注:CA就是一个第三方的证书认证机构,如verisign等)。Android应用程序允许而且一般也都是使用self- signed证书(即自签名证书)。证书是用于在应用程序之间建立信任关系,而不是用于控制程序是否可以安装。签名影响安全性的最重要的方式是通过决定谁可以进入基于签名的permisssions,以及谁可以share 用户IDs。


9.2 用户IDs和文件存取


每一个Android应用程序(。apk文件)都会在安装时就分配一个独有的Linux用户ID,这就为它建立了一个沙盒,使其不能与其他应用程序进行接触(也不会让其它应用程序接触它)。这个用户ID会在安装时分配给它,并在该设备上一直保持同一个数值。
由于安全性限制措施是发生在进程级,所以两个package中的代码不会运行在同一个进程当中,他们要作为不同的Linux用户出现。我们可以通过 使用AndroidManifest.xml文件中的manifest标签中的sharedUserId属性,来使不同的package共用同一个用户 ID。通过这种方式,这两个package就会被认为是同一个应用程序,拥有同一个用户ID(实际不一定),并且拥有同样的文件存取权限。注意:为了保持安全,只有当两个应用程序被同一个签名签署的时候(并且请求了同一个sharedUserId)才会被分配同样的用户ID。
所有存储在应用程序中的数据都会赋予一个属性——该应用程序的用户ID,这使得其他package无法访问这些数据。当通过这些方法getSharedPreferences(String, int),openFileOutput(String, int)或者 openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)来创建一个新文件时,你可以通过使用MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE标志位来设置是否允许其他package来访问读写这个文件。当设置这些标志位时,该文件仍然属于该应用程序, 但是它的global read and/or write权限已经被设置,使得它对于其他任何应用程序都是可见的。
例如:APK A 和APK B 都是C公司的产品,那么如果用户从APK A中登陆成功。那么打开APK B的时候就不用再次登陆。 具体实现就是A和B设置成同一个User ID:
packagename APK A的AndroidManifest:

<manifest xmlns:android="http://schemas.ndroid.com/apk/res/android"

package="com.Android.demo.a1"

android:sharedUserId="com.c">


packagename APK A的AndroidManifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.Android.demo.b1"

android:sharedUserId="com.c">
这个"com.c" 就是user id。 APK B就可以像打开本地数据库那样打开APK A中的数据库了。APK A把登陆信息存放在A的数据目录下面。APK B每次启动的时候读取APK A下面的数据库判断是否已经登陆:
APK B中通过A的package name 就可以得到A的 packagecontext:

friendContext = this.createPackageContext( "com.android.demo.a1", Context,CONTEXT_IGNORE_SECURITY);
通过这个context就可以直接打开数据库。

9.3 权限(permission)


权限用来描述是否拥有做某件事的权力。Android系统中权限分为普通级别(Normal),危险级别(dangerous),签名级别(signature)和系统/签名级别(signature or system)。系统中所有预定义的权限根据作用的不同,分别属于不同的级别。
对于普通和危险级别的权限,我们称之为低级权限,应用申请即授予。其他两级权限,我们称之为高级权限或系统权限,应用拥有platform级别的认证才能申请。当应用试图在没有权限的情况下做受限操作,应用将被系统杀掉以警示。
系统应用可以使用任何权限。权限的声明者可无条件使用该权限。


目前Android系统定义了许多权限,通过SDK文档用户可以查询到哪些操作需要哪些权限,然后按需申请。
为了执行你自己的权限,你必须首先在你的AndroidManifest.xml中使用一个或多个

<permission> 标签声明。例如,一个应用程序想用控制谁能启动一个activities,它可以为声明一个做这个操作的许可,如下:

<manifest xmlns:android="http://schemas。android。com/apk/res/android"package="com.me.app.myapp" >

<permission android:name="com.me.app.myapp.permission.DEADLY_ACTIVITY" android:label="@string/permlab_deadlyActivity"

android:description="@string/permdesc_deadlyActivity"

android:permissionGroup="android.permission-group.COST_MONEY"

android:protectionLevel="dangerous" />

</manifest>


9.4 使用权限(uses-permission)


应用需要的权限应当在users-permission属性中申请,所申请的权限应当被系统或某个应用所定义,否则视为无效申请。同时,使用权限的申请需要遵循权限授予条件,非platform认证的应用无法申请高级权限。

所以,程序间访问权限大致分为两种:

第一种低级点的(permission的protectlevel属性为normal或者dangerous),其调用者apk只需声明<uses-permission>即可拥有其permission。
第二种高级点的(permission的protectlevel属性为signature或者signatureorsystem),其调用者apk就需要和被调用的apk一样拥有相同的signature。


若想拥有使用权限,必须在AndroidManifest.xml文件中包含一个或更多的<uses-permission> 标签来声明此权限。
例如低级权限需要监听来自SMS消息的应用程序将要指定如下内容:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.app.myapp" >

<uses-permission android:name="android.permission.RECEIVE_SMS" />

</manifest>
应用程序安装的时候,应用程序请求的permissions是通过package installer来批准获取的。package installer是通过检查该应用程序的签名来确定是否给予该程序request的权限。在用户使用过程中不会去检查权限,也就是说要么在安装的时候就批准该权限,使其按照设计可以使用该权限;要么就不批准,这样用户也就根本无法使用该feature,也不会有任何提示告知用户尝试失败。


例如高级权限用有system级别权限设定的api时,需要使其apk拥有system权限。比如在 android 的API中有供给SystemClock.setCurrentTimeMillis()函数来修改系统时间。有两个方法:

第一个方法简单点,不过需要在Android系统源码的情况下用make来编译:
1. 在应用程序的AndroidManifest.xml中的manifest节点中插入android:sharedUserId="android.uid.system"这个属性。
2. 修改Android.mk文件,插入LOCAL_CERTIFICATE := platform这一行
3. 使用mm命令来编译,生成的apk就有修改系统时间的职权范围了。
第2个方法麻烦点,不外不消开虚拟机跑到源码情况下用make来编译:
1. 同上,插手android:sharedUserId="android.uid.system"这个属性。
2. 使用eclipse编译出apk文件,但是这个apk文件是不能用的。
3. 使用针系统的platform密码钥匙来从头给apk文件签名。

signapk platform.x509.pem platform.pk8 input.apk output.apk


9.5 自定义Permission


Android系统定义的权限可以在Manifest.permission中找到。任何一个程序都可以定义并强制执行自己独有的permissions,因此Manifest.permission中定义的permissions并不是一个完整的列表(即能有自定义的permissions)。
一个特定的permission可能会在程序操作的很多地方都被强制实施:
当系统有来电的时候,用以阻止程序执行其它功能。
当启动一个activity的时候,会阻止应用程序启动其它应用的Acitivity。
在发送和接收广播的时候,去控制谁可以接收你的广播或谁可以发送广播给你。
当进入并操作一个content provider的时候。
当绑定或开始一个service的时候。


9.6 组件权限
通过 AndroidManifest.xml 文件可以设置高级权限,以限制访问系统的所有组件或者使用应用程序。所有的这些请求都包含在你所需要的组件中的 android:permission属性,命名这个权限可以控制访问此组件。
Activity 权限 (使用 <activity> 标签) 限制能够启动与 Activity 权限相关联的组件或应用程序。在 Context.startActivity() 和 Activity.startActivityForResult() 期间检查;
Service 权限(应用 <service> 标签)限制启动、绑定或启动和绑定关联服务的组件或应用程序。此权限在 Context.startService(), Context.stopService() 和 Context.bindService() 期间要经过检查;

BroadcastReceiver 权限(应用 <receiver> 标签)限制能够为相关联的接收者发送广播的组件或应用程序。在 Context.sendBroadcast() 返回后此权限将被检查,同时系统设法将广播递送至相关接收者。因此,权限失败将会导致抛回给调用者一个异常;它将不能递送到目的地。在相同方式下,可以使 Context.registerReceiver() 支持一个权限,使其控制能够递送广播至已登记节目接收者的组件或应用程序。其它的,当调用 Context.sendBroadcast() 以限制能够被允许接收广播的广播接收者对象一个权限(见下文)。
ContentProvider 权限(使用 <provider> 标签)用于限制能够访问 ContentProvider 中的数据的组件或应用程序。
如果调用者没有请求权限,那么会为调用抛出一个安全异常( SecurityException )。在所有这些情况下,一个SecurityException异常从一个调用者那里抛出时不会存储请求权限结果。

9.7 发送广播时支持权限


当发送一个广播时你能总指定一个请求权限,此权限除了权限执行外,其它能发送Intent到一个已注册的BroadcastReceiver的权限均可以。通过调用Context.sendBroadcast()及一些权限字符串,为了接收你的广播,你请求一个接收器应用程序必须持有那个权限。注意,接收者和广播者都能够请求一个权限。当这样的事发生了,对于Intent来说,这两个权限检查都必须通过,为了交付到共同的目的地。


9.8 其它权限支持
在调用service的过程中可以设置任意的fine-grained permissions(更为细化的权限)。这是通过Context.checkCallingPermission()方法来完成的。使用一个想得到的permission string来进行呼叫,然后当该权限获批的时候可以返回给呼叫方一个Integer(没有获批也会返回一个Integer)。需要注意的是这种情况只能发生在来自另一个进程的呼叫,通常是一个service发布的IDL接口或者是其他方式提供给其他的进程。
Android提供了很多其他的方式用于检查permissions。如果你有另一个进程的pid,你就可以通过Context的方法Context.checkPermission(String, int, int)去针对那个pid去检查permission。如果你有另一个应用程序的package name,你可以直接用PackageManager的方法 PackageManager.checkPermission(String, String) 来确定该package是否已经拥有了相应的权限。


9.9 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的activity获取到进入该Intent指定的URI的权限,而不论它是否有权限进入该intent对应的content provider。
这种机制允许一个通常的capability-style模型,这种模型是以用户交互(如打开一个附件, 从列表中选择一个联系人)为驱动,特别获取fine-grained permissions(更细粒化的权限)。这是一种减少不必要权限的重要方式,这种方式主要针对的就是那些和程序的行为直接相关的权限。
这些URI permission的获取需要content provider(包含那些URI)的配合。强烈推荐在content
provider中提供这种能力,并通过android:grantUriPermissions或者<grant-uri-permissions>标签来声明支持。
更多的信息可以参考Context.grantUriPermission(),Context.revokeUriPermission()和 Context.checkUriPermission() methods。
QA
1.拥有signature的权限是否可以不用声明<uses-permission>就能access带normal或dangerous权限设定的数据或功能?
只要signature相同,就算不显式声明<ues-permission>也能access设定了normal或dangerous权限设定的数据或功能。


2.若需要system级别权限使用系统api(即使用system级别的签名),如何同时使用其他signature权限设定(即使用signature级别的签名)的其他apk的功能?
拥有system级别权限的使用者可以access其他普通signature权限声明设定过的功能。
所以,设定为拥有system级别权限即可。

更多相关文章

  1. Android中shape的使用
  2. Delphi XE8 iOS与Android移动应用开发(APP开发)[完整中文版]
  3. 【Android】windows系统上安装与使用Android(安卓)NDK r5
  4. Android手机定位应用小解
  5. Android声音管理AudioManager使用
  6. 关于android中使用new Message的内存泄露问题
  7. 箭头函数的基础使用
  8. NPM 和webpack 的基础使用
  9. Python list sort方法的具体使用

随机推荐

  1. 在阿里云镜像站下载KiCad
  2. Manjaro Linux 入门使用教程
  3. 前端开发工具安装及使用小结
  4. 如何加快 Node.js 应用的启动速度
  5. Docker 镜像制作教程:针对不同语言的精简
  6. 编写vscode自带插件Ement常用语法并使用
  7. html快捷键/属性/标签学习
  8. VSCode、Chrome安装及HTML页面结构了解
  9. Springboot docker jenkins 自动化部署并
  10. 居中布局列举