Android 防介面劫持方案,無視Android系統版本限制,無需操作棧
阿新 • • 發佈:2019-02-07
Android 防介面劫持方案
Demo下載地址
分析
要想真正做到防劫持,就需要在我們的程式執行時,不允許啟動其他應用程式。但是Android為了提高使用者的使用者體驗,對於不同的應用程式之間的切換,基本上是無縫,如果在啟動一個Activity時,給它加入一個標誌位FLAG_ACTIVITY_NEW_TASK,就能使它置於棧頂並立馬呈現給使用者。在這種機制下,無法做到真正的防劫持
那麼只能使用一種折中的方案,使用者使用app的時候,如果被惡意程式劫持跳轉到別的介面,這個時候我們就要做出預警提示使用者,告訴使用者當前介面已經不是我們的app了,有潛在的危險。
這種方案主要有一個問題需要攻克,就是監控是否有程式覆蓋了我們的APP,也就是我們需要監控棧頂的變化,在Android5.0之前,可以使用ActivityManager中提供的方法操作棧,但是在Android5.0時,谷歌由於使用者隱私的考慮,弱化了這個介面,ActivityManager只能管理自己APP的棧,如果想管理其他APP的棧,需要使用者主動授權,而好多手機廠商又對這個許可權做了進一步限制,只能為特定的APP授權(如系統捆綁應用)。這樣Android5.0之後就無法通過管理棧的方式來進行防劫持。
既然只能對自己的應用進行操作,我們可以從Activity的生命週期做些文章,具體方案往下看
方案
1. 在所有Activity的onPause()方法中,彈出提示使用者的警告,延遲1-2秒執行
2. 在所有Activity的onResume()方法中,取消在onPause中的提示
效果
● 當自己的應用切換頁面時,觸發當前頁面的onPause()方法,並立刻觸發要跳轉頁面的onResume()方法,這樣就不會發出警告提示使用者。
● 當用戶主動切換應用程式或者主動退回到桌面時,觸發當前介面的onPause()方法,1-2秒後出現警告
● 當應用介面遭到惡意程式劫持時,觸發當前介面的onPause()方法,1-2秒後出現警告
注意
1. 不能使用onStop()方法來代替onPause(),因為大部分劫持介面都採用透明主題,而跳轉到透明Activity時,是不觸發onStop()方法的,可以使用onStart()方法來代替onResume()
2. 當程式中需要載入第三方的Activity時,防劫持機制會判斷第三方的Activity是非法的,並彈出警告,專案上在使用時可自行呼叫onResume(),來取消警告
程式碼
BaseActivity.java
package com.example.test;
import android.app.Activity;
public class BaseActivity extends Activity {
@Override
protected void onResume() {
Anti_hijackingUtils.getinstance().onResume();
super.onResume();
}
@Override
protected void onPause() {
Anti_hijackingUtils.getinstance().onPause(this);
super.onPause();
}
}
Anti_hijackingUtils.java
package com.example.test;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.widget.Toast;
public class Anti_hijackingUtils {
/**
* 用於執行定時任務
*/
private Timer timer = null;
/**
* 用於儲存當前任務
*/
private List<MyTimerTask> tasks = null;
/**
* 唯一例項
*/
private static Anti_hijackingUtils anti_hijackingUtils;
private Anti_hijackingUtils() {
// 初始化
tasks = new ArrayList<MyTimerTask>();
timer = new Timer();
}
/**
* 獲取唯一例項
*
* @return 唯一例項
*/
public static Anti_hijackingUtils getinstance() {
if (anti_hijackingUtils == null) {
anti_hijackingUtils = new Anti_hijackingUtils();
}
return anti_hijackingUtils;
}
/**
* 在activity的onPause()方法中呼叫
*
* @param activity
*/
public void onPause(final Activity activity) {
MyTimerTask task = new MyTimerTask(activity);
tasks.add(task);
timer.schedule(task, 2000);
}
/**
* 在activity的onResume()方法中呼叫
*/
public void onResume() {
if (tasks.size() > 0) {
tasks.get(tasks.size() - 1).setCanRun(false);
tasks.remove(tasks.size() - 1);
}
}
/**
* 自定義TimerTask類
*/
class MyTimerTask extends TimerTask {
/**
* 任務是否有效
*/
private boolean canRun = true;
private Activity activity;
public void setCanRun(boolean canRun) {
this.canRun = canRun;
}
public MyTimerTask(Activity activity) {
this.activity = activity;
}
@Override
public void run() {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (canRun) {
// 程式退到後臺,進行風險警告
Toast.makeText(activity, "應用Test切換至後臺執行",
Toast.LENGTH_LONG).show();
tasks.remove(MyTimerTask.this);
}
}
});
}
}
}