对于一款成熟的App,一定会开始涉及安全和性能的问题,这里就先说一下安全性的问题,今天突然收到我们线上某app的安全性检测报告,一看一千多个安全隐患感觉这app完全无安全性可言,仔细一看其实都是我们开发过程的一个规范问题,虽然这款app不是我开发的,但是有很多安全隐患我也的确是没有考虑过,这里就不得不赶紧拓展补充能量了。
ProGuard
在Android Studio当中混淆APK,借助SDK中自带的Proguard工具,只需要修改build.gradle中的一行配置即可。这个就很简单了,基本都在用混淆。之前有些过基本混淆规。
Log
开发过程中相信大家都会打印一下log查看信息的,特别是接口数据类的日志,这些日志同样会泄露app中的信息,因此release版本的日志屏蔽是非常重要的。使用原生的Log大家可以自行封装一个工具类,在打印日志之前判断是否是debug,debug模式下打印日志。现在log框架也有很多,都有这样的配置。debug模式判断:
if(BuildConfig.DEBUG){
//is debug
}else{
//is release
}
组件安全(exported)
Android中的四大组件 Activity,Service,Provider,Receiver 四大组件中都会一个共同的属性:android:exported。而组件安全主要在于android:exported这个属性,android:exported决定组件是否允许被其他APP调用。
- android:exported 当Android sdk 的最小版本为16或者更低时或组件设置了intent-filter他的默认值是true。如果是17和以上的版本默认值是false。
- 组件访问权限:如果需要被其他app唤起建议加入自定义的权限,只有有权限才能调用。
- intent传输:跳转使用显示跳转,隐式跳转可能存在被未知的第三方应用劫持的风险
自定义权限方法如下:
//定义权限
<permission android:name="com.yz.permission.INTENT_OTHER"
android:protectionLevel="normal" />
//申请权限
<uses-permission android:name="com.yz.permission.INTENT_OTHER"/>
<activity
android:name=".TestActivity"
android:exported="true"
android:permission="com.yz.permission.INTENT_OTHER" />
//指定权限
注:四大组件自定义权限方法相同,protectionLevel属性是指权限的类型。官网给出的说明如下:
- normal:这是最低风险的权限,如果应用声明了此权限,也不会提示安装应用的用户授权(例如,如果声明了定位权限,则应用到定位功能时,会明确提示用户,是否授予定位权限,但是protectionLevel为normal的不会明确提示,直接默认授予),系统直接默认该应用有此权限
- dangerous:这种级别的权限风险更高,拥有此权限可能会访问用户私人数据或者控制设备,给用户带来负面影响,这种类型的权限或许不会默认授权
- signature: 这种权限级别,只有当发请求的应用和接收此请求的应用使用同一签名文件,才会授权,并且是默认授权,不会提示用户
- signatureOrSystem:这种权限应该尽量避免使用,偏向系统级,签名保护级别应该足够对于大多数需求和工作
调试安全
release版本应该禁止调试,application的android:debuggable属性设置为true时,攻击者可以通过动态调试来窥探客户端的数据流和工作流,增加核心程序逻辑被破解的风险。
- Android提供了isDebuggerConnected()方法,判断是否有调试器连接
- debuggable属性判断,release版本debuggable设置为false,在程序中判断debuggable值是否被修改
判断debuggable代码如下:
if (0!=(getApplicationInfo().flags&= ApplicationInfo.FLAG_DEBUGGABLE)) { //程序被修改为可调试状态!!! android.os.Process.killProcess(android.os.Process.myPid()); }
签名安全
签名是Andriod软件一种有效的身份标识,签名时推荐V1和v2全部勾选,签名说明参考《AS打包签名》,防止二次打包,判断签名是否一致。 获取apk的签名hash值与apk的签名文件的hash值进行判断,不一致说明apk被二次打包。 获取签名hash值的代码如下:
public int getSignature(String packageName) {
PackageManager pm = this.getPackageManager();
PackageInfo pi = null;
int sig = 0;
try {
pi = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
Signature[] s = pi.signatures;
sig = s[0].hashCode();
} catch (Exception e1) {
sig = 0;
e1.printStackTrace();
}
return sig;
}