权限系统

March 22, 2016

权限系统

对于 Android 权限,你需要知道了解的知识点有:

  • 沙盒模型
  • 申请权限
  • 普通权限和危险权限,以及权限组
  • 6.0 以上和早前版本的区别
  • 自定义权限以及通过自定义权限保护你的数据

Android 是一个权限分离的操作系统(privilege-separated operating system)。每个运行在 Android 上的应用都拥有一个独特的系统标识(Linux user ID 和 group ID)。系统本身的组成部分也都有单独的标识,所以,每个应用和系统都是独立的。

Android 系统提供了一个细粒度(finer-grained)的权限机制(permissiom mechanism),以保证各个进场直接数据的访问的安全。

安全架构 Security Architecture

默认的,一个应用没有任何权限,也不可能对用户,系统和其他应用带来任何不利影响(adversely impact)。包括读写用户的私有数据(private data),比如通讯录,邮件;也没有权限读写其他应用的文件,访问网络,让设备保持唤醒状态等等。

沙盒:每个 Android 应用都运行在自己的一个沙盒(sandbox)里,应用需要显示(explicitly)的共享资源和数据。对外分享时,需要定义沙盒没有提供的权限,对外获取时,也需要chiyou向沙盒申请权限,沙盒会询问用户。

不管是 Java ,native 还是 hybrid 编写的程序,都运行在沙盒里,所以,他们都有相同的安全级别。

应用签名 Application Signing

所有的 APK 文件都需要证书(certificate)签名,证书的 private key 一般由开发者持有,然后发布到各个市场。证书的作用就是区分应用的作者(application authors)。This allows the system to grant or deny applications access to signature-level permissions and to grant or deny an application's request to be given the same Linux identity as another application.

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

在安装的时候,Android 给应用的每个包(package)生成一个 user ID。这个 ID 伴随这应用的整个生命周期,从安装到卸载。在不同的设备,相同的 package 可能会被分配不同的 ID,但是,在一个设备上,不同的 package 的 UID 肯定是不同的。

因为安全机制的级别是进程级别的,任何两个 package 的代码不可能运行在一个相同的进程里,因为他们以不同的 linux user 的身份运行着。不过你可以在 AndroidManifest.xml 文件中的 manifest 标签中,定义相同的 shareUserId ,这样,他们就有相同的 user ID。这样,这两个 package 被任务是一个应用,他们有相同的 user ID 和文件权限(file permisstions)。但是前提是,这个应用需要用相同的证书签名。不然你自己开发一个 app,然后就可以跟 QQ 共享 userID 了。

任何由应用产生并存储的数据,都有指定的应用的 user ID,一般情况下,这些数据不被其他应用获取。当然,你可以使用 MODE_WORLD_READABLE 和/或者 MODE_WORLD_WRITEABLE 标识来指明你的应用的数据的访问等级。如果有了这些标识,你还是这些文件的拥有者,但是其他应用就有读取的可能。

权限使用 Using Permissions

默认的,Android 应用没有任何权限,如果需要设备上受保护的功能(protected features),必须在 app 的 manifest 中使用 <uses-permission> 标签声明。

如果只是普通的权限(normal permissions),不会对用户的隐私数据产生威胁(risk),系统会自动授权(automatically grants)。如果申请了危险的权限(dangerous permissions),有潜在的风险(potentially affect),系统会询问用户以获取用户允许。Android 系统对于危险的权限,在不同的版本上处理不一样。

  • 在 Android 6.0 (API level 23) 以上,系统在运行时(run-time)询问用户,用户可以随时撤回(revoke)权限,所以应用在需要使用权限的地方,每次确认是否获得权限。
  • 在 6.0 以前的,系统会在安装的时候询问用户,用户如果不允许每个权限的申请,安装将终止。在应用版本升级的时候,系统也会在安装新版本的时候,对新增的权限进行申请。一旦应用被安装,用户只有一个撤回权限的选择,就是卸载应用。

有些时候,权限失败的话,应用会收到一个 SecurityException 异常,但有些时候,你可能收不到这种异常,更多时候,你可以在系统日志里看到权限失败的 log。

在 Manifest.permission 这个类中定义了所有权限。任何应用都可以根据自己需要申请权限。

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

  • At the time of a call into the system, to prevent an application from executing certain functions.
  • When starting an activity, to prevent applications from launching activities of other applications.
  • Both sending and receiving broadcasts, to control who can receive your broadcast or who can send a broadcast to you.
  • When accessing and operating on a content provider.
  • Binding to or starting a service.

权限自动调整 Automatic permission adjustments

Android 的版本变更还是比较快的,在不同的版本上,对一些权限做出的一定的修改,为了避免因为系统升级给 app 带来的问题,Android 提供了一个自动调整的机制。比如 WRITE_EXTERNAL_STORAGE 权限在 API leve 4 的时候加入,如果你的 targetSdkVersion 是 3 或者更低的话,这个权限你自动就获取了,不过在安装的时候,这个权限会提示给用户的,即使你没有申请。

Google 建议 targetSdkVersion 尽量使用最高的版本。通过 Build.VERSION_CODES 可以查看权限的修改。

普通和危险的权限 Normal and Dangerous Permissions

系统权限被分成几个不同的保护等级,其中有两个重要的等级是普通和危险权限。

  • 普通权限(Normal permissions) 。普通权限是指那些对用户的私人数据或者其他应用不大可能会产生威胁的权限。比如设置时区(time zone)是一个普通等级的权限。系统会自动授权给应用这种普通等级的权限。普通等级权限参考 http://developer.android.com/guide/topics/security/normal-permissions.html
  • 危险权限(Dangerous permissions)。危险等级权限指那些可能会去用户私人数据或者其他应用产生威胁的权限。比如读取用户通讯录就是一个危险的权限。如果你申请了这种危险等级的权限,系统需要用户明确的授权给应用。

权限组 Permission groups

所有危险的权限都属于某个权限组。在 6.0 以上,对应危险权限会在运行的时候提示用户授权。对于两个属于同一个权限组里的两个权限,系统只会提示一次。比如 READ_CONTACTS 和 WRITE_CONTACTS 属于同一个权限组,如果用户授权了其中的一个,另外一个就不需要用户再次授权了。

一个权限组可能包括普通权限和危险权限,系统只关系危险权限,对于普通权限,你可以忽略他们。

两个特殊的权限 Special Permissions

SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS 两个权限是比较特殊的。不过呢,一般的 APP 是不需要申请这两个权限的。如果需要的话,需要在 manifest 文件中声明,然后还需要向用户发送一个 Intent ,获得用户的授权。系统会显示一个管理界面(showing a detailed management screen )给用户。

危险权限列表

待完成。

定义和执行权限 Defining and Enforcing Permissions

Android 还允许你定义自己的权限。在 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>

name:就是权限的名字,一个字符串。
label 和 description 是这个权限的一个标签和说明,一般 label 会比较简短,description 稍微详细点,大概有两三句话搞定。
permissionGroup : 你可以指定这个权限所属的权限组。当然你也可以自己定义一个权限组。
protectionLeval : 你可以定义这个权限的安全等级。

Enforcing Permissions in AndroidManifest.xml

上面说的是定义权限,然后怎么使用呢。Android 的四大组件(component)都可以在 manifest 注册的时候添加 android:permission 属性,这样,在调用者(caller)调用相应 API 启动这些组件的时候,如果他们没有申请权限的话,就会收到 SecurityException 。

Activity ,如果你在某个 <activity> 中添加了 android:permisstion 属性的话,当调用者(一般是其他 app)使用 startActivity() 或者 startActivityForResult() 方法启动这个 Activity 而没有在它所在的 app 的 manifest 文件中申请对应的权限的话,调用者会收到 SecurityException。

Service。和 Activity 相似,startService(),stopService(),bingService() 可能会收到 SecurityException 。
BroadcastReceiver 。你可以给你的 BroadcastReceiver 添加权限属性,如果其他 app 通过 Context.sendBroadcast() 发广播的时候,系统会尝试把广播交付给收听者,但是如果发广播的 app 没有对应权限的话,它不会收到 SecurityException,不过你的 BroadcastReceiver 不会收到这个广播。这样你可以限制给你发送广播的人,只有获得对应权限的人给你发的广播你才会接收到。另外,你也可以发送一个广播,只有获得权限的人才会接受到你发的广播。系统提供了这么一个 API : Context.sendBroadcast(Intent intent,String receiverPermission)。
ContentProvider ,和其他的不大一样,ContentProvider 可以分别定义读权限和写权限,android:readPermission 和 android:writePermission 。特别的,如果 Provider 分别定义了读和写的权限,如果你有写权限,不等于你有读的权限。在你第一次访问这个 Provider 的时候,系统会做安全检测,如果没有对应的权限,调用者会收到 securityException。ContentResolver.query() 需要读权限,update(),insert(),delete() 需要有写权限。ContentProvider 还提供了另外一种权限机制,URI Permission。

其他权限使用方式 Other Permission Enforcement

下面这段实在不懂得怎么翻译,先直接 copy 过来,大概意思就是在 service 中,可以通过 Context.checkCallingPermission() 方法来检查调用者是否获得到了对应的权限。这是 Service 被用来做多进程间通信的时候,为了保证安全,对其他进程调用做的一个安全检查。可以通过进程的 ID 和包名的方式去检查。Context.checkPermission(string,int,int) 和 PackageManager.checkPermission(String,String).
Arbitrarily fine-grained permissions can be enforced at any call into a service. This is accomplished with the Context.checkCallingPermission() method. Call with a desired permission string and it will return an integer indicating whether that permission has been granted to the current calling process. Note that this can only be used when you are executing a call coming in from another process, usually through an IDL interface published from a service or in some other way given to another process.

URI Permissions

这是一种被称为 per-URI permission ,可以理解为为单个资源进行的一次临时的授权。官网举的例子是,比如你是一个邮件应用,你需要通过一个图片查看器来打开某个邮件中的一个附件。很明显,因为这是邮件的附件,所以你把它设置为隐私的,所以图片查看器是不应该有权限访问的,但是你又期望图片查看器能打开这个图片怎么办?你可以在启动图片查看器的 Intent 上加上 Intent.FLAG_GRANT_READ_URI_PERMISSION and/or Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这样,接受这个 intent 的 Activity 就获得了访问这个 URI 的许可,即使这个 Activity 所在的 App 没有获得相应的权限,这里的权限指的是 Provider 定义的权限,如果你的数据足够的隐私的话,你是不会对别人开放权限也不会告诉别人怎么获得权限的。当然,为了让这样的机制能够运作起来,需要和你自定义的 Content Provider 有一定的配合的。It is strongly recommended that content providers implement this facility, and declare that they support it through the android:grantUriPermissions attribute or <grant-uri-permissions> tag.

其他文章

--- EOF ---

添加新评论