Android签名与权限的安全问题(3)

时间:2021-07-16 01:47:34

签名和权限的作用

Android签名中使用到的一些加密技术有:

公/私钥, SHA1(CERT.SF,MANIFEST.MF), RSA(CERT.RSA), 消息摘要,

移动平台中的主流签名作用:

  • Android平台中是使用自签名

    自签名,证书的签名者和证书拥有者是同一人.

自签名的完整性认证

自签名是没有信任模式的,因为自签名信息是自己的,对无法知道该信息是不是安全,我们只能对其的完整性进行认证.

限制安装和运行

下面是限制应用安装和运行的流程

  • 应用安装时

    校验是否含签名 –> 没有,禁止安装

    –> 有,提取证书进行校验–> 证书是否有效可信任–> 不是禁止安装.

    基于证书的公钥对签名进行校验–> 签名是否正确 –> 不正确禁止安装.

  • 应用运行时

    校验是否包含签名 –> 没有,禁止运行

    –> 有,提取证书进行校验–> 证书是否有效可信任–> 不是禁止运行.(这一步跟安装流程相似)

    基于证书的公钥对签名进行校验–> 签名是否正确 –> 不正确禁止运行.(这一步跟安装流程相似)

权限的作用(细粒度的特权管理)

  • 权限是一个ID或者一个字符串
  • 谦虚用来细分权利(类似Capability,分散权利)
  • 通常一个权限与一个类操作绑定
  • 权限首先需要申请(AndroidManifest或者代码动态申请)
  • 但是申请后是否被批准有平台策略决定

    如:用户需要读取SDCard的权限,这时Android平台会弹出访问SDCard的窗口,如果用户accept了,那这个权限就被申请.

权限的安全性保护(通过签名)

  • 权限的完整性保护(防篡改)

    如发短信的方式,不可能没发一条短信都弹dialog来申请权限,所以这时开发只要在Manifest 中添加 sendSMS permission 就可以.(通过认证并获得签名后再加policy权限)

  • 权限的授权安全策略(防Escalate)

    如普通应用申请Inject Event 权限(注入)

签名作用

完整性鉴别

  • 自签名支持完整性鉴别

    Android中使用的是自签名方式,那就是无法对签名的信任进行认证,只能通过他的完整性鉴别.

  • 不做安装和运行时的限制(不做信任模式)

    Android不会在安装的时候进行Signature Permission 的验证,他仅仅是做Signature(签名) 这步骤.

Signature Permission 和ShareUID(TODO 讲得不错要做详细)

  • Signature Permission(Signature Permission Level Permission)

    相当于他对一些特权的permission 他用单独的signature 方式去验证.

    • 示例代码块:
 <permission android:name="android.permission.HARDWARE_TEST"//权限名称,要注意命名规范
    android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"//在显示时对权限进行归类
        android:protectionLevel="signature"// 权限安全界别
    android:label="@string/permlab_hardware_test"// 在对应语言系统上显示

  android:description="@string/permdesc_hardware_test"
  //该权限的描述与使用
  /> 

上面的protectionLevel="signature" ,因此他并不是给第三方应用去使用的.对于普通的app去使用该权限的话肯定是会被拒绝的.但是对于如果是系统应用的话只要要.mk 里的config写的是security="platefrom" 那么这个app里的Signature 就会使用平台的private key 进行签名.

  • ShareUID(Share Process UID)

    • 示例代码块:

      android:sharedUserid="xxx"

    其实Android中的UID也是在manifest 中写的,如上面代码,那么Android重为什么要有UID.

    UID其实是一个特权的概念,不同文件对一些权限会做一些限制,如对于 uid="owner" 会用r/w/x 权限,而对于uid="other" 可能就会连read 的权限都没有.这样就可以使得Android中每一个 project他对于的资源目录下是属于一个private 的状态.

    所以Android很好的引用了Linux自有的一些特性,将每个项目通过群组(UID)的方式分离出来.

    • Process间Share UID的目的是共享资源

      如果a,b,c三个应用他们之间的share UID是同一个,那么这几个应用间的资源是开源互相访问的.

      漏洞: 那么这样就会引出一个安全问题,如果360配置的时候将share UID设置成 QQ应用一样,而且框架允许的情况下,那么问题就来了,360可以很简单的就访问到了qq的数据.

    • Android中两个Apk Share相同的UID必须其签名所用的Private Key 一样.(为什么?)

      google的解决方法就是,如果多个应用之间真的要设置成Share UID相同,那么前提条件就是这几个应用的私钥也必须是一样的,是同一个owner ,也就是说厂家是同一家.

身份ID和升级的匹配

  • Android中的自签名只是代表了身份,但是不代表身份是否可信任
  • Android应用的identifier(鉴定者) 是Package Name:
    • Package Name不一样,相互不影响,只允许同时存在(安装)
    • Package Name一样,只能存在一个,允许做升级处理.
  • 升级的安全性考虑
    • 必须签名的证书一致(市场上假冒app)
    • 如果不一致,则用户要么放弃新的应用,要么先卸载旧的,再安装新的.
    • 正常的升级将不清除应用cache,以保证历史数据的持续性.

Android APK 之META INF

其实很应用的安装包,就是一个压缩包,把apk 后缀修改成.zip 就可以解压里面的数据.

  • APK的一个结构

    • assets
    • lib
    • META-INF 里面有3个文件,相当于是摘要信息文件(签名信息CERT.RSA,CERT.SF,MANIFEST.MF)
    • res
    • AndroidManifest
    • class.ex
  • 签名流程

    java -jar signapk.jar testkey.x509.pem testkey.pk8 update.apk update-signed.apk

Android中的权限

Android签名中使用到的一些加密技术有:

签名(特权权限)

权限作用

细粒度特权管理

  • 权限与操作关联

    每一个权限都与对应的操作(功能)有关联.

  • 应用需要显式申请权限

    如需要在AndroidManifest中去申请需要的权限.

  • 用户对权限可知(不可控)

    用户对该项目使用到的app需要涉及什么权限,都是可以查看到的,但是在安装app时必须accept所有权限才可以安装,否则就无法安装,这就是不可控,但是在Android6.0后google出现了RuntimePermission,对部分单个权限是可控的,而且当系统root后,也可以通过一些管家对某个app 的单个权限进行分配.

  • 对特权权限单独控制

    Android中对特权权限单独控制,这个方式其实就是通过签名来处理的.比如上面提到的sendSMS 发送短信的权限.

Android的不同权限类别

  • Normal 默认,一般安装不会被显示出来.
  • Dangerous 危险,安装时会有pop提示.
  • Signature 签名,对于一些特权权限就需要通过签名来保护,对apk的私钥邀请与签名一样.
  • SignatureOrSystem 签名or系统,对于申请权限的私钥跟签名一样,或者改app是系统app才可以使用该权限.
  • 自定义权限注意

    一般一些开发厂商或应用开发商会自定义一些权限.

注意:

对于自定义权限很简单,但是对于要设置他的protectionLevel这个问题还是比较关键的.如果level定义太严格,那以后拓展起来就比较差,如果定义太宽泛,就很容易出现安全漏洞.建议根据如下方式定义:

  • 根据需求应用范围用户权限,按照上面的权限类别描述来定义需要的权限.
  • 为了避免permission name 重复,命名规范也要注意.
  • 如,百度地图在自定义权限时,该sdk提供一些api但是如果这些功能只给百度自己公司的app 用,那就可以定义protectionLevel=”SignatureOrSystem”.

Android权限的定义源码路径

Android运行时权限控制方式

下面是两种方式的描述

通过PM的CheckPermission

  • Android独有的service(底层平台不具有)
  • 所以需要在Android本身framework中控制
  • 主流的service一般都基于binder IPC或者其他IPC提供服务
  • Android真正的service都是在底层实现的,所以在最底层控制(service所在的server中)以避免逃逸控制
    • 绕开Utility Function 直接 Invoke Remote Service
    • 如:DayDream(屏保)

映射为OS的特定属性

  • 非Android特有的service(底层平台已经提供,如File访问,TCPIP数据收发等)
  • 多个入口访问: Android API,Java API, NDK C API, Shell,etc
  • 底层控制准则,会聚扣在底层,所以在底层(OS层面)统一控制,这样可以避免逃逸控制.
  • 所以复用OS的一些安全控制特性,比如GID
  • 所以需要把Android空间的 Permission Mapping到OS的GID
  • 如: 访问SDCard

因为Android是基于Linux OS的那么Linux它有的一些安全特性与GID ,Android就可以对他复用,因此在对于多接口的访问安全问题就可以解决了,可以先看下面代码:

读取SDCard的权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

SDCard权限的映射关系

<permission name="android.permission.WRITE_EXTERNAL_STORAGE"
    <group gid="sdcard_rw"
/>

Android的permission 与UID/GID 的mapping

  • 任何自定义权限只要符合上面xml 语法的都会在system/etc/permissions 下面的xml文件,被系统读取并parse 在UID/GID中进行mapping. 如见system/etc/permissions 目录下的platform.xml .
  • 安全性问题
    • 任何应用都可以为自己的permission 分配GID吗?那不是安全漏洞?
    • 当然不行! 只有ROOT用户才允许新增或改写.