System Permissions(系统权限)

来源:互联网 发布:微信三公机器人软件 编辑:程序博客网 时间:2024/05/29 04:49

文章转载禁止用于商业用途,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处莫高雷草原以及作者@JiongBull。

访问Android API指南目录索引查看全部翻译

系统权限

Android是一种权限隔离的操作系统,每个应用都以唯一的系统标识(Linux用户ID和组ID)运行着。系统的某些部分也被分割成一些唯一的标识。因此,Linux可以把应用互相隔离开,也与系统隔离开了。

更精细的安全特性是由“权限”机制提供的,它可以对某个进程可执行的特定操作进行限制,并且提供URI前缀权限对访问特定数据节点的操作进行授权。

本文描述了应用开发者如何使用Android提供的安全特性的方法。在Android开源工程中Android Security Overview一文中提供了更多的信息。

安全架构


Android安全架构有一条核心设计思想,就是默认情况下所有的应用都无权执行会对其他应用、操作系统或用户带来不利影响的操作。这些操作包含读写用户隐私数据(比如通讯录或邮件)、读写其他应用的文件、执行网络访问、保持设备唤醒状态等等。

因为每个Android应用都是在自己的进程沙箱中执行的,所以必须显示的共享资源和数据。可以通过声明应用需要的权限来获取基本沙盒无法提供的额外功能。应用静态的声明所需得权限,然后Android系统会在应用安装时提示用户授权。Android不提供动态授权机制(运行时授权),因为这会让用户体验变得复杂,且不利于安全。

应用沙箱不依赖于用来编译应用所使用的技术。特别是因为Dalvik VM的边界并不安全, 任何应用都可以运行本地代码(参考the Android NDK)。所有类型的应用,比如Java、本地和混合代码的应用,都可以以同样的方式运行在沙箱中,而且对彼此都有同样的安全级别。

应用签名


所有的APK(.apk文件)都必须由某个证书进行签名,证书的私钥由开发者持有。证书标识了应用的作者。证书是不需要通过证书机构来签发,这完全是允许的,而且通常Android应用都是使用的自签名证书。在Android中使用证书的目的是为了区分应用的作者。这样系统就能授权或拒绝应用申请访问签名级别的权限(signature-level permissions)和请求给予与其他应用相同的LinuxID的要求(request to be given the same Linux identity)。

用户ID和文件访问


在安装时,Android会为每个安装包分配一个唯一的Linux用户ID。 在安装设备上的该包的生命周期内,这个标识是固定不变的。在不同的设备上,相同的包可能会拥有不同的UID,但是在给定的设备上每个包都是固定的UID。

由于安全限制是在进程级别生效的,而且每个包都是作为独立的Linux用户来执行的,这样两个包的代码不可能运行在同一个进程中。你可以在每个包的AndroidManifest.xml中的 manifest标签内使用sharedUserId属性来给他们赋予同样的用户ID。这样,对于这两个包来说,在安全层面上它们被视为同一个应用,拥有同样的用户ID和文件权限。注意,为了保证安全性,只有两个应用的签名相同(并且申请同样的sharedUserId)才会被赋予相同的用户ID。

应用存储的任何数据都会被赋予应用的用户ID,通常其他包是无法访问的。当通过getSharedPreferences(String, int)、 openFileOutput(String, int)或者openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)创建文件时,你都可以使用MODE_WORLD_READABLE和/或MODE_WORLD_WRITEABLE标识来允许其他包来读/写该文件。在设置了这些标记之后,该文件依然属于你的应用,但是它的全局读/写权限被适当的重置了,这样其他的包就能访问它了。

使用权限


最基本的Android应用默认是没有关联任何权限的,这就意味着它不可能做出任何会影响用户体验或设备上数据的事情。想要使用设备上受保护的功能,你就必须在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>

在应用安装时,包安装器会基于对应用签名的检查情况和用户对声明权限的反馈把应用所需得权限授权给它。在应用运行时,不会再要求用户对权限进行校验。应用要么是在安装时被授权了某个权限,然后使用期望的特性,要么没有获得授权,那么任何使用该特性的请求都会失败,且不会提醒用户。

通常授权失败会导致向应用抛出SecurityException。然而,并不保证每次都会发生。例如,sendBroadcast(Intent)方法会在向接收器分发数据时检查权限,但是在方法调用返回时,如果授权失败了则不会接收到异常。不过,在几乎所有的情况下,授权失败都会打印到系统日志中。g.

然而,在正常的用户使用情形下(例如从Google Play商店中安装应用),只要用户不同意应用请求的任何一条授权请求,应用就不会被安装。所以你通常不需要担心由于授权缺失导致的运行时失败,因为只要应用被安装了就意味着它们已经获得了期望的授权。

Android系统提供的所有权限都可以在Manifest.permission中找到。 任何应用都还可以定义并启用自定义权限,所以这份列表无法包含全部可能的权限清单。

权限控制可以在程序运行过程中的很多场合生效:

  • 执行系统调用时,阻止应用执行某些操作。
  • 启动activity时,阻止应用启动其他应用的activity。
  • 发送和接收广播时,控制谁可以接收你的广播,或谁可以向你发送广播。
  • 访问或操作content provider时。
  • 绑定或启动service时。

注意:久而久之,平台可能会对某些API的使用添加新的限制,你的应用必须必须申请一些以前不需要申请的权限。因为已有的应用认为这些API是可以自由访问的,Android可能会在这些应用的manifest文件中加入新权限的申请请求,这样就能避免这些应用在新平台上不可用的情形。Android根据targetSdkVersion的属性值来决定应用是否需要这些权限。如果该属性值低于引入这些权限的版本值,那么Android就会加入这些权限。

例如,WRITE_EXTERNAL_STORAGE权限是从API级别4开始引入的,用来限制访问共享存储区域。如果你的应用的targetSdkVersion是3或更低,那么在更高版本的Android平台上会主动把该权限添加到你的应用中。

注意,如果你的应用确实如此,那么在Google Play上会显示你的应用需要这些权限,即使你的应用实际并不需要这些。

要避免发生这样的事并且移除你不需要得默认权限,请维持你的targetSdkVersion尽可能的更新到最高版本。你可以在Build.VERSION_CODES文档中了解每个版本引入的权限信息。

声明并实施权限


要实施自定义权限,你必须首先在你的AndroidManifest.xml文件中使用一个或多个<permission>标签来声明它们。

例如,某个应用想控制谁能启动它的activity就必须像下面这样声明操作的权限:

<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>

<protectionLevel>属性是必填的,该属性用来告知系统用何种方式通知用户应用请求的权限,或谁可以获取这个权限,在链接中的文档有详细说明。

<permissionGroup>属性是可选的,仅用于帮助系统向用户显示权限信息。通常你会把该属性设置为标准系统组(在android.Manifest.permission_group中有列举),设置为自定义的情况比较罕见。最好使用已存在的组,这样显示给用户的权限UI会更加简洁。

注意,应该同时提供权限的标签和描述。他们是字符串资源,在用户查看权限列表(android:label)或某个权限详情(android:description)时可以看到。标签内容不应太长,用来描述权限保护的功能的关键部分。描述内容应该是几句话,用来描述权限拥有者可以做什么。按照惯例,描述内容通常时两句话,第一句来描述权限,第二句警告用户应用获取授权后可能导致的后果。

下面是关于CALL_PHONE权限的标签和描述:

    <string name="permlab_callPhone">directly call phone numbers</string>    <string name="permdesc_callPhone">Allows the application to call        phone numbers without your intervention. Malicious applications may        cause unexpected calls on your phone bill. Note that this does not        allow the application to call emergency numbers.</string>

通过“设置”应用和shell命令adb shell pm list permissions查看当前系统中已定义的权限。在设置> 应用中可以打开“设置”应用。选中某个应用,然后滚动到下面就能看到该应用使用的权限列表了。对于开发者来说,使用adb '-s’选项可以像用户看到的格式那样显示权限列表。

$ adb shell pm list permissions -sAll Permissions:Network communication: view Wi-Fi state, create Bluetooth connections, fullInternet access, view network stateYour location: access extra location provider commands, fine (GPS) location,mock location sources for testing, coarse (network-based) locationServices that cost you money: send SMS messages, directly call phone numbers...

AndroidManifest.xml中实施权限

通过在你的AndroidManifest.xml中声明高级别的权限可以限制应用对某些系统或应用组件的访问。所有权限的申请请求都包含在相应组件的android:permission属性中,系统通过权限的名称来控制应用的访问。

Activity权限(应用在<activity>标签上)限制了谁可以启动该关联activity。在执行Context.startActivity()Activity.startActivityForResult()时检查该权限。如果调用者没有获取请求的权限,那么会在调用时抛出SecurityException异常。

Service权限(应用在<service>标签上)限制了谁可以启动或绑定该关联service。在执行Context.startService()Context.stopService()Context.bindService()时检查该权限。如果调用者没有获取请求的权限,那么会在调用时抛出SecurityException异常。

BroadcastReceiver权限(应用在<receiver>标签上)限制了谁可以发送广播给该关联的receiver。在执行了Context.sendBroadcast()返回结果后,因为系统会尝试把已提交的广播分发给指定的receiver,所以这时会检查该权限。结果是,授权失败的话不会导致向调用者抛回异常,只是不会分发该intent罢了。同样,可以在Context.registerReceiver()上应用权限,这样可以控制谁可以给动态注册的receiver进行广播。再换一种方式,也可以在Context.sendBroadcast()上应用权限,这样可以限制哪些BroadcastReceiver的对象可以接收广播(详见下文)。

ContentProvider权限(应用在<provider>标签上)限制了谁可以访问ContentProvider中的数据。(Content provider还拥有一种很重要的被称为URI permissions的安全特性,稍后会讲到) 不像其他组件那样,这里可以设置两个独立的属性:android:readPermission限制谁可以从provider中读取数据,android:writePermission限制谁可以往provider中写数据。注意,如果provider同时受到读和写权限的保护,仅有写权限并不意味着你可以从provider中读取数据。当你获取这个provider时就开始检查相关权限了(如果两个权限都没有,就会抛出SecurityException),并且在对provider进行操作时也会检查。使用ContentResolver.query()时需要读权限,使用ContentResolver.insert()ContentResolver.update()ContentResolver.delete()需要写权限。在这些情形下,没有获取相关权限都会在调用时抛出SecurityException。

发送广播时实施权限

除了实施决定谁可以向已注册的BroadcastReceiver发送广播的权限之外(就像上面描述的那样),你也可以在发送广播时指定一个需要的权限。通过在调用Context.sendBroadcast()时传入权限的字符串,就可以要求注册了receiver的应用必须拥有该权限才能接收到你的广播。

注意,广播的发送者和接收者都可以设置需要权限。如果两边都设置了权限,那么intent在两边的授权检查都必须通过后才能传递给关联的目标。

其他权限的实施

在调用服务时,可以随时对权限进行精确的实施。这是通过Context.checkCallingPermission()方法来完成的。对这个方法传入目标权限名称,该方法会返回一个整型数值,它可以反映当前调用的进程是否获得授权。注意,这只适用于执行来自其他进程调用的情形,通常是通过service公布的IDL接口或其他进程提供的途径。

还有其他几种检查权限的途径。如果你知道其他进程的pid,那么你可以使用Context的方法Context.checkPermission(String, int, int)来检查那个pid的权限。如果你知道其他应用的包名,那么你可以直接使用PackageManager的方法PackageManager.checkPermission(String, String)来查明某个包是否被授予了特定的权限。

URI权限


在使用content provider时,前面介绍的标准权限系统是不能满足需要的。content provider可能需要使用读写权限来保护自己,而它的直接客户端也需要获取某些特定的URI给其他应用处理。典型得例子就是邮件应用中的附件。由于邮件是很敏感的用户数据,所以访问邮件应该受到权限的保护。然而,如果把某个指向图片附件的URI传递给图片浏览器,图片浏览器会因为没有访问e-mail的权限而无法打开附件。

解决这个问题的方式就是使用URI前缀权限:在启动activity或向activity返回结果时,调用者可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION和/或Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这就给接收方activity访问intent里URI数据的权限,而不用管它是否拥有任何访问intent对应content provider里数据的权限。

这种机制实现了一种通用的功能性授权模式,也就是由用户交互(打开附件、选择联系人列表等)驱动的精确授权。这可是减少应用所需权限的关键,他们只需要那些与行为直接相关的权限就行了。

然而,精细化的URI授权需要持有这些URI的content provider的合作才能生效。强烈建议content provider实现这种授权功能,并且通过android:grantUriPermissions属性或<grant-uri-permissions>标签声明它们支持这样的功能。

更多信息可以在Context.grantUriPermission()Context.revokeUriPermission()Context.checkUriPermission()方法中找到。

继续阅读:

Permissions that Imply Feature Requirements
关于某些权限隐含了应用对设备特性需求的信息,包含相应的硬件和软件特性。
<uses-permission>
manifest标签的API文档,用于声明应用需求的系统权限。
Manifest.permission
所有系统权限的API文档。

还可能感兴趣:

Device Compatibility
关于Android在各种不同类型的设备上工作的信息,并介绍了如何针对每种设备优化应用或限制对不同设备的可用性。
Android Security Overview
详细讨论了Android平台的安全模型。
1 0
原创粉丝点击