App安全性保障方案

App安全性保障方案

之前晋级考核的时候认识到自己在app安全领域存在薄弱环节,所以这段时间研究了Android应用的安全防护,结合公司的项目特点做出记录和总结;主要包扩两个方面:http接口安全和App防逆向破解

一 Http接口安全

Http接口安全是为了保证客户端与服务端进行数据交互时的数据安全,防止数据库被爬取及数据被篡改

动态token

token是一个与用户身份关联的字符串,作为客户端与服务端交互时的令牌,可用于唯一确定客户端用户身份.
基于token的身份验证方法一般是这样的:

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
  4. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

token一般有个有效期,当token失效时,客户端会使用获取token的接口来更新token,这样能够保证客户端的token有效且是动态的,第三方截取到旧的token也无法发起有效请求,并且刷新token这个过程对于用户是无感知的.

签名验证

如果不对请求做签名验证,则可以简单通过抓包工具拿到请求参数,篡改后提交且大规模调用,导致我们的核心业务数据被篡改且系统资源被大量消耗.签名验证也是第三方开放平台确保接口安全的标准做法:

  • 微信支付


    微信支付开放平台签名算法
  • 支付宝支付


    支付宝支付签名算法
  • 春雨医生开放平台


    春雨开放平台签名

签名算法:signStr=signature(params&timetamp&SIGN_KEY)

  1. 客户端和服务端均保存有相同的key
  2. 客户端发起请求前,参数+时间戳+key使用签名算法生成签名
  3. 客户端发起请求,携带 参数+时间戳+signStr
  4. 服务端验证时间戳是否在有效期内(如2分钟)
  5. 根据同样的签名算法计算签名并验证是否一致

签名验证解决的问题:

  • 避免请求数据被篡改;如果第三方修改其中任意参数,则会导致签名不同,后台验证后则返回调用失败
  • 防止重放攻击,避免数据库被爬取;因为有时间戳验证,同一个签名在短时间内就会无效,服务器直接拦截掉了过期的请求

注意点:

  • key的安全性很重要,建议采用服务端动态下发的方式(下发如何确保安全,也需要更多考虑),且事先约定好key的更新机制(用于key泄露时,服务端重新下发)
  • 客户端时间戳的准确性如何确保?建议每次客户端打开时计算与服务端的差值,用于确保时间戳的一致性

对返回结果加密传输

因为我们的请求会被抓包工具轻易的抓取,所以请求返回的数据需要加密处理

  1. 客户端正常发起请求
  2. 服务端进行业务处理,对response加密
  3. 客户端对response解密

解决的问题:

  • 对Response加密之后,即使第三方抓取到了我们的数据,也无法解密,保证了我们的数据安全

注意:

  • 这里需要使用可逆算法,因为需要加解密,如AES、RSA等;
  • 同样我们的秘钥也需要确保安全,且事先约定好更新机制,用于应对秘钥泄露;
  • 可以对整个response加密,也可以只对真正的业务数据部分加密,可以跟业务特点自行选择;
  • 因为存在一些接口是不能加密的(如初始化接口等),可以增加一个是否加密字段,便于客户端兼容所有接口

在Android下,可以这样简单实现

数据结构

public class CodeEntity {
    public int code;
    public String msg;
    //业务数据
    public Object data;
    //当前是否需要解密,默认不需要
    public boolean encrypted = false;
}

CommonGsonResponseBodyConverter对返回结果解密

public class CommonGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
    ...

    @Override
    public T convert(ResponseBody value) throws IOException {
        String response = value.string();
        ...

        /**
         * AES解密
         * 如果该接口有加密,则先解密
         */
        if (entity.encrypted) {
            response =  AES.getInstance().decode("key",response);
        }

        ...

    }
}

AES加解密:

public class AES implements IDecode, IEncode {
    private static final String CBC_PKCS5_PADDING = "AES/ECB/PKCS5Padding";//AES是加密方式 CBC是工作模式 PKCS5Padding是填充模式
    private static final String AES = "AES";//AES 加密
    private static final Charset CHARSET = Charset.forName("UTF-8");

    private volatile static AES sAES = null;

    private AES() {
    }

    public static AES getInstance() {
        if (sAES == null)
            synchronized (AES.class) {
                if (sAES == null)
                    sAES = new AES();
            }
        return sAES;
    }

    @Override
    public String encode(String key, String plaintext) {
        try {
            byte[] raw = key.getBytes(CHARSET);
            SecretKeySpec spec = new SecretKeySpec(raw, AES);
            Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
            cipher.init(Cipher.DECRYPT_MODE, spec);
            //解密
            byte[] input = cipher.doFinal(plaintext.getBytes(CHARSET));
            //Base64转码
            return Base64.encodeToString(input, Base64.DEFAULT);
        } catch (Exception e) {
            Logger.e("加密出错,直接返回明文==>明文为:" + plaintext);
            return plaintext;
        }
    }

    @Override
    public String decode(String key, String ciphetext) {
        try {
            byte[] raw = key.getBytes(CHARSET);
            SecretKeySpec spec = new SecretKeySpec(raw, AES);
            Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
            cipher.init(Cipher.DECRYPT_MODE, spec);
            //将密文先使用Base64解码
            byte[] decoder = Base64.decode(ciphetext.getBytes(CHARSET), Base64.DEFAULT);
            //解密
            byte[] bytes = cipher.doFinal(decoder);
            return new String(bytes, CHARSET);
        } catch (Exception e) {
            Logger.e("解密出错,认为无需解密==>密文为:" + ciphetext);
            return ciphetext;
        }
    }

}

二 App防逆向破解

逆向工程一直都是App安全的潜在威胁,如果App没有采取防逆向措施,则你的App能够被轻易破解,包括源代码,资源及本地数据;App逆向工程和反逆向一直都是与时俱进的,这里总结App开发中常用的反逆向方案,往往都是多重方案结合起来使用,以确保App安全

代码混淆

这是最基本的防护方法,也是使用最广泛最成熟的,一般App都会使用;开启混淆后,源代码中的类名、方法名、变量名会变成随机字母,使代码难以阅读但却不影响正常运行,这样App被反编译后代码逻辑也不会暴露,另外混淆还有减少包体积的作用

资源混淆

代码混淆只混淆了我们的代码,apk中的资源在解压之后一目了然,这样导致布局、样式、图片资源等完全处于暴露状态,第三方通过简单的操作就可以拿到,所以资源文件也采取混淆操作.

推荐使用腾讯开源的AndResGuard项目进行资源压缩,不仅可以最大程度保护我们的资源安全,且可以减小APK体积

加固加壳

加固

我们在加固的过程中需要三个对象:
1、需要加密的Apk(源Apk)
2、壳程序Apk(负责解密Apk工作)
3、加密工具(将源Apk进行加密和壳Dex合并成新的Dex)

主要步骤:
我们拿到需要加密的Apk和自己的壳程序Apk,然后用加密算法对源Apk进行加密在将壳Apk进行合并得到新的Dex文件,最后替换壳程序中的dex文件即可,得到新的Apk,那么这个新的Apk我们也叫作脱壳程序Apk.他已经不是一个完整意义上的Apk程序了,他的主要工作是:负责解密源Apk.然后加载Apk,让其正常运行起来。

Apk加固之后是比较安全的(相对),现在市场是有很多加固平台,可自行选择使用

本地数据安全

App有一些数据是存在设备中的,包括缓存、配置、数据库等,如果这些数据不加密,则会存在泄漏的风险,所以对于本地数据根据数据的重要性来决定是否需要加密存储

  1. 文件尽量存放在Internal Storage而不是External Storage,该目录下其他用户无法查看(root可查看)
  2. 数据库采用加密方案,例如Reaml数据可以便捷的加解密,基于Sqlite的数据库也有很多开源加密方案,可自行选择
  3. 采用通用加密方案,对sp、file、数据库均支持加解密,例如FaceBook的开源项目concel
  4. 秘钥尽量不要在本地保存(更不要直接写在代码中),如果一定要保存,需要多层加密,拆分保存

才疏学浅,还请大家及时指出博客中的问题,不慎感激

关于作者

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266