Android——系统权限

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 许可的labeldescription的例子:

<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()的时候使用一个权限字符串,你就可以要求接收器的宿主程序必须有相应的权限,才能接收你的广播

需要注意的是Receiverbroadcaster都可以要求权限。当这种情况发生时,这两种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参考。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值