开发APP时经常有问到:“APP的安全怎么保障,应用程序被PJ了怎么办?手机被人捡去了怎么办?” 特别在号称“安全第一,风控牛逼”的银行系统内,移动产品安全性仍被持有怀疑态度。那我们来总结下APP安全的方向和具体知识。
1.应用程序安全
2.应用程序内敏感数据存储安全
3.应用程序前后台数据报文传输安全
dex2jar:将dex文件转换成jar文件。
jd-gui:将jar文件转换成java代码 ,使用jd-gui工具打开classes-dex2jar.jar这个文件就能查看Java代码。
APK文件中资源文件都是在打包的时候被编译过了,我们直接打开的话是看不到明文的。想将APK文件中的资源反编译出来,又要用到apktool工具。
apktool:用于最大幅度地还原APK文件中的9-patch图片、布局、字符串等等一系列的资源。没有反编译资源之前,AndroidManifest.xml和activity_main.xml这样的资源文件都是非明文的,无法阅读。
smali文件夹的目录结构和我们源码中src的目录结构是几乎一样的,主要的区别就是所有的java文件都变成了smali文件。smali文件其实也是真正的源代码,只不过它的语法和java完全不同,它有点类似于汇编的语法,是Android虚拟机所使用的寄存器语言。 使用smali语法,修改代码,就能重新编译自己的apk。
那么如果这是别人的程序的话,我们从哪儿能拿到它原来的签名文件呢?很显然,这是根本没有办法拿到的,因此我们只能拿自己的签名文件来对这个APK文件重新进行签名,但同时也表明我们重新打包出来的软件就是个十足的盗版软件。
(1)令 APK 难以被逆向工程,增加反编译的成本。
(2)打包时移除无用资源,减少 APK 体积。
android {buildTypes {release {minifyEnabled trueshrinkResources trueproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}
}
(1)枚举
枚举类内部存在 values 方法,混淆后该方法会被重新命名,并抛出NoSuchMethodException。Android系统默认的混淆规则中已经添加了对于枚举类的处理,我们无需再去做额外工作。
(2)被反射的元素
代码混淆过程中,被反射使用的元素会被重命名,然而反射依旧是按照先前的名称去寻找元素,所以会经常发生NoSuchMethodException 和 NoSuchFiledException 问题。
(3)实体类
实体类即我们常说的"数据类",当然经常伴随着序列化与反序列化操作。很多人也应该都想到了,混淆是将原本有特定含义的"元素"转变为无意义的名称,所以,经过混淆的"洗礼"之后,序列化之后的 value 对应的 key 已然变为没有意义的字段,这肯定是我们不希望的。 反序列化的过程创建对象从根本上来说还是借助于反射,混淆之后 key 会被改变,所以也会违背我们预期的效果。
(4)四大组件
Android 中的四大组件同样不应该被混淆。原因在于: 四大组件使用前都需要在 AndroidManifest.xml文件中进行注册声明,然而混淆处理之后,四大组件的类名就会被篡改,实际使用的类与manifest中注册的类并不匹配,故而出错。其他应用程序访问组件时可能会用到类的包名加类名,如果经过混淆,可能会无法找到对应组件或者产生异常。
(5)JNI 调用的Java 方法
当 JNI 调用的 Java 方法被混淆后,方法名会变成无意义的名称,这就与 C++ 中原本的Java方法名不匹配,因而会无法找到所调用的方法。
(6)自定义控件不需要被混淆
(7)JavaScript 调用 Java 的方法不应混淆
(8)Java 的 native 方法不应该被混淆
(9)项目中引用的第三方库也不建议混淆
使用BroadcastReceiver组件需要动态注册或者静态注册,如果动态注册广播,即在代码中使用registerReceiver()方法注册BroadcastReceiver,只有当registerReceiver()的代码执行到了才进行注册,取消时则调用unregisterReceiver()方法。但registerReceiver()方法注册的BroadcastReceiver是全局的并且默认可导出的,如果没有限制访问权限,可以被任意外部APP访问,向其传递Intent来执行特定的功能。因此,动态注册的BroadcastReceive可能会导致拒绝服务攻击、APP数据泄漏或是越权调用等安全风险
解决方案:
1.在 AndroidManifest.xml 文件中使用静态注册 BroadcastReceiver,同时设置 exported=“false”,不被外部应用调用。
2.必须动态注册 BroadcastReceiver时,使用registerReceiver(BroadcastReceiver,IntentFilter,broadcastPermission,android.os.Handle)函数注册。
3.Android8.0新特性想要支持静态广播、需要添加intent.setComponent(new ComponentName()),详情可以自行查阅
Activity、Service、Provider、Receiver四大组件若配置为android:exported=”true”,将可以被外部应用调用,这样存在安全隐患的风险。
解决方案:
在应用的AndroidManifest.xml文件中,设置组件的android:exported=”false”或者通过设置自定义权限来限制对这些组件的访问。值得一提的是,若部分功能使用前提是配置必须使用exported为true,这种情况开发者应该根据实际情况来进行集成
Android 2.1以上的系统可以为APP提供应用程序数据的备份和恢复功能,该功能由AndroidMainfest.xml文件中的allowBackup 属性值控制,其默认值为true。当该属性没有显式设置为false时,攻击者可通过adb backup和adb restore对APP的应用数据进行备份和恢复,从而可能获取明文存储的用户敏感信息,如用户的密码、证件号、手机号、交易密码、身份令牌、服务器通信记录等。利用此类信息攻击者可伪造用户身份,盗取用户账户资产,或者直接对服务器发起攻击。
解决方案:
将AndroidMainfest.xml文件中的allowBackup属性值设置为false来关闭应用程序的备份和恢复功能;也可以使用专业安全加固方案的本地数据保护功能,避免本地数据泄露。
截屏攻击是指在APP运行过程中,界面被监控并且截屏或者录屏。截屏攻击主要发生在APP登录、身份认证、资金操作等界面。Android5.0中增加了一个MediaProjection接口,它提供截屏或者录屏的服务,准许APP拥有截取屏幕或者记录系统音频的能力。同时系统允许其他消息窗口覆盖在系统的录制提示上,从而在用户无感知的情况下启动录屏或者截屏工具。通过此方式,攻击者可以获取APP关键界面的截图或者录像,从而获取用户的敏感信息。
解决方案:
在Activity的oncreate()方法中调用:getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);或者getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);来达到防止截屏攻击的目的。
WebView组件默认开启了密码保存功能,会提示用户是否保存密码,当用户选择保存在WebView中输入的用户名和密码,则会被明文保存到应用数据目录的databases/webview.db中。攻击者可能通过root的方式访问该应用的WebView数据库,从而窃取本地明文存储的用户名和密码。
解决方案:
开发者调用 WebView.getSettings().setSavePassword(false),显示调用API设置为false,让WebView不存储密码。
Android API level 17以及之前的系统版本,由于程序没有正确限制使用addJavascriptInterface方法,远程攻击者可通过使用Java Reflection API利用该漏洞执行任意Java对象的方法。通过addJavascriptInterface给WebView加入一个JavaScript桥接接口,JavaScript通过调用这个接口可以直接与本地的Java接口进行交互。就有可能出现手机被安装木马程序、发送扣费短信、通信录或者短信被窃取,甚至手机被远程控制等安全问题。
解决方案:
如果一定要使用addJavascriptInterface接口,需使用以下方法:设置minSdkVersion值大于或等于17,使应用不能在4.2以下系统上运行,允许被JavaScript调用的方法必须以@JavascriptInterface进行注解声明。
根据CVE披露的WebView远程代码执行漏洞信息(CVE-2012-663、CVE-2014-7224),Android系统中存在一共三个有远程代码执行漏洞的隐藏接口。分别是位于android/webkit/webview中的“searchBoxJavaBridge”接口、android/webkit/AccessibilityInjector.java中的“accessibility”接口和“accessibilityTraversal”接口。调用此三个接口的APP在开启辅助功能选项中第三方服务的Android系统上将面临远程代码执行漏洞。
解决方案:
如果应用内使用了WebView组件,那么使用 WebView.removeJavascriptInterface(String name) API时,显示的移除searchBoxJavaBridge、accessibility、accessibilityTraversal这三个接口。
此类配置应当妥善存放,不要在类中硬编码敏感信息,可以使用JNI将敏感信息写到Native层。
首先不应当使用SharePreferences来存放敏感信息,sharedpreferces存储的xml文件数据可能被反编译拿到。存储一些配置信息时也要配置好访问权限,如私有的访问权限 MODE_PRIVATE(Activity.MODE_PRIVATE,//默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容),避免配置信息被篡改。
解决方案:
敏感信息是否明文存储,比如账号密码之类重要信息进行加密存储,加密主要有对称加密、非对称加密、摘要算法。
(1)对称加密AES/SM4
AES主要是用在数据本身的加密,即使传输过程中被截取了,也是加密过后的数据。但AES的弊端的是,客户端加密的话,密钥肯定是储存在app中,如果app被成功PJ了,数据也就被暴露了。所以只有app本身程序的安全也解决了,app才能相对安全。
(2)非对称加密RSA/SM2
因为RSA加密有个长度限制,这就导致了RSA加密不能用于所有的数据交互。但是可以用到一些短数据,比如用户个人信息之类的,在交易中,一次订单的数据也不是很大等。
(3)摘要算法
比如MD5、SHA-1、SM3等。所谓的不可逆加密就是,只能单向加密,不能反向解密。MD5把数据加密,最后得到固定长度的16进制编码。这个加密的作用一般是匹配验证,验证某个数据是否改变。比如密码,在向服务器存储密码,一般不会存储明文密码。安卓本地存储个标志也一般不会明文存储。
(4)JNI写入Native层
调试信息函数可能输出重要的调试信息,常见的就是Log日志类其中包含的信息可能会导致用户信息泄露,泄露核心代码逻辑等,为发起攻击提供便利,例如:Activity的组件名;通信交互的日志;跟踪的变量值等
解决方案:
应用内使用统一的Log控制基类,可以灵活的控制Log的输出打印。(测试环境允许打印日志、正式环境不打印);或者使用第三方的日志框架
如果大量应用会读取zip压缩包进行相关逻辑业务,常见的场景就是从服务器下载压缩包,进行资源、代码热更新。在解压zip包时,如果对文件名没有进行限制,通过在文件名加上 …/…/ 前缀的方式,可以将文件解压到任意路径。如果攻击者用远程劫持或者本地替换等方式将APP将要加载的正常zip包替换为带有路径前缀的恶意zip包,而APP又未对解压文件的文件名称进行处理,则可能会出现攻击者可以对应用资源、代码进行任意篡改、替换,从而实现远程代码劫持等安全问题。
解决方案:
1.对zip包进行解压操作时,在获取文件名后,添加过滤代码对文件名中可能包含的 …/ 进行过滤判断。
2.使用通信协议加密技术,对通信过程中的数据进行加密保护,保证数据不被篡改。
3.建议客户端与服务端使用加密通道进行数据交互,并对传输数据进行完整性校验,防止zip包被拦截替换。
应用内若存在明文存储的H5资源文件,则会泄露页面基本布局和一些重要的信息,如登录界面、支付界面等。攻击者可篡改H5资源文件,可能植入钓鱼页面或者恶意代码,导致用户账号、密码、支付密码等敏感信息泄露。更有甚者,通过H5代码暴露相关活动的业务逻辑,可能被黑产团队用来刷红包、薅羊毛等,造成经济损失。
解决方案:
可以使用第三方的专业安全加固方案,对应用中的H5文件进行加固保护。
so文件为APK中包含的动态链接库文件,Android利用NDK技术将C/C++语言实现的核心代码编译为So库文件供Java层调用。so文件被 PJ可能导致应用的核心功能代码和算法泄露。攻击者利用核心功能与算法可轻易抓取到客户端的敏感数据,并对其解密,导致用户的隐私泄露或直接财产损失。
解决方案:
对so文件进行安全加固、可以使用第三方的安全平台so加固方案。
以上是APP开发中需要注意的安全问题。不要等到项目投产时再去统一检测和检查APP安全问题。项目启动时,安全的事宜就要做到心里有数了。