背景
随着移动互联网的发展,移动应用的安全问题越来越突显,特别是涉及到钱相关的产品,前段一段时间,我们的Android客户端产品被人破解了,修改了一些代码,重新打包签名后,就可以免费获得资源,对我们的收入造成了一些影响,移动产品安全性就不得不提重视,安全性这个话题很大,包括客户端、服务端、数据存储、协议等很多方面,这里只是从客户端的角度来讨论一上如何保证客户端产品的安全性,抛砖引玉,也希望大家多提意见和建议。
下面主要从以下几个方面来展开讨论:
C/S协议安全
在不使用https的前提下,要保证C/S协议的安全,一般都会进行参数的校验,以及参数加密,客户端和服务端会约定一个固定的字符串作为key,对于客户端来说,这个key应该放到哪里?最早之前,我们是直接放到Java代码中,这样可以说没有什么安全性,后来为了稍微更加安全一点,把这些key都统一放到so库中实现,虽然也不能保证绝对安全,但起码可以增加破解的难度。
你肯定要说,如果这样做,就会有一个问题,如果别人把so拿出来,在直接调用这些native接口,也同样可以获得key,所以也同样不安全,怎么办呢?
能不能让 so 库只能在我们自己的app运行,别人调用就是砖头呢?
各位看官,接着往下看。
so库校验签名
一般的情况下,都会在 Application.onCreate()
方法里面检查当前应用的签名是否合法,如果不合法就直接退出,这种情况其实无法正在防止破解,因为破解的可以找到调用入口,把相应的代码删除,所以这样方法也就失效了,那有没有更好的方案呢?
想到的一种思路就是,so库本身就具体签名校验的机制,当so库被加载时 (JNI_OnLoad()
方法),如果签名不合法,直接失败,so库根本加载不起来。
大概的思路如下代码所示:
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
LOGI("Library JNI_OnLoad begin =========");
if (checkSignature(env) != JNI_TRUE) {
LOGE(" The app signature is NOT correct, please check the apk signture. ");
LOGI("Library JNI_OnLoad end ===========");
return -1;
} else {
LOGI(" The app signature is correct.");
}
LOGI("Library JNI_OnLoad end ===========");
return JNI_VERSION_1_6;
}
复制代码
说明:这里有一个问题,需要注意,在开发过程中,我们不需要检查签名的合法性,只有在release版本才检查,所以上述逻辑还再完善,需要添加上DEBUG和RELASE的判断。
要怎么判断呢?我目前的思路是通过宏来判断,如果定义了宏并且为JNI_TRUE
的话,就认为是release版本。
以下是完整的实现:
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
LOGI("Library JNI_OnLoad begin =========");
// RELEASE_MODE这个宏是通过编译脚本设定的,如果是release模式,
// 则RELEASE_MODE=1,否则为0或者未定义
#ifdef RELEASE_MODE
if (RELEASE_MODE == 1) {
// 检查当前应用的签名是否一致,如果不签名不一致的话,则直接退出
if (checkSignature(env) != JNI_TRUE) {
LOGE(" The app signature is NOT correct, please check the apk signture. ");
LOGI("Library JNI_OnLoad end ===========");
return -1;
} else {
LOGI(" The app signature is correct.");
}
}