app安全:如何应对界面劫持

app安全:如何应对界面劫持

写这篇文章是因为,公司的项目对安全性要求较高一些,所以每年都会不定时把app提交到三方检测机构进行安全检测,其中有一项就是界面劫持风险,那么接下来,就一步步教大家怎么解决这个问题。

界面劫持的原理

在android系统中,程序可以枚举当前运行的进程而不需要声明任何权限,如果攻击者注册一个receiver,响应android.intent.action.BOOT_COMPLETED,使得开启启动一个service;在这个service中启动一个计时器,不停枚举当前进程中是否有预设的进程启动,如果发现有预设进程,则使用FLAG_ACTIVITY_NEW_TASK启动自己的钓鱼界面,而正常应用的界面则隐藏在钓鱼界面的后面。

解决办法

这是系统漏洞,在应用程序中很难去防止这种界面支持。但应用程序自身可以增加一些防范实施。
防范实施:
1. 开启守护进程,当发现应用程序不在栈顶时,在屏幕最上层创建一个悬浮小窗口(提示信息与客户确定),以提醒用户。
2. 使用抢占式,即与劫持程序抢占栈顶。
3. 在应用切到后台时,在通知栏弹出通知提示。

以上三种防范措施都是可取的,但是其中第二种,抢占式的抢占栈顶这种做法,频繁出现的话,用户会非常反感,于是,我们最终的方案是结合第一种和第三种方法来处理:app被切到后台后Toast弹框并在通知栏显示一条通知。(提醒用户,app被切到后台运行)

具体实施

1. 在Activity的各生命周期中启动或者停止服务(在onResume中开启service,在onStart和onDestory中关闭service)
示例:


@Override
protected void onPause() {

    Intent intent = new Intent();
    intent.putExtra("pageName", this.getComponentName()
            .getPackageName());
    intent.putExtra("className", this.getComponentName().getClassName());
    intent.setClass(this, AppStatusService.class);
    startService(intent);
}

public void onResume() {

    Intent intent = new Intent();
    intent.putExtra("pageName", this.getComponentName().getPackageName());
    intent.putExtra("className", this.getComponentName().getClassName());
    intent.setClass(this, AppStatusService.class);
    stopService(intent);
}

@Override
public void onDestroy() {
    Intent intent = new Intent();
    intent.setClass(this, AppStatusService.class);
    stopService(intent);
}
2. 编写具体的Service逻辑
/**
 * 应用是否在前台运行
 * 
 * @return true:在前台运行;false:已经被切到后台了
 */
private boolean isAppOnForeground() {
    List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
    if (appProcesses != null) {
        for (RunningAppProcessInfo appProcess : appProcesses) {
            if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                if (appProcess.processName.equals(packageName)) {
                    return true;
                }
            }
        }
    }
    return false;
}
/**
 * 定义一个timerTask来发通知和弹出Toast
 */
TimerTask timerTask = new TimerTask() {

    @Override
    public void run() {
        if (!isAppOnForeground()) {

            isAppBackground = true;
            //发通知
            showNotification();
            //弹出Toast提示
            MainActivity.mCurrentActivity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(getApplicationContext(),
                            getApplicationContext().getString(R.string.pervent_hijack_mes), Toast.LENGTH_SHORT)
                            .show();
                }
            });
            mTimer.cancel();
        }
    }
};
/**
 * 弹出通知提示
 */
private void showNotification() {
    NotificationManager notificationManager = (NotificationManager) getSystemService(
            android.content.Context.NOTIFICATION_SERVICE);
    Notification notification = new Notification(R.drawable.icon, "xx银行", System.currentTimeMillis());
    notification.defaults |= Notification.DEFAULT_VIBRATE;// 设置震动
    notification.defaults |= Notification.DEFAULT_LIGHTS;// 设置LED灯提醒
    notification.flags |= Notification.FLAG_NO_CLEAR;// 通知不可被状态栏的清除按钮清除掉
    notification.flags |= Notification.FLAG_ONGOING_EVENT;// 通知放置在 正在运行

    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_MAIN);
    intent.putExtra("notification", "notification");
    intent.setClassName(packageName, className);

    // 修改vivo手机点击通知栏不返回
    intent.addCategory(Intent.CATEGORY_LAUNCHER);

    // 增加Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED用于正常的从后台再次返回到原来退出时的页面中
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
    PendingIntent pendingInt = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    String temp = "";
    notification.setLatestEventInfo(this, "xx手机银行", "手机银行已经被切到后台运行" + temp, pendingInt);
    // 设置服务的级别,使其不容易被kill掉,解决后台返回前台黑屏的问题
    startForeground(1, notification);
}
3. 在onStart中执行timer
        @Override
public void onStart(Intent intent, int startId) {
    try {
        Bundle bundle = null;
        if (intent != null) {
            bundle = intent.getExtras();
        }
        if (bundle != null) {
            className = bundle.getString("className");
        }
        // 通过计时器延迟执行
        mTimer.schedule(timerTask, 50, 50);

    } catch (Exception e) {
        e.printStackTrace();
    }
}
具体效果如图:

补充内容

根据importance的不同来判断前台或后台
RunningAppProcessInfo 里面的常量IMOPORTANCE就是上面所说的前台后台,其实IMOPORTANCE是表示这个app进程的重要性,因为系统回收时候,会根据IMOPORTANCE来回收进程的。具体可以去看文档。
public static final int IMPORTANCE_BACKGROUND = 400//后台
public static final int IMPORTANCE_EMPTY = 500//空进程
public static final int IMPORTANCE_FOREGROUND =100//在屏幕最前端、可获取到焦点 可理解为Activity生命周期的OnResume();
public static final int IMPORTANCE_SERVICE = 300//在服务中
public static final int IMPORTANCE_VISIBLE = 200//在屏幕前端、获取不到焦点可理解为Activity生命周期的OnStart();

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值