1. 安全架构

  2. 应用签名

  3. 用户ID和文件访问

  4. 使用权限

  5. 声明及执行权限

    1. 在清单文件中执行权限

    2. 发送广播时执行权限

    3. 其他权限执行

  6. URI权限


权限


本文描述了应用开发人员如何使用由Android提供的安全特性。Android开源项目提供一个比较综合的Android安全概述


Android是一个权限分隔的操作系统,它里面的每一个应用以不同的系统身份(Linux用户ID和组ID)运行。系统部分也被分割成不同的身份。因此,linux把应用从彼此及系统中隔离了。


附加的细粒度安全特性是由“权限”机制提供的,它在一个特定进程可以执行的特定操作上施加限制,并且每个URI权限授予特设访问指定的数据块。



1.安全架构


Android安全架构的中心设计思想是,默认情况下,没有应用有权对其他应用,操作系统,或者用户造成不利影响。这包括读或写用户的私有数据(比如通讯录或邮件),读或者写其他应用的文件,执行网络访问,保持设备唤起等待。


因为Android在应用彼此间用沙箱隔离了,所以应用必须显示地共享资源和数据。它们通过声明它们需要的权限来做到这一点,这些权限可以使它们具有基础沙箱没有提供的额外能力。应用程序静态地声明它们需要的权限,然后Android系统在程序被安装时提示用户许可。Android没有动态(在运行时)授予权限的机制,因它使用户体验的安全性复杂化了。


应用沙箱不依赖用于构建应用程序的技术。在个别虚拟机内部没有安全界限,因此任何应用可以运行本地代码(详见the Android NDK)。所有类型的应用Java,本地,以及它们的混合— 都以同样的方式被隔离在沙箱内,并且彼此间有相同的安全等级。



2. 应用签名


所有Android应用程序(.apk文件)必须通过证书签名,证书的密钥由应用的开发者掌握着。该证书标示了应用的作者。证书不需要由证书发布机构来设计:Android应用使用自签名证书是完全允许的和典型的。在Android中,证书的目的是为了区分应用的作者。这允许系统同意或拒绝应用访问签名级权限以及同意或拒绝应用作为另一应用获取相同的linux身份的请求。



3. 用户ID和文件访问


在安装时,Android给每个包一个不同的Linux用户ID。该ID在包的生命周期期间保持不变,相同的包可能有不同的UID(用户ID);重要的是,在给定设备上每个包拥有不同的UID。


由于安全执行发生进程层面,因此任何两个包的代码不能正常地运行在相同的进程内,因为它们需要作为不同的Linux用户来运行。可以在每个包的AndroidManifest.xml文件的manifest标签上使用sharedUserId属性来指定相同的用户ID。通过这样做,出于安全的目的,两个包被视为同一个应用,拥有相同的用户ID和文件权限。注意,为了维持安全,仅两个被签有相同签名(及请求相同的shareUserId)的应用将被指定相同的用户ID。


任何由应用存储的数据将被指定了该应用的用户ID,因此它对其他包来说一般是不可访问的。当使用getSharedPreferences(String, int),openFileOutput(String, int),或openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)创建一个新的文件时,可以使用MODE_WORLD_READABLE 和/或MODE_WORLD_WRITEABLE标志来允许任何其他包读/写该文件。在设置这些标志时,文件依然由你的应用拥有,但它的全局读和/或写权限已被恰当地设置,因此任何其他的应用都可以看到它。



4. 使用权限


默认情况下,一个基本的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)方法检查权限,该方法调用返回后,如果有权限失败将不会收到异常。几乎所有情况下,权限失败将被打印到系统log中。


Android系统提供的权限可以在Manifest.permission找到。任何应用也可以定义和执行它们自己的权限,因此这不是一个全部可能权限的综合性清单。

特定的权限可能在程序执行期间的很多地方被执行:

A particular permission may be enforced at a number of places during yourprogram's operation:

  • 在系统调用时,阻止应用执行某一方法。
  • 当启动活动时,阻止应用启动其他应用的活动。
  • 发送和接收广播时,控制谁可以接收你的广播或者谁可以给你发送广播。
  • 当访问和操作内容提供者时。
  • 绑定到或者启动服务时。


注意:随着时间的推移,新的限制可能被添加到平台中,为了使用某些APIs,你的应用必须请求它最初不需要的权限。因为现有的应用假定访问那些APIs是自由获得的,Android可能对应用的清单文件应用新的权限请求以避免在新的平台版本上阻碍应用执行。至于应用是否可能需要权限,Android基于提供给targetSdkVersion属性的值来做出决定。如果该值低于权限被添加的版本,那么Android则添加该权限。


例如,WRITE_EXTERNAL_STORAGE权限被添加在API标准4中以限制访问共享的存储空间。如果你的targetSdkVersion是3或者更低,那么该权限在新版的Android上被添加到你的应用中。


注意,如果这发生在你的应用中,你的应用列在Google Play上将显示这些被需要的权限,即便是应用可能实际上并不需要它们。


为避免这点以及移除默认不需要的权限,通常把targetSdkVersion更新为尽可能高的值。可以在Build.VERSION_CODES 文档中查看每个发布的版本中添加了哪些权限。



5. 声明及执行权限


为了执行权限,首先必须在AndroidManifest.xml中使用一个或多个<permission>标签声明它们。


例如,想要控制谁可以启动它的活动之一的应用程序可以为此操作声明权限,如下所示:


<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中有列出)或者在较为少见的情况设置为你自己定义的组。优先使用一个现有的组,因为这简化了显示给用户的权限界面。

注意,应该为权限提供标注和描述。它们是string资源,可以在用户查看一组权限(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>


可以通过Settings应用和shell命令adb shell pm list permissions来查看系统当前定义的权限。为使用Settings应用,前往Settings > Applications。选取一个应用然后向下滚动来查看该应用使用的权限。对于开发者来说,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...


5.1 在AndroidManifest.xml中执行权限


通过在AndroidMainfest.xml文件中申请限制访问系统或应用全部组件的高级权限。这需要的全部就是在期望的组件上包含一个android:permission属性,并对将要被用来控制访问它的权限命名。


Activity权限(应用于<activity>标签)限制谁可以启动该活动。在Context.startActivity()和Activity.startActivityForResult()调用期间检查该权限;如果调用者没有必需的权限,那么该调用会抛出SecurityException异常。


Service权限(应用于<service>标签)限制谁可以启动或绑定到该服务。在Context.startService(),Context.stopService() andContext.bindService()调用期间检查该权限;如果调用者没有必需的权限,那么该调用会抛出SecurityException异常。


BroadcastReceiver权限(应用于<receiver>标签)限制谁可以向该接收器发送广播。在Context.sendBroadcast()调用返回后检查该权限,然后系统尝试向指定的接收器递送被提交的广播。作为结果,权限失败不会导致把一个异常抛回给调用者;它只是不递送意图而已。以同样的方式,权限可以提供给Context.registerReceiver()来控制谁可以向以编程方式注册的接收者广播。相反,可以给Context.sendBroadcast()提供权限来控制哪个BroadcastReceiver对象被允许接收该广播(见下文)。


ContentProvider权限(应用于<provider>标签)限制谁可以访问ContentProvider内的数据。(内容提供者拥有一个重要的附加安全设施被称为URI权限,稍后介绍它。)不像其他组件,有两个单独的权限属性可供设置:android:readPermission限制谁可以从提供者读取数据,以及android:writePermission限制谁可以向其写入数据。注意,如果提供者用读和写权限做了保护,仅持有写权限不意味着可以从提供者读取数据。当第一次检索提供者时检查这两个权限(如果你没有两个权限中的任何一个,将会抛出SecurityException异常),然后在提供者上执行操作。


ContentResolver.query()要求持有的读权限;


ContentResolver.insert(),ContentResolver.update(),ContentResolver.delete()要求持有写权限。在所以这些情况下,不持有必要权限将会导致从调用中抛出异常。


5.2 发送广播时执行权限


权限除了限制谁可以给一个注册的BroadcastReceiver对象发送意图以外(如上所述),你还可以在发送广播时指定一个必要的权限。通过调用Context.sendBroadcast()携带一个权限字符串,来要求接受者应用必须持有那个权限以接收你的广播。


注意,接收者和广播者它们两个可以要求权限。当这种情况发生时,两个权限检查必须都通过的意图将被递送给关联目标。


5.3 其他权限执行


任意细化的权限可以在任何对服务的调用时被执行。这是通过Context.checkCallingPermission()方法完成的。用一个期望的权限字符串调用它,然后将返回一个表明那个权限是否被授予给当前调用进程的整数值。注意,这仅在你正在执行一个来自另一进程的调用时使用,一般是通过服务发布的一个IDL接口或者是以以些其他指定另一进程的方式。


有很多其他有的方法来检查权限。如果有另一进程的进程pid,可以使用Context方法Context.checkPermission(String, int, int)来对该pid检查权限。如果有另一应用的包名,可以直接使用PackageManager方法PackageManager.checkPermission(String, String)来查看特定包是否被授予了指定的权限。



6. URI权限


当用于内容提供器时,目前系统所描述的标准权限往往是不足够的。内容提供器可能打算用读和写权限来保护其自己,而它的直接客户端还需要把指定的URL传递给其他应用以便让它们对其操作。典型的例子就是邮件应用中的附件。访问邮件应用应该由权限保护,因为这是用户的敏感数据。然而,如果把一个图片附件的URI给了图片查看器,那个查看器将不会有权限打开该附件,因为它没有理由持有访问全部邮件的权限。


此问题的解决办法是每个URI权限:当启动或把结果返回给活动时,调用者可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION和/或Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这赋予了接收活动访问Intent内指定的数据URI的权限,不管它是否有权限访问与Intent对应的内容提供器内的数据。


此机制允许普通的用户交互功能风格来驱动点对点的细粒度权限的授予。这可以是一个关键的设施为把由应用所需要的权限减少至仅与应用行为有直接关系的那些。


然而,细粒度URI权限的授予需要一些持有那些URIs的内容提供器的配合,强烈推荐内容提供器实现这设施,并通过android:grantUriPermissions属性或<grant-uri-permissions>标签声明它们支持该设施。


更多信息可查看Context.grantUriPermission(),Context.revokeUriPermission(),和Context.checkUriPermission()方法。




更多相关文章

  1. Android判断用户的网络类型(2/3/4G、wifi)
  2. android使用主流库搭建应用框架
  3. Android(安卓)- 和其他APP交互 - 把用户带到其他app
  4. Android(安卓)动态权限最全解析
  5. android 搜索
  6. 软件工程(第六组)第四次作业
  7. Android研发安全2-Activity组件安全(下)
  8. Android中使用webservice验证用户登录的示例
  9. Android(安卓)socket 学习记录 之 执行new socket(ip, port)程序

随机推荐

  1. 关于android真机访问本地电脑服务器以及
  2. Android(安卓)UI开发第二十四篇――Actio
  3. Android中线程池的使用分析
  4. Android(安卓)基于百度语音的语音交互功
  5. 2017上半年技术文章集合【Android】—184
  6. 乔布斯对 Android(安卓)如此愤怒:不惜发起
  7. Android(安卓)对Path的旋转效果的拓展
  8. Android(安卓)Handler 消息通信机制
  9. Android项目总结之社会化分享
  10. android中adb不是内部或者外部指令以及丢