本文继续Android安全话题,继续说道第四篇,继续对具体安全漏洞做具体的防止实践。本篇介绍组件劫持攻防。本文让你明白手机QQ强制登录提示功能的原理。
Activity是android最基本的组件,也是最常用的组件,基本运行原理请见:https://developer.android.com/reference/android/app/ActivityManager.html, activity的启动模式(四大启动模式自行找文看)决定了安全存在风险。于是存在了劫持风险。Activity劫持主要的表现是自己的Activity被恶意攻击,或者仿冒的恶意Activity界面进行攻击和非法用途。此漏洞的存在允许执行网络钓鱼攻击,以及用户在相关活动中处理的信息泄露。此外,此漏洞允许攻击者修改数据,从而危及其完整性。
轻度劫持
原理:
恶意被第三方程序启动,绕过本身的业务逻辑,造成数据泄露。主要体现在自己的首页被非法启动,绕过登录界面,导致可直接进行数据浏览和查看,或者携带对本app有害的指令,造成其他问题。
可以参考以前本号发布的安全要点与规范 中权限的描述。由于android所有组件通讯都会是基于intent来实现的,对于Intent的处理都有相关的action 中的 permission 进行过滤,所以如果开发者不严谨就会造成acivity被恶意开启。
解决方案: 第二篇安全要点中已经说明组件权限,具体属性请自行阅读,这里不过多解释!
<activity android:name=".MainActivity"
android:label="@string/title_activity_single"
android:exported="false"
android:permission="自定义权限"
android:protectionLevel="normal" />
如果还需要做加固,Android 提供各种 API 来在运行时检查、执行、授予和撤销权限。这些 API是 ContextWraper的一部分,这个类提供有关应用程序环境的全局信息。
if (context.checkCallingOrSelfPermission("自定义权限")
!= PackageManager.PERMISSION_GRANTED) {
// The Application requires permission to access the // Internet");
} else {
// handler
}
深度劫持
原理: 启动一个Activity
时,给它加入一个标志位FLAG_ACTIVITY_NEW_TASK
,就能使它置于栈顶并立马呈现给用户。如果不加以区别就能获取用户的相关信息,如果伪造出一个登录界面,搜集后发往云端,那么就会造成账户秘密泄露。被恶意程序截获 ,产生恶意程序的界面或者假冒伪造的界面覆盖在最上层,导致非法采集用户信息,盗取账号和密码。俗称钓鱼。 常用攻击要体现在登录和支付。
解决方案:
没有直接的解决方案, 只能引导用户!通过遍历系统当前最上层Activity
,来判断是否是自己应用的界面,来做友好提示,防止用户被钓鱼程序劫持。下面就请来姿势砸我吧!
public static boolean checkMyself(Context context) {
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> listAppcations =
pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
Collections.sort(listAppcations, new
ApplicationInfo.DisplayNameComparator(pm));
// 排序
List<ApplicationInfo> appInfos = new ArrayList<ApplicationInfo>();
// 保存过滤查到的AppInfo
for (ApplicationInfo app : listAppcations) {
//这个排序必须有.
if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
//appInfos.add(getAppInfo(app));
safePackages.add(app.packageName);
}
} //得到所有的系统程序包名放进白名单里面.
ActivityManager activityManager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); String runningActivityPackageName;
int sdkVersion; try {
sdkVersion =
Integer.valueOf(android.os.Build.VERSION.SDK);
} catch (NumberFormatException e) {
sdkVersion = 0;
}
if (sdkVersion >= 21) {
//获取系统api版本号,如果是5x系统就用这个方法获取当前运行的包名
runningActivityPackageName = getCurrentPkgName(context);
} else {
runningActivityPackageName =
activityManager.getRunningTasks(1).get(0).topActivity.getPackageName();
}
//如果是4x及以下,用这个方法.
if (runningActivityPackageName != null) {
//有些情况下在5x的手机中可能获取不到当前运行的包名,
所以要非 空判断。
if(runningActivityPackageName.equals(context.getPackageName()) ) {
safe = true;
}
// 白名单比对
for (String safePack : safePackages) {
if (safePack.equals(runningActivityPackageName)) {
safe = true;
}
}
}
return safe;
}
因此在需要的检测的Activity中调用代码检查!
@Override protected void onStop() {
super.onStop(); if (checkMyself(this)){
//todo
} else {
//todo
}
}
该服务依附在主进程上,不是独立的进程。本地服务在一定程度上节约了资源,由于是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。主进程被Kill后,服务便会终止。
该服务是独立的进程,对应进程名格式为所在包名加上你指定的android:process
字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。但由于独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。一些提供系统服务的Service通常是常驻的。
由于Service于外界也是通过Intent机制来协助应用间的交互与通讯,因此Intent的信息和描述就可能对Service发起攻击,造成自己的服务被停止服务漏洞.
1) 漏洞位置: 处理getIntent()的intent附带的数据 2) 漏洞触发前提条件: getIntent()的intent附带空数据、异常或畸形数据; 处理getXXXExtra()获取的数据时没有进行异常捕获. 3) 漏洞原理: Android系统中提供了Intent机制来协助应用间的交互与通讯,其负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,系统则根据此Intent的描述,负责找到对应的组件,将Intent传递给调用的组件,并完成组件的调用。调用的组件在处理Intent附加数据的时候,没有进行异常捕获,因此当处理空数据、异常或者畸形数据时,导致应用崩溃。
service也需要在manifest注册,因此权限属性和activity一样,出于安全考虑,应将不必要的组件设置私有,防止引起拒绝服务,尤其是杀毒、安全防护、锁屏防盗等安全应用; 在AndroidMenifest.xml
文件中,将相应组件的“android:exported
”属性设置为“false”
,如下示例:
<service android:name="MyService"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action_tamic"/>
</intent-filter>
</service>
Content Provider和Receiver同上,具体我们也需要加入权限和声明为内部使用。干货文章有啥理由不关注呢?
猜你喜欢
技术 - 资讯 - 感悟
END