Android是一个特权分隔的操作系统,每一个应用程序运行在不同的系统身份中(Linux的user ID和group ID)。系统部分和不同的身份被隔离开来。因此,Linux隔离了应用程序(与其它程序隔离,与系统隔离)。

通过权限(permission)机制提供了附加的安全功能。权限机制强迫限制特定操作,比如操作一个特定进程执行和每个URI权限允许点对点地访问特定数据块。

更多通用的Android Security Overview被提供在Android开源项目中。

安全体系结构(Security Architecture)


Android安全体系结构的核心就是——默认情况下,程序没有权限执行下面三种操作:对其他程序不利的操作、系统操作、用户操作。包括读写用户数据(联系人或者邮件之类),读写其它程序中的文件,执行网络操作等等。

由于Android程序运行在一个沙箱中,程序必须显示地分享资源和数据(声明权限,注权限是附加的功能并不是由沙箱提供的)。程序静态地声明它们需要的权限,并且在安装程序时,Android系统提示用户同意权限。Android没有动态(run—time)同意权限机制,因为这会使用户体验复杂,同时会破坏安全。

程序沙箱不依赖于程序编译技术。特别是Dalvik VM不是一个安全边界,并且所有的app都能运行native代码。java、native和hybird,以上所有的这些程序类型都是以同样的方式被沙箱化的,并且都有同样的安全等级。

程序签名(Application Signing)


所有的APK必须要被一个证书签名,证书的私钥被开发者持有,证书签名不需要证书权限。在Android中,证书的目的是用来区分开发者程序的,允许系统同意或拒绝程序访问签名级别权限和同意或拒绝一个程序访问其它有相同的Linux身份的程序。

用户ID和文件访问(User IDs and File Access)


在安装的时候,Android会给每个包一个不同的Linux用户ID。在同个设备的包的声明周期中,这个ID常量会始终保持。在不同的设备中,同一个包可能会有不同的UID。无论如何,在一个给定的设备中每个包都有不同的UID。

由于安全机制的执行发生在进程级别,任何两个包的代码一般是不能运行在同一个进程的,它们运行在两个不同的Linux用户中。但是,可以在他们包的manifest中使用shareUserId属性来给他们分配同一个用户ID。通过这样做了之后,两个包被就被当作了一个程序,它们拥有一样的用户ID和文件权限。注意,为了保持安全,这两个程序必须拥有相同签名并且请求同一个sharedUserId,否则是不会被给与相同的用户ID。

任何的数据被程序存储,程序被分配一个用户ID,这样程序就不能访问其它的包。通过getSharedPreferences(String, int), openFileOutput(String, int), or openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)创建一个新文件时,可以使用MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE标记允许其它程序读写文件。设置了这些标记,尽管文件一直是属于一个程序的,但是这类文件的全局读写权限还是能让其它程序看的到这些文件。

使用权限(Using Permissions)


一个基本的Android程序默认是没有权限的,意味着它不能执行任何事情或者设备上数据,这样会影响用户体验。为了使用设备被保护的功能,必须在manifest中通过一个或多个<uses-permission> 标记来声明程序需要的权限。比如接受短信:

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.app.myapp" >
<uses-permission android:name="android.permission.RECEIVE_SMS" />
...
</manifest>

在程序安装的时候,程序权限的授予是通过package installer进行的。package installer检查签名程序声明的权限,然后与用户交互是否授予程序权限。运行时,程序的权限检查工作是不会进行的;安装时,app既可以被授予某个权限(能使用期望的功能),也可以被不授予权限(使用这功能会失败同时不会提示用户)。
通常一个权限失败,会导致一个 SecurityException 抛回给应用程序。但是,Android并不保证这种情况会处处发生。比如,将数据分发给广播接收器, sendBroadcast(Intent)(异步方法,会检查权限)方法调用返回后,如果有权限失败,也不会收到一个异常。几乎绝大多数情况,一个权限失败会被打印到系统log当中。

如果app需要的所有权限都没被用户授予,那么app是不能被安装的。

Android系统定义的权限可以在Manifest.permission中找到。任何一个程序都可以定义并强制执行自己独有的权限(<permission>),因此Manifest.permission中定义的权限并不是一个完整的列表(即有可能有自定义的权限<permission>)。

在你的程序操作过程中,一个特定的权限可能会在很多地方都被执行,比如:

  • 当系统有来电的时候,用以阻止程序执行其它功能。
  • 当启动一个活动(activity)的时候,会阻止应用程序启动其它应用程序的Acitivity。
  • 在发送和接收广播的时候,去控制谁可以接收你的广播或谁可以发送广播给你。
  • 当进入并操作一个内容提供器(content provider)的时候
  • 当绑定或起动一个服务(service)的时候

声明和执行自定义权限(Declaring and Enforcing Permissions)


为了执行自定义权限,首先必须要在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>属性是可选的,只是用于帮助系统显示权限给用户(实际是告知系统该许可是属于哪个许可组permission group的)。通常会选择使用标准的系统权限组来设定该属性,或者更为少见的用自己定义的权限组。推荐使用一个已经存在的权限组,因为这样会简化给用户显示的权限UI。

需要注意的是标签(label)和描述(description)都是需要为自定义权限提供的。当用户去看权限列表(android:label)或者某个权限的详细信息(android:description)时,这些字符串资源就可以显示给用户。label应当尽量简短,只需要用几个关键词语告知用户该权限是在保护什么功能就行。而description可以用几句话来具体描述拥有该权限程序可以做哪些事情,我们通常用两句话来描述:第一句描述该权限;第二句警告用户如果批准该权限会可能有什么不好的事情发生。下面是一个描述CALL_PHONE 许可的label和description的例子:

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

在系统中,可以通过设置app(Settings app )和adb shell命令(adb shell pm list permissions)看到定义的权限。

使用设置app(Settings app ):Settings > Applications,选中一个app进入信息页面,就能看到app使用的权限情况。

adb shell命令(adb shell pm list permissions):"-s"选项会将权限信息以类似表格的形式呈现给用户:

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

在清单文件执行权限(Enforcing Permissions in AndroidManifest.xml)

限制进入系统或程序的组件的高级别权限可以在manifest中实现。在组件中包含一个android:permission属性,命名该权限用来控制访问组件。

Activity权限(应用于<activity>标签),限制谁可以启动相应的Activity。这个权限检查发生在Context.startActivity()和Activity.startActivityForResult(),如果调用者没有申请相应权限,会导致SecurityException被抛回给调用者。

Service权限(应用于<service>标签),限制谁可以启动或者绑定相应的Service。这个权限检查发生在 Context.startService()、Context.stopService()和 Context.bindService(),如果调用者没有申请相应权限,会导致SecurityException被抛回给调用者。

BroadcastReceiver权限(应用于<receiver>标签),限制谁才可以给广播接收器发送广播。这个权限检查发生在Context.sendBroadcast()调用返回之后,系统会尝试发送广播给接收器。(注:Context.sendBroadcast()是一个异步调用,调用返回是立即发生的。)最终,权限如果失败了,也不会返回一个异常给调用者,只是没有发送Intent罢了。同样的,在动态注册广播(代码中使用Context.registerReceiver())中也可以实现一个权限控制。另一种方式(具体内容见下面)是,当调用Context.sendBroadcast()时,权限用于限制哪一个BroadcastReceiver才可以接收该广播。

ContentProvider权限(应用于<provider>标签),限制谁能访问ContentProvider的数据。(ContentProvider还有另外一套重要的安全机制——URI permissions,以后会谈到。)不像其它组件,ContentProvider有两个单独的权限属性:android:readPermission(限制谁可以从provider中读取数据)和android:writePermission(限制谁可以将数据写入provider)。需要注意的是provider同时被读写权限保护的情况下,如果你这时只有写权限并不意味着能从provider读。这个权限检查发生在第一次检索provider时,并且这时对provider做了某些操作。使用ContentResolver.query()需要读权限,使用 ContentResolver.insert(),ContentResolver.update(), ContentResolver.delete()需要写权限。在所有这些情况下,没有所需的权限将会导致SecurityException被抛出。

发送广播时执行权限(Enforcing Permissions when Sending Broadcasts)

在发送广播的时候,指定一个需要的权限。在调用Context.sendBroadcast()的时候使用一个权限字符串,你就可以要求接收器的宿主程序必须有相应的权限,才能接收你的广播。

需要注意的是Receiver和broadcaster都可以要求权限。当这种情况发生时,这两种permission检查都需要通过后才会将相应的intent发送给相关的目标。

其它权限执行(Other Permission Enforcement)

在Service里面,可以通过Context.checkCallingPermission()方法来执行任意细化的权限。调用的时候使用一个期望的权限字符串,它会返回一个整数来表明这个权限是否被授予当前调用进程。需要注意的是这种情况只能发生在来自另一个进程的呼叫,通常是一个service发布的IDL接口或者是以其他方式提供给其他的进程。

Android提供了其他几个有用的方式用于检查permissions。如果你有另一个进程的pid,你就可以通过Context.checkPermission(String, int, int)去针对那个pid去检查permission。如果你有另一个应用程序的包名,你可以直接用PackageManager.checkPermission(String, String)来确定该包是否已经拥有了相应的权限。

URI权限(URI Permissions)


目前为止,我们所讨论的标准权限系统对ContentProvider来说是不够的。一个ContentProvider可能想要通过读写权限保护自己,然而它的直属客户端也需要将特定的URI传递给其它程序,以便对该URI进行操作。一个典型的例子是邮件程序的附件。访问的邮件需要被权限保护,因为这些是敏感的用户数据。然而,如果一个指向图片附件的URI传递给一个没有访问邮件权限的图片浏览器,那么图片浏览器是没有权限去打开附件的。

针对这个问题的解决方案是每个URI权限(per-URI permissions):当启动一个Activity或者从一个Activity返回结果时,调用者可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION and/or Intent.FLAG_GRANT_WRITE_URI_PERMISSION。不管是否有权限通过Intent访问在ContentProvider中的数据,这都会授予接收的Activity权限去访问Intent中URI指定的数据。

这种机制使得常见的能力-风格模型(capability-style)得以实现,用户交互操作(打开附件,从列表中选择联系人等)驱动点对点的授予细化权限。这是一个减少程序权限的关键能力,只留下和程序行为相关的权限。

然而,对于细化URI权限的授予,需要那些持有该URI的ContentProvider配合。强烈建议在ContentProvider中实现这种能力,并且通过android:grantUriPermissions属性或者<grant-uri-permissions>标签来声名ContentProvider支持这一个特性。

更多的信息可以参考下面几个方法 Context.grantUriPermission(), Context.revokeUriPermission(), 和Context.checkUriPermission()。


扩展

Permissions that Imply Feature Requirements

关于有些权限请求隐含者硬件或者软件限制。

<uses-permission>

manifest的一个应用API标签,用于声明app请求的系统权限。

Manifest.permission

所有系统权限的API参考。

Android——系统权限的更多相关文章

  1. Android系统权限及签名

    Android系统权限及签名   2015-03-23 19:13:33CSDN-chen52671-点击数:50     Android权限及签名 引子 现象:系统中的一个定制Service,服务是 ...

  2. 对Android系统权限的认识

    Android系统是运行在Linux内核上的,Android与Linux分别有自己的一套严格的安全及权限机制 Android系统权限相关的内容 (一)linux文件系统上的权限 -rwxr-x--x ...

  3. Android系统权限和root权限

    Android系统权限和root权限http://www.verydemo.com/demo_c189_i277.html

  4. Android系统权限和root权限大全

    tyle="margin:20px 0px 0px; font-size:14.285714149475098px; line-height:26px; font-family:Arial; ...

  5. android系统权限SET_PREFERRED_APPLICATIONS怎么获取

    最近自己写个demo,需要用到SET_PREFERRED_APPLICATIONS权限 在网上找了半天,大概有两种方式: 1. 将手机root后,把apk强行push到system/app目录下,重启 ...

  6. android系统权限的管理

    被权限搞了好久,决定好好的研究一下: 参考资料 http://blog.csdn.net/xieyan0811/article/details/6083019?reload http://blog.c ...

  7. [转]通过apk签名使应用程序有系统权限

    [转]通过apk签名使应用程序有系统权限 (2013-01-08 13:40:50) 转载▼ 标签: it 分类: Android 出处:http://blog.csdn.net/doom66151/ ...

  8. Android如何通过shareduserid获取系统权限

    [原文] android会为每个apk进程分配一个单独的空间(比如只能访问/data/data/自己包名下面的文件),一般情况下apk之间是禁止相互访问数据的.通过Shared User id,拥有同 ...

  9. android:sharedUserId 获取系统权限

    最近在做的项目,有好大一部分都用到这个权限,修改系统时间啊,调用隐藏方法啊,系统关机重启啊,静默安装升级卸载应用等等,刚开始的时候,直接添加权限,运行就报错,无论模拟器还是真机,在logcat中总会得 ...

随机推荐

  1. Django的日常-路由层

    目录 Django的日常-2 路由层 有名分组和无名分组 反向解析 路由的分发 Django的日常-2 路由层 我们之前已经接触过路由层,只是我们可能不知道他叫这个名字,实际上在Django里面路由层 ...

  2. tensorflow+inceptionv3图像分类网络结构的解析与代码实现

    tensorflow+inceptionv3图像分类网络结构的解析与代码实现 论文链接:论文地址 ResNet传送门:Resnet-cifar10 DenseNet传送门:DenseNet SegNe ...

  3. C语言处理字符串及内存操作

    字符串处理函数 1.字符串长度 strlen表示包含的字符的个数,size_t strlen(char cosnt *string), 返回的是size_t类型,它是无符号整数类型,在表达式中进行运算 ...

  4. IDEA设置maven项目的默认配置

    IDEA设置maven项目的默认配置 问题描述 很多刚使用idea的人,用其创建maven工程时会遇到一个问题,明明给项目设置了新的maven配置(使用阿里镜像源或者自定义maven版本),但是重新打 ...

  5. PHP中的符号 ->、=> 和 :: 的含义(用法)

    php新手经常碰到的问题,->.=> 和 :: 这三个家伙是什么分别都是做什么的啊!看着就很晕. 没关系,下面我们做一下详细的解释,如果你有C++,Perl基础,你会发现这些家伙和他们里面 ...

  6. hibernate一对一关联手动改表后No row with the given identifier exists:

    articleId手动改了一个并不存在的值 把被控端的id改成存在的就好了

  7. diango中orm的惰性机制

    那么首先要知道什么是ORM 专业化的角度来说:叫对象关系映射(Object-Relation Mapping)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术. 那具体ORM是什么呢?:( ...

  8. Vue中利用$emit实现子组件向父组件通信

    Vue中利用$emit实现子组件向父组件通信 父组件 <template> <div> <p>我是父组件</p> <child :isShow=& ...

  9. 用javascript的classList代替jquery的class操作

    javascript的className操作方法比较难受,他获取的是一个连续的字符串 必须要用split拆分开,好多人因此想去用jquery的addClass,removeClass,hasClass ...

  10. laravel 下载报错:Unable to guess the mime type as no guessers are available

    在使用laravel的download()函数实现下载功能时,报错如下:Unable to guess the mime type as no guessers are available (Did ...