Android开发中关于组件导出的风险及防范

前言

近年来,移动APP存在一个非常的重要的问题就是安全问题,造成的后果有可能是用户的隐私泄露和财产损失等,对于一款成熟的APP或者是金融银行类APP,这无疑是最致命的,所以对APP进行有效的防范也是很有必要。

近段时间,公司安排了某安全公司对我们的APP进行了全方面的安全测试,根据文档检测结果看,整体上看还是很安全的,其中有一项就是组件导出风险,接下来我们说说四大组件、组件导出必要性、风险以及如何防范。

一、四大组件

从事Android开发,我们都知道Android有四大组件, 分别是:

  • 活动(Activity),用于表现功能,是用户操作的可视化界面,它为用户提供了一个完成操作指令的窗口;
  • 服务(Service),后台运行服务,不提供界面呈现;
  • 广播接受者(Broadcast Receive),用于接收广播;
  • 内容提供者(Content Provider),支持多个应用中存储和读取数据,相当于数据库。

从这些组件简单的介绍,我们知道它们的重要性,赋予了app更加丰富的功能,所以这四大组件的安全性对我们app和用户来说就显得更加地重要。

二、组件导出必要性

什么是组件导出呢?组件导出的意思就是组件可以被外部应用调用,我们可以在这四大组件声明的清单文件设置组件是否导出,如下:

        <activity
            android:exported="true"
            android:name=".other.ComponentActivity">
        </activity>

或者:

        <activity
            android:name=".other.ComponentActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
            </intent-filter>
        </activity>

上面两种方式都是Activity组件导出的方式,主要是exported的值",为true时表示导出,Activity中exported的默认值:

  • 没有intent filter时,默认为false;
  • 有intent filter时,默认为true

Broadcast Receive和Service的默认值都跟Activity的一样。

Content Provider中exported的默认值:

  • 当minSdkVersion或者targetSdkVersion小于16时,默认为true
  • 大于17时,默认为false

开发过程中,app会有一些特定需求会使用到三方SDK,如微信分享、支付、推送等功能,我们发现这里都有一个共同点,都会涉及到组件导出的问题,如微信的

WXEntryActivity:

        <!-- 微信分享 -->
        <activity
            android:name="${applicationId}.wxapi.WXEntryActivity"
            android:exported="true"
            android:launchMode="singleTask"
            android:theme="@android:style/Theme.Translucent.NoTitleBar" />

这样就会被安全机构检测出来的,如果不设置WXEntryActivity为组件导出,微信分享等功能根本就调不起来,这是官方的写法,我们认为这是必须要设置为组件导出,除非你把微信分享需求干掉,那业务不把你骂死;又或者是监听网络变化的广播接收器(7.0版本以上只能代码中动态注册才能接收该广播)、推送功能,集成过一些推送SDK都有印象,一些Service也会声明android:exported="true"等等。

这些无可避免的组件导出,我们可以回复安全机构:微信分享、推送等功能必须设置组件导出,所以我们只有保证自己的四大组件的设置,确保其是安全的,这样才能确保app处于比较安全的状态,应付安全检测,给你的领导一个交代。

三、组件导出风险

前面说明了组件的重要性、组件导出,那么组件导出的风险是什么呢?

  • Activity作为组成Apk的四个组件之一,是Android程序与用户交互的界面,如果Activity打开了导出权限,可能被系统或者第三方的App直接调出并使用。Activity导出可能导致登录界面被绕过、拒绝服务攻击、程序界面被第三方恶意调用等风险。
  • Broadcast Receiver作为组成Apk的四个组件之一,对外部事件进行过滤接收,并根据消息内容执行响应,如果设置了导出权限,可能被系统或者第三方的App直接调出并使用。Broadcast Receiver导出可能导致敏感信息泄露、登录界面被绕过等风险。S
  • ervice作为组成Apk的四个组件之一,一般作为后台运行的服务进程,如果设置了导出权限,可能被系统或者第三方的App直接调出并使用。Service导出可能导致拒绝服务攻击,程序功能被第三方恶意调用等风险。
  • Content Provider组成Apk的四个组件之一,是应用程序之间共享数据的容器,可以将应用程序的指定数据集提供给第三方的App,如果设置了导出权限,可能被系统或者第三方的App直接调出并使用。Content Provider导出可能导致程序内部的敏感信息泄露,数据库SQL注入等风险。

接下来以Activity导出为示例,说明下其风险,其它组件类比就好。首先Activity要在清单文件AndroidManifest.xml注册:

        <activity android:name="com.littlejerk.sample.other.WebActivity"/>

Activity的启动通常有两种方法

  • 显式启动,需要指定启动的Activity:
        Intent intent = new Intent(getContext(),WebActivity.class);
        intent.putExtra("URL","https://blog.csdn.net");
        startActivity(intent);
  • 隐式启动,Intent中不再包含需要启动的具体的Activity类,而是通过Intent提供某些信息,系统去检索符合启动意图的Activity,这里是通过意图过滤器声明Intent信息:动作(action)、数据(data)、分类(Category)、类型(Type),组件(Component)、和扩展信息(Extra)。
        <!-- 通过隐式启动的方式需要在AndroidManifest.xml文件声明-->
        <activity android:name=".other.WebActivity">
            <intent-filter>
                <action android:name="com.littlejerk.sample.action.VIEW_URL" />
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

        //调用方式启动WebActivity
        Intent intent = new Intent();
        intent.setAction("com.littlejerk.sample.action.VIEW_URL");
        intent.putExtra("URL","https://blog.csdn.net");
        startActivity(intent);

使用Action跳转,如果有一个程序的AndroidManifest.xml中的某一个 Activity的IntentFilter段中 定义了包含了相同的Action,那么这个Intent就与这个目标Action匹配。如果这个IntentFilter段中没有定义 Type、Category,那么这个 Activity就匹配了。但是如果手机中有两个以上的程序匹配,那么就会弹出一个对话框来提示说明。

上面说过有IntentFilter,如果不指定android:exported,那么该值默认为true,外部的应用通过隐式意图的方式也能将对应的组件启动起来。这种情况我们就是我们说的组件导出,而导出则意味着很有可能存在安全问题,接下来看下WebActivity页面:

        Intent intent = getIntent();
        String url = intent.getStringExtra("URL");
        UILog.e(TAG, url.charAt(0));
        mTvContent.setText(url);

我们注意到WebActivity只是接收一个URL并且显示出来(没有加载这个URL),从这里我们可以看出URL并没有做参数检验,应用可能会崩溃;因为该页面又是可被三方应用调用的,这时候如果别人恶意传递一些不良的网页信息,那你这个应用不拦截就直接加载了,则这个应用有可能就要下架了。

四、如何防范

我们以最常见的Activity为例说明了组件导出的风险,因为这个URL参数是我们处理的,我们可以防止应用空指针异常,这没问题,但是上面也说如果加载了不良URL呢?其实组件导出的风险最根本原因是被别人调用了,那这样有没有办法控制这个别人的范围,只允许我们信赖的人去调用。

在这里不得不提Android的权限机制,Android的Permission检查机制是用来控制一个应用拥有哪些执行权利。例如应用拥有拍照权限才能拥有拍照权利,那么我们是否可以通过权限来控制一个应用是否有启动WebActivity的权利呢?

Android提供了自定义权限的能力,应用可以定义自己的权限,如在清单文件中自定义一个permission:

    <permission
        android:label="允许打开WebActivity页面权限"
        android:name="com.littlejerk.sample.permission.WEB"
        android:protectionLevel="signature" />

label:权限的描述

name:该权限的名称,使用该权限时通过名称来指定使用的权限

protectionLevel:该权限受保护的等级,这很重要,它有三个等级

  • signature:签名级别权限,即权限的定义方和注册方必须具有相同的签名才有效
  • system:系统级别权限,即权限的定义方和注册方必须为系统应用
  • signatureOrSystem :同签名或系统应用,上述二者具备其一即可

权限定义完成,如何用它来保护暴露的组件呢,看下面代码:

        <!-- 通过隐式启动的方式需要在AndroidManifest.xml文件声明-->
        <activity
            android:permission="com.littlejerk.sample.permission.WEB"
            android:name=".other.WebActivity">
            <intent-filter>
                <action android:name="com.littlejerk.sample.action.VIEW_URL" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

在activity声明时,activity标签下有一个permission,通过permission就能指定保护该activity的权限名称了,这样,只有具有了该权限的activity才能启动它(注意在定义方和使用方都要在清单文件中定义和声明自定义的权限),在调用方的清单文件中声明和使用该权限:

    <!--调用方可不用声明-->
    <permission
        android:label="允许打开WebActivity页面权限"
        android:name="com.littlejerk.sample.permission.WEB"
        android:protectionLevel="signature" />
    <!--调用方必须申请此权限-->
    <uses-permission android:name="com.littlejerk.sample.permission.WEB"/>

有了权限的控制,activity组件导出的范围就可控了,当我们公司应用间存在相互的组件调用时,就可以使用同签名的权限来做限制,至于其它应用因为不是相同的签名,所以它们无法调用我们暴露出去的组件,这很有效地规避了风险。

Activity是我们最常见的一个组件了,但是BroadcastReceiver用的地方也不少,一般安全评测都有提到这个组件的,我们有必要提一提它,其实各个组件的安全控制也可通过permission来控制的。

BroadcastReceiver的注册有两种方式

  • 静态注册,在Manifest中声明注册
  • 动态注册,在代码中依赖其他组件,通过registerReceiver注册

BroadcastReceiver有广播的发送方和接收方,所以当使用permission来校验通信的时候一般都需要双向校验,即广播的方送方和接收方都需要添加权限检验,保证发送方只将广播发送给信赖的接收方,同样的接收方也只接受来自信赖方的广播。

广播发送方

发送方需要在清单文件AndroidManifest.xml中声明权限:

    <permission
        android:label="声明发送方权限"
        android:name="com.littlejerk.sample.permission.BROADCAST_SEND"
        android:protectionLevel="signature" />

然后使用sendBroadcast(Intent intent, String receiverPermission)方法发送广播:

        //发送广播
        Intent intent = new Intent();
        intent.setAction("com.littlejerk.sample.broadcast.action.TEST");
        sendBroadcast(intent, "com.littlejerk.sample.permission.BROADCAST_SEND");

从receiverPermission字面意思就知道,接收广播方必须要申请com.littlejerk.sample.permission.BROADCAST_SEND这个自定义权限,不然,无法接收到action通知,如接收方的清单文件AndroidManifest.xml:

    <!-- 接收方需申请发送方权限-->
    <uses-permission android:name="com.littlejerk.sample.permission.BROADCAST_SEND"/>

如果接收方的广播接收器不控制自己的权限,则同开发者应用只监听com.littlejerk.sample.broadcast.action.TEST这个action就行了,但是为了双重检验,我们也需要给接收方声明自己的权限。

广播接收方

我们定义一个广播接收器TestReceiver:

public class TestReceiver extends BroadcastReceiver {
    private static final String TAG = "TestReceiver";

    //接收到广播信息的回调
    @Override
    public void onReceive(Context context, Intent intent) {
        //对外来的参数应该做些合法的检查
        String action = intent.getAction();
        if (TextUtils.isEmpty(action)) {
            return;
        }
        UILog.e(TAG, "action:" + action);
    }
}

接着在清单文件AndroidManifest.xml中声明控制权限:

    <permission
        android:label="声明接收方权限"
        android:name="com.littlejerk.sample.permission.BROADCAST_RECEIVER"
        android:protectionLevel="signature" />

然后把这个控制权限给广播接收器,接收器有两种注册方式
静态注册方式,在清单文件AndroidManifest.xml中:

        <receiver
            android:name=".widget.receiver.TestReceiver"
            android:permission="com.littlejerk.sample.permission.BROADCAST_RECEIVER">
            <intent-filter>
                <action android:name="com.littlejerk.sample.broadcast.action.TEST"/>
            </intent-filter>
        </receiver>

然后是动态注册方式,在你需要注册的地方声明:

Receiver receiver = new Receiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.littlejerk.sample.broadcast.action.TEST");
registerReceiver(receiver, intentFilter, "com.littlejerk.sample.permission.BROADCAST_RECEIVER", null);

这两种注册方式都可以,但是推荐使用动态注册广播的方式,因为Android O上为了App性能和功耗的考虑,对静态注册的广播做了很大的限制,至于是什么限制,这里就不说了。

我们对接收器也做了权限限制,那么发送方也必须要申请这个权限才能发送action给它呀,所以发送方的清单文件AndroidManifest.xml中在原有的基础上需要添加:

    <!-- 发送方需申请接收方权限-->
    <uses-permission android:name="com.littlejerk.sample.permission.BROADCAST_RECEIVER"/>

至此,广播的双向检验就完成了,以上所有代码都测试过了,没有任何问题,很好地过滤了无关广播,保护了组件的安全。

总结

文章主要讲了四大组件的含义及其重要性,然后阐明为什么会组件导出及导出风险,有些组件导出时必须的,因为要实现一些特定功能,但是对于可控的组件,尽量设置不导出。如果需要导出组件,我们需要严格地做参数检验,防止崩溃;除此之外,最好地防范就是添加权限,这样才能有效地防止被恶意调用,造成不必要的损失。

到此这篇关于Android开发中关于组件导出的风险及防范的文章就介绍到这了,更多相关Android 组件导出内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android四大组件之Service详解

    一.Service简介 Service是Android程序中四大基础组件之一,它和Activity一样都是Context的子类,只不过它没有UI界面,是在后台运行的组件. Service是Android中实现程序后台运行的解决方案,它非常适用于去执行那些不需要和用户交互而且还要求长期运行的任务.Service默认并不会运行在子线程中,它也不运行在一个独立的进程中,它同样执行在UI线程中,因此,不要在Service中执行耗时的操作,除非你在Service中创建了子线程来完成耗时操作. 二.Serv

  • 详解Android的四大应用程序组件

    Android的一个核心特性就是一个应用程序可作为其他应用程序中的元素,可为其他应用程序提供数据.例如,如果程序需要用某些控件来加载一些图片,另一个程序已经开发出了此项功能,且可供其他程序使用,就可以直接使用跨进程通信方式调用那个程序的功能,而不是自己再开发一个.为了实现这样的功能,Android系统必须能够在需要应用程序中的任何一部分时启动它的进程,并且实例化那部分的Java对象.所以,不像大多数其他系统中的程序,Android程序不是只有单一的进入点,而是它们拥有系统实例化和运行必须的组件,

  • Android四大组件之Activity详解

    一.Activity的生命周期 首先,我们来了解一下Activity典型的生命周期 一个Activity从启动到结束会以如下顺序经历整个生命周期: onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory().包含了六个部分,还有一个onRestart()没有调用, 下面就来一一介绍 onCreate():当 Activity 第一次创建时会被调用.当 Activity 第一次创建时会被调用.这是生命周

  • Android开发之自定义UI组件详解

    Android开发自定义UI组件实现红色小球跟随手指移动 要写实现自定义UI组件,要创建一个BallView类,继承View类,在BallView类中创建画笔,然后重写OnDraw()方法和OnTouchEvent()方法. /** * Created by nuist__NJUPT on 2021/5/9. * 自定义UI组件 * View组件在布局中是一个矩形的空白区域,没有任何内容 * 而UI组件之所以有内容,是因为继承了View组件之后在其提供的空白区域上重新绘制外观,这就是UI组件的实

  • Android开发中关于组件导出的风险及防范

    前言 近年来,移动APP存在一个非常的重要的问题就是安全问题,造成的后果有可能是用户的隐私泄露和财产损失等,对于一款成熟的APP或者是金融银行类APP,这无疑是最致命的,所以对APP进行有效的防范也是很有必要. 近段时间,公司安排了某安全公司对我们的APP进行了全方面的安全测试,根据文档检测结果看,整体上看还是很安全的,其中有一项就是组件导出风险,接下来我们说说四大组件.组件导出必要性.风险以及如何防范. 一.四大组件 从事Android开发,我们都知道Android有四大组件, 分别是: 活动

  • Android开发中Flutter组件实用技巧

    目录 正文 简化 Assert 管理 更容易 imports 从按钮上移除飞溅效果 更简单的平台小工具 可见性小工具 正文 今天我将向您展示 4 个非常有用的 Flutter 技巧,您可以立即应用到您的项目.我不会向您展示任何包或扩展,就像我通常做的那样,但是非常简单,但是非常有用的提示! 简化 Assert 管理 管理 Assert 可能非常困难.如果你想在你的应用程序中多次使用一个图像,你必须一次又一次地指定路径.但是有一个简单得多的解决方案.创建一个 App Assets 类,用于存储所有

  • Android开发中Button组件的使用

    前言 安卓系统中,Button是程序和用户进行交互的一个重要控件,今天我们就来简单的对Button进行学习,其中Button组件是文本按钮(继承自TextView),而ImageButton是图像按钮(继承自ImageView).两者之间的区别在于: 1.Button即可显示文本也可显示图形(通过设置背景图),而ImageButton只能显示图形不能显示文本: 2.Button可在文本周围区域显示小图,而ImageButton无法在某个区域显示小图: 3.ImageButton上的图像可按比例进

  • Android开发中RecyclerView组件使用的一些进阶技讲解

    RecyclerView的优势: 它自带ViewHolder来实现View的复用机制,再也不用ListView那样在getView()里自己写了 使用LayoutManager可以实现ListView,GridView以及流式布局的列表效果 通过setItemAnimator(ItemAnimator animator)可以实现增删动画(懒的话,可以使用默认的ItemAnimator对象,效果也不错) 控制item的间隔,可以使用addItemDecoration(ItemDecoration

  • Android开发中button按钮的使用及动态添加组件方法示例

    本文实例讲述了Android开发中button按钮的使用及动态添加组件方法.分享给大家供大家参考,具体如下: MainActivity.java package com.example.lab2; import android.os.Bundle; import android.app.Activity; import android.content.Context; import android.view.Menu; import android.view.View; import andro

  • Android开发中使用颜色矩阵改变图片颜色,透明度及亮度的方法

    本文实例讲述了Android开发中使用颜色矩阵改变图片颜色,透明度及亮度的方法.分享给大家供大家参考,具体如下: 一.如图 二.代码实现 public class ColorImageActivity extends Activity { private ImageView mImageView; private SeekBar mSBRed,mSBGreen,mSBBlue,mSBAlpha,mSBLight; //修改后的图片 private Bitmap mModBitmap; //画布

  • 深入解读Android开发中Activity的生命周期

    什么是Activity        首先,Activity是Android系统中的四大组件之一,可以用于显示View.Activity是一个与用记交互的系统模块,几乎所有的Activity都是和用户进行交互的,但是如果这样就能说Activity主要是用来显示View就不太正确了. 在深入了解Activity之前,我们先要知道一下MVC设计模式,在JAVAEE 中MVC设计模式已经很经典了,而且分的也比较清晰了,但是在Android中,好多人对MVC在Android开发中的应用不是很清楚,下面我

  • RxJava入门指南及其在Android开发中的使用示例

    RxJava的GitHub主页,部署部分就没什么好说的了~ https://github.com/ReactiveX/RxJava 基础 RxJava最核心的两个东西是Observables(被观察者,事件源)和Subscribers(观察者).Observables发出一系列事件,Subscribers处理这些事件.这里的事件可以是任何你感兴趣的东西(触摸事件,web接口调用返回的数据...) 一个Observable可以发出零个或者多个事件,知道结束或者出错.每发出一个事件,就会调用它的Su

  • Android开发中滑动分页功能实例详解

    本文实例讲述了Android开发中滑动分页功能.分享给大家供大家参考,具体如下: android UI 往右滑动,滑动到最后一页就自动加载数据并显示 如图: Java代码: package cn.anycall.ju; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.Activity; import andro

  • Android开发中ProgressDialog简单用法示例

    本文实例讲述了Android开发中ProgressDialog简单用法.分享给大家供大家参考,具体如下: 网上一般对进度条的示例都是如何显示,没有在任务结束如何关闭的文章,参考其他文章经过试验之后把整套进度条显示的简单示例如下: 建立android工程等工作都略去,Google一下就可以了. 下面来介绍主要的Activity ProgressBarDemo.java package com.lveyo.android.demo.progressbar; import android.app.Ac

随机推荐