Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

App安全(一) Andoid防止升级过程被劫持和换包

$
0
0

文/ Tamic
地址/ http://blog.csdn.net/sk719887916?viewmode=list

前言

APP 安全一直是开发者头痛的事情,越来越多的安全漏洞,使得开发者

越来越重视app安全,目前app安全主要有由以下几部分

APP组件安全

Android 包括四大组件:Activitie、Service、Content Provider、Broadband Receiver,
它们每一个都可以通过外面隐式的Intent方式打开,
android组件对外开放 就会被其他程序劫持,因此必须在manifest里面声明
exported为false,禁止其他程序访问改组件,
对于要和外部交互的组件,应当添加一下访问权限的控制, 在有权限后外部程序才可以开启,
还需要要对传递的数据进行安全的校验。不符合规则的一律不处理。

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,直到被你错误的安装在
    手机上。

    • 安装过程;

    加入你的以上两个过程都做了加固,但是安装时候,本地文件path被错误修改了,仍然可以安装错误apk,这不仅
    会对用户体验产生不利,甚至威胁手机安全。

解决办法:

  • 升级api加入https 这个肯定不用再过多介绍
  • 下载Api也加入https不用介绍,这里指的提出的是你需要服务端吧的文件进行文件Hash值校验,防止文件被篡改,
    通过对文件hash值,还要对服务端的校验验签,防止不是自己服务器返回的文件

  • 安装过程也必须对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就行")) {

          // r如果符合,就不去下载

        }

接着我们发现是你自己app的服务器地址,请求下载Api用DownLoadModel接受请求头 ,下载好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 LIUYONGKUI726 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注入下期接着说;

作者:sk719887916 发表于2016/8/17 17:46:21 原文链接
阅读:26 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>