App安全(一) Android防止升级过程被劫持和换包
文/ Tamic
地址/ http://blog.csdn.net/sk719887916/article/details/52233112
前言
APP 安全一直是开发者头痛的事情,越来越多的安全漏洞,使得开发者
越来越重视app安全,目前app安全主要有由以下几部分
APP组件安全
Android 包括四大组件:Activitie、Service、Content Provider、Broadband Receiver,
它们每一个都可以通过外面隐式的Intent方式打开,
android组件对外开放 就会被其他程序劫持,因此必须在manifest里面声明
exported为false,禁止其他程序访问改组件,
对于要和外部交互的组件,应当添加一下访问权限的控制, 在有权限后外部程序才可以开启,或者提供特定的action过滤器达到启动目的。
还需要要对传递的数据进行安全的校验。不符合规则的一律不处理。
Webview 安全漏洞
Android API 4.4以前,谷歌的webview存在安全漏洞,网站可以通过js注入就可以随便拿到客户端的重要信息,
甚至轻而易举的调用本地代码进行流氓行为,谷歌后来发现有此漏洞后
,在API 4.4以后增加了防御措施,如果用js调用本地代码,开发者必须在代码申明JavascriptInterface,
列如在4.0之前我们要使得webView加载js只需如下代码:
mWebView.addJavascriptInterface(new JsToJava(), “myjsfunction”);
4.4之后使用需要在调用Java方法加入@JavascriptInterface注解,
如果代码无此申明,那么也就无法使得js生效,也就是说这样就可以避免恶意网页利用js对客户端的进行窃取和攻击。
APP反编译
App被反编后,源码暴露,不仅对数据造成隐私泄露,而且对一些接口造成易攻击的潜在风险。
我们必须对apk进行源码混淆,并且进行apk加固
APP二次打包
即反编译后重新加入恶意的代码逻辑,或置入新病毒重新生成一个新APK文件。
二次的目的一般都是是盈利广告和病毒结合,对正版apk进行解包,插入恶意病毒后重新打包并发布,因此伪装性很强。截住app重打包就一定程度上防止了病毒的传播。因此app加固是防止二次打包的重要措施。
APP进程劫持
一般我们称为进程注入,也就动态注入技术,hook技术目前主流的进程注入方式,通过对linux进行so注入,达到挂钩远程函数实现监控远程进程的目的。
APP DNS劫持
DNS劫持俗称抓包。通过对url的二次劫持,修改参数和返回值,进而进行对app访问web数据伪装,实现注入广告和假数据,甚至起到导流用户的作用,严重的可以通过对登录APi的劫持可以获取用户密码,也可以对app升级做劫持,下载病毒apk等目的,解决方法一般用https进行传输数据。
APP APi接口验签
一般服务端的接口也会被攻击,虽然是服务端安全问题,但还是属于App系统维护体系,如果app后端挂了,app也不叫app了,一般变现为被恶意程序频繁请求某一接口,导致订单等重复注入,验证码被盗刷,虚假用户注册,严重的甚至拖垮app.
今天先看下APP升级过程被劫持的问题
我们做app版本升级时一般流程是采用请求升级接口,如果有升级,服务端返回下一个下载地址,下载好Apk后,再点击安装。
其实这个过程中有三个地方会被劫持。 请求升级时,下载文件时,安装时。
- 升级APi
升级Api建议用https,防止被恶意程序劫持,结果是恶意返回下载地址,这样就把伪装的apk下载到本地,结果你应该懂的!
下载API:
如果升级api你做了加固,下载api没做加固,还是徒劳,恶意程序也可以返回恶意文件或者apk,直到被你错误的安装在手机上。
- 安装过程;
假设你以上两个过程都做了加固,但是在安装apk的时候,本地文件path被错误修改了,仍然可以安装错误的apk,这不仅
会对用户体验产生不利,甚至会威胁手机安全。
解决方案:
升级api加入https 这个肯定不用再过多介绍,看了我介绍的retrofit 的https就明白了。
下载Api也需加入https,也不用再做介绍,这里着重强调的是需要对服务端返回的文件进行Hash值校验,防止文件被篡改,
通过对文件hash值,还要对服务端返回的自定义key的进行校验验签,防止不是自己服务器返回错误的文件安装过程也必须对Apk文件进行包名和签名验证,防止Apk被恶意植入木马,或替换。
假设我的升级bean为;
public class UpgradeModel {
private int code;
private String msg;
private DataBean data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public DataBean getData() {
return data;
}
public void setData(DataBean data) {
this.data = data;
}
public static class DataBean {
private String description;
private String downUrl;
private String version;
private String hashcod;
private String key;
private String isForce;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getDownUrl() {
return downUrl;
}
public void setDownUrl(String downUrl) {
this.downUrl = downUrl;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getHashcod() {
return hashcod;
}
public void setHashcod(String hashcod) {
this.hashcod = hashcod;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getIsForce() {
return isForce;
}
public void setIsForce(String isForce) {
this.isForce = isForce;
}
}
}
通过一次请求到服务端数据后,如果有版本更新 我们应该先验证
Url和key是不是我们和服务端协商好的Url和key
UpgradeModel aResult = xxxx//解析服务器返回的后数据
if (aResult != null && aResult.getData() != null ) {
String url = aResult.getData().getDownUrl();
if (url == null || !TextUtils.equals(url, "这里是你知道的下载地址: 也可以只验证hostUrl")) {
// 如果符合,说明不是目标下载地址,就不去下载
}
接着我们验证下载url是你自己app的服务器地址,然后再去请求下载Api,这时用DownLoadModel接受请求头 ,看是否符合自己和服务器约定的key和hash之,下载好apk到本地后,继续判断文件的hash和升级api返回的hashcode, 加之key是否是和下载服务器返回的key,如果不一致,就不安装
File file = DownUtils.getFile(url);
// 监测是否要重新下载
if (file.exists() && TextUtils.equals(aResult.getData().getHashCode(), EncryptUtils.Md5File(file))) {
&& TextUtils.equals(aResult.getData().getKey(), DownLoadModel.getData()..getKey())
// 如果符合,就去安装 不符合重新下载 删除恶意文件
}
等我们验证了下载文件的地址是我们自己服务器提供的,验证没问题后就只剩安装了。接着还要对apk文件进行包名和签名校验,如果包名和签名不一致,那么就是伪装程序,这个漏洞显而易见!
/** installApK
* @param context
* @param path
* @param name
*/
public static void installApK(Context context, final String path, final String name ) {
if (!SafetyUtils.checkFile(path + name, context)) {
return;
}
if (!SafetyUtils.checkPagakgeName(context, path + name)) {
Toast.makeText(context, "升级包被恶意软件篡改 请重新升级下载安装", Toast.LENGTH_SHORT ).show();
DLUtils.deleteFile(path + name);
((Activity)context).finish();
return;
}
switch (SafetyUtils.checkPagakgeSign(context, path + name)) {
case SafetyUtils.SUCCESS:
DLUtils.openFile(path + name, context);
break;
case SafetyUtils.SIGNATURES_INVALIDATE:
Toast.makeText(context, "升级包安全校验失败 请重新升级", Toast.LENGTH_SHORT ).show();
((Activity)context).finish();
break;
case SafetyUtils.VERIFY_SIGNATURES_FAIL:
Toast.makeText(context, "升级包为盗版应用 请重新升级", Toast.LENGTH_SHORT ).show();
((Activity)context).finish();
break;
default:
break;
}
}
SafetyUtils安全类如下:
/**
* 安全校验
* Created by LIUYONGKUI on 2016-04-21.
*/
public class SafetyUtils {
/** install sucess */
protected static final int SUCCESS = 0;
/** SIGNATURES_INVALIDATE */
protected static final int SIGNATURES_INVALIDATE = 3;
/** SIGNATURES_NOT_SAME */
protected static final int VERIFY_SIGNATURES_FAIL = 4;
/** is needcheck */
private static final boolean NEED_VERIFY_CERT = true;
/**
* checkPagakgeSigns.
*/
public static int checkPagakgeSign(Context context, String srcPluginFile) {
PackageInfo PackageInfo = context.getPackageManager().getPackageArchiveInfo(srcPluginFile, 0);
//Signature[] pluginSignatures = PackageInfo.signatures;
Signature[] pluginSignatures = PackageVerifyer.collectCertificates(srcPluginFile, false);
boolean isDebugable = (0 != (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));
if (pluginSignatures == null) {
PaLog.e("签名验证失败", srcPluginFile);
new File(srcPluginFile).delete();
return SIGNATURES_INVALIDATE;
} else if (NEED_VERIFY_CERT && !isDebugable) {
//可选步骤,验证APK证书是否和现在程序证书相同。
Signature[] mainSignatures = null;
try {
PackageInfo pkgInfo = context.getPackageManager().getPackageInfo(
context.getPackageName(), PackageManager.GET_SIGNATURES);
mainSignatures = pkgInfo.signatures;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
if (!PackageVerifyer.isSignaturesSame(mainSignatures, pluginSignatures)) {
PaLog.e("升级包证书和旧版本证书不一致", srcPluginFile);
new File(srcPluginFile).delete();
return VERIFY_SIGNATURES_FAIL;
}
}
return SUCCESS;
}
/**
* checkPagakgeName
* @param context
* @param srcNewFile
* @return
*/
public static boolean checkPagakgeName (Context context, String srcNewFile) {
PackageInfo packageInfo = context.getPackageManager().getPackageArchiveInfo(srcNewFile, PackageManager.GET_ACTIVITIES);
if (packageInfo != null) {
return TextUtils.equals(context.getPackageName(), packageInfo.packageName);
}
return false;
}
/**
* checkFile
*
* @param aPath
* 文件路径
* @param context
* context
*/
public static boolean checkFile(String aPath, Context context) {
File aFile = new File(aPath);
if (aFile == null || !aFile.exists()) {
Toast.makeText(context, "安装包已被恶意软件删除", Toast.LENGTH_SHORT).show();
return false;
}
if (context == null) {
Toast.makeText(context, "安装包异常", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
}
后续
这样我们的对升级流程的安全漏洞已经做到很安全细微了,很难被恶意程序轻易劫持,其他剩下的安全问题:服务器验签,js注入,hook注入 下期再接着分析讲解。
Tamic : 可搜索同名简书
gitHub: https://github.com/NeglectedByBoss
App安全(一) Android防止升级过程被劫持和换包的更多相关文章
- android recovery升级过程中掉电处理
一般在升级过程,都会提示用户,请勿断电,不管是android的STB,TV还是PHONE,或者是其他的终端设备,升级过程,基本上都可以看到“正在升级,请勿断电”,然后有个进度条,显示升级的进度. 但是 ...
- android 在线升级借助开源中国App源码
android 在线升级借助开源中国App源码 http://www.cnblogs.com/luomingui/p/3949429.html android 在线升级借助开源中国App源码分析如下: ...
- Android系统Recovery工作原理之使用update.zip升级过程---updater-script脚本语法简介以及执行流程(转)
目前update-script脚本格式是edify,其与amend有何区别,暂不讨论,我们只分析其中主要的语法,以及脚本的流程控制. 一.update-script脚本语法简介: 我们顺着所生成的脚本 ...
- Android数据库升级
随着Android应用版本的迭代,经常遇到数据库表结构发生改变,或者一些指定的表数据需要更新.这也就引出一个问题Android数据库的更新问题. Android数据库升级分类 Android数据库更新 ...
- 《大话移动APP测试:Android与iOS应用测试指南》
<大话移动app测试:android与ios应用测试指南> 基本信息 作者: 陈晔 出版社:清华大学出版社 ISBN:9787302368793 上架时间:2014-7-7 出版日期:20 ...
- Android Studio 升级为3.1 踩到的坑
原文:https://blog.csdn.net/xiariluoxue/article/details/80050700 AndroidStudio.gradle.buildToolsVersion ...
- OTA制作及升级过程笔记【转】
本文转载自:http://www.it610.com/article/5752570.htm 1.概述 1.1 文档概要 前段时间学习了AndroidRecovery模式及OTA升级过程,为加深理 ...
- Android Recovery升级原理
摘要 Recovery模式指的是一种可以对安卓机内部的数据或系统进行修改的模式(类似于windows PE或DOS).也可以称之为安卓的恢复模式,在这个所谓的恢复模式下,我们可以刷入新的安卓系统,或者 ...
- 现在创业做App,先做 Android 还是 iOS?
随着互联网+的高速发展,现在创业大部分都是在布局移动端,初期往往摆在面前最大的难题是,如何分配有限的成本,在最快的速度内占领市场?这个大难题会影响创始人在团队和产品建设方方面面的决定.缩小至移动App ...
随机推荐
- jade 详解
简介 jade 是HTMl模板引擎,用javascript编写,可以在Node.js中使用.本文主要介绍原生node操作jade文件的方法. 安装 npm install jade 方法(API) ...
- 机器学习技法:12 Neural Network
Roadmap Motivation Neural Network Hypothesis Neural Network Learning Optimization and Regularization ...
- bzoj 4830: [Hnoi2017]抛硬币
Description 小A和小B是一对好朋友,他们经常一起愉快的玩耍.最近小B沉迷于**师手游,天天刷本,根本无心搞学习.但是 已经入坑了几个月,却一次都没有抽到SSR,让他非常怀疑人生.勤勉的小A ...
- [HNOI 2015]开店
Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...
- [HNOI 2016]网络
Description 一个简单的网络系统可以被描述成一棵无根树.每个节点为一个服务器.连接服务器与服务器的数据线则看做 一条树边.两个服务器进行数据的交互时,数据会经过连接这两个服务器的路径上的所有 ...
- [测试题]gene
Description Input Output Sample Input 3A+00A+A+ 00B+D+A- B-C+00C+ Sample Output bounded Hint 题解 //It ...
- STL rope
rope的部分简单操作 函数 功能 push_back(x) 在末尾添加x insert(pos,x) 在pos插入x erase(pos,x) 从pos开始删除x个 replace(pos,x) 从 ...
- ●POJ 3608 Bridge Across Islands
题链: http://poj.org/problem?id=3608 题解: 计算几何,求两个凸包间的最小距离,旋转卡壳 两个凸包间的距离,无非下面三种情况: 所以可以基于旋转卡壳的思想,去求最小距离 ...
- 【hihoCoder 1419】重复旋律4
Description 小 Hi 平时的一大兴趣爱好就是演奏钢琴. 我们知道一个音乐旋律被表示为长度为 N的数构成的数列. 小 Hi 在练习过很多曲子以后发现很多作品中的旋律有重复的部分. 我们把一段 ...
- 【Uva 10269 马里奥与公主的归途】
·马里奥n次解救了公主,现在需要从魔王的宫殿返回. ·英文题,述大意: 给定一个点数不超过100的无向图,其中的点分为两类:乡村和城堡. 输入A个乡村,B个城堡(乡村编号1~A,城堡编号A+ ...