- 对本书的赞誉
- 序一
- 序二
- 序三
- 前言
- 第一部分 安全架构
- 第 1 章 企业信息安全建设简介
- 第 2 章 金融行业的信息安全
- 第 3 章 安全规划
- 第 4 章 内控合规管理
- 第 5 章 安全团队建设
- 第 6 章 安全培训
- 第 7 章 外包安全管理
- 第 8 章 安全考核
- 第 9 章 安全认证
- 第 10 章 安全预算、总结与汇报
- 第二部分 安全技术实战
- 第 11 章 互联网应用安全
- 第 12 章 移动应用安全
- 第 13 章 企业内网安全
- 第 14 章 数据安全
- 第 15 章 业务安全
- 第 16 章 邮件安全
- 第 17 章 活动目录安全
- 第 18 章 安全热点解决方案
- 第 19 章 安全检测
- 第 20 章 安全运营
- 第 21 章 安全运营中心
- 第 22 章 安全资产管理和矩阵式监控
- 第 23 章 应急响应
- 第 24 章 安全趋势和安全从业者的未来
- 附录
12.3 APP 业务安全
除了一些在开发过程中的安全问题外,还有一些是跟业务相关,比如反编译、二次打包、钓鱼 APP 等各种问题,这里涉及一些混淆、加壳、签名验证、运行时环境检测等技术方案。除此之外,还有一些是在业务上想做更多安全控制,比如数据防泄露、防截屏、安全键盘等。
12.3.1 代码安全
相比 iOS 系统的封闭及严格的 APP 审核及签名控制,Android 系统的开源以及基于 Java 的特性,导致 APP 在 Android 上更容易被反编译。目前常用的一些反编译工具(比如 APKTool、dex2jar 等)能够毫不费劲地还原 Java 里的明文信息,Native 里的库信息也可以通过 objdump 或 IDA 获取。而心怀不轨的人可能会通过反编译后加入恶意的代码逻辑,重新打包一个 APK 文件去发布安装,也就是我们常说的“二次打包”问题。针对这些问题,常见的解决方案是代码混淆、加壳、反调试、签名验证等。
1.代码混淆
APP 的代码混淆包括 Java 代码的混淆以及一些资源文件的混淆。
Java 语言编写的代码本身就容易被反编译,Google 很早就意识到这一点,在 Android 2.3 的 SDK 中正式加入了 ProGuard 代码混淆工具,开发人员可以使用它对自己的代码进行保护。ProGuard 提供了压缩、混淆、优化代码以及反混淆栈跟踪的功能,网上资料较多,不再赘述。
资源文件的混淆,一些互联网公司也提供了一些方法供参考,比如微信提供的 AndResGuard( https://github.com/shwenzhang/AndResGuard )等。
2.加壳
Android 加壳分为 dex 加壳和对 native 编译(即 so 文件加壳),主流的加壳技术基本可以分为四代:
·整体 dex 加壳。对 classes.dex 文件进行整体加壳加密,存放在 APK 的资源中,运行时将加密后的 classes.dex 文件在内存中解密,并让 Dalvik 虚拟机动态加载执行。
·防调试防 Dump。整体 dex 在内存中解密,黑客通过内存 Dump 的方式即可拿到明文,所以出现了防调试、防内存 Dump 的技术。
·方法体抽离。相比前面整体加壳加密,第三代开始尝试只对 classes.dex 文件中的方法、函数进行抽取加密,在 Java 虚拟机执行具体某个方法时才将其动态解密,并以不连续的方式存放到内存中。后面慢慢发展成将 Java 代码内关键算法、业务逻辑等函数自动转化成 native 的 C++代码进行防护。
·VMP 加壳/so 加壳。我们知道程序的执行,是依靠 CPU 对于符合规范的指令集的解析处理。如果将原指令集通过自定义规范进行变形处理,生成新的指令集(称之为虚拟指令集),CPU 将无法识别虚拟指令。此时若配合能够解析虚拟指令集的解释器(称之为虚拟机),就可以达到不直接通过 CPU 而是通过虚拟机来执行虚拟指令。这就是很多公司各种 VMP 保护方案的基本原理。
对于使用 Android NDK 编写的 Native 代码,逆向它原本就有一定的困难,如果再添加外壳保护(比如 UPX)则更加困难了。
不同的加壳方案有着不同的效果,表 12-3 是某厂商网站上的风险描述表。
表 12-3 某官网提供的 APP 加固风险表
目前市面上有很多 APP 加固平台,像 360、百度、腾讯、网易盾、阿里聚安全等为免费的,像爱加密、梆梆、几维、顶象科技等为收费的。从事安全的读者肯定会关注网上各类破解文章,比如图 12-3 这个针对 360 加固的秒脱。
图 12-3 加固后的对抗
其实笔者想说的是,APP 加固的目的是提升对手的攻击成本,如果通过加固能让相当一部分人知难而退,就达到 APP 加固本身的效果了。
在实际工作中,考虑到 APP 内部复杂的业务场景和升级机制,有一些加固方案可能会存在一定的兼容性问题,用户体验要求高的场景可能还会追求运行速度,因此需要慎重权衡。一般的思路是在 APP 上进行各种埋点,结合后端大数据风控来降低安全风险。
3.反调试
调试器检测,一般是利用 android.os.Debug.isDebuggerConnected() 这个 API 来判断,还有一些其他的思路,比如调用 Android 中 flag 属性 ApplicationInfo.FLAG_DEBUGGABLE 判断是否属于 debug 模式,循环检查 android_server 调试端口和进程信息,循环检查自身 status 中的 TracePid 字段值等。
此外,还有检测模拟器,检测设备是否已经 ROOT。模拟器检测技术,一般是取一些模拟器特征,例如通过电话管理器攻取设备 IMEI、IMSI,判断设备配置信息与 Android 模拟器设备配置默认值是否相同,检测设备是否有安装蓝牙设备硬件,判断当前设备 WIFI MAC 地址,检测是否具有 QEMU 虚拟机通道文件等。
ROOT 检测一般的思路是,看 ROOT 后的手机会有哪些特征,比如,检测 su 文件是否存在及可以执行,检测是否安装 Superuser.apk 等。但实际情况是,Android 碎片化非常严重,国产手机厂商特别喜欢修改原生 ROM,这会导致一些检测方法失效,需要关注。
4.签名验证
签名验证主要是为了防止二次打包,Android 签名验证一般有三种方法:
·Java 层验证,即在 Java 代码中实现公钥信息的比对,比对的样本可以放在本地或者服务器侧。但是 Java 代码容易被反编译,这个校验逻辑可能被篡改。
·NDK 层验证,即在 Native 代码实现公钥信息的比对,比对的样本进行加密存储。我们知道 so 通过反汇编生成的 ARM 代码,相对 smali 被篡改的难度更大,再结合 so 文件加固,可以进一步增强反编译难度。
·服务端验证,即程序通过特定方法检测获取自身代码校验值,上送云端服务器进行比较,从而验证合法性。这样针对那些非法或伪造、篡改过的客户端,服务端可以直接拒绝服务,进一步保障安全。
12.3.2 数据安全
针对 APP,我们通常说的敏感信息包含两方面:一是用户敏感信息,比如用户名、密码、手机号、邮箱、身份证、银行卡、住址等;二是 APP 本身的一些敏感信息,包括产品核心算法、核心业务逻辑、私钥、本地存储的证书、加密算法等。这些敏感信息都需要在设计 APP 时考虑严格保护,在存储、使用、传输过程中也要考虑保护方法。
1.数据存储安全
Android 有外部存储和内部存储之分,外部存储安全隐患比较大,任何软件只需要在 AndroidManifest.xml 中声明如下一行权限就可以在外部存储设备上读写。
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
笔者建议涉及用户隐私哪怕是已经加密过的也不要保存到外部存储设备上,还有一些 APP 会动态加载一些外部资源,这些外部资源可能保存在外部存储上,建议在加载时验证文件完整性。在做代码审计的时候,关注 getExternalStorageState、getExternal-StorageDirectory 等关键函数就能定位到 APP 使用外部存储的代码逻辑。
内部存储是所有软件存放私有数据的地方,Android SDK 中提供了 openFileInput() 与 openFileOutput() 方法来读写程序的私有数据目录。openFileOutput() 方法的第二个参数指定了文件创建模式,如果使用了 MODE_WORLD_READABLE 或 MODE_WORLD_WRITEABLE,就可能导致敏感信息泄露。
除了 File 方式外,Android 还提供了 Shared Preference、SQLite、ContentProvider 方式进行数据存储。与 openFileOutput() 方法一样,Shared Preference 的 getSharedPreferences 方法打开文件时第二个参数如果设置为 MODE_WORLD_READABLE 或 MODE_WORLD_WRITEABLE 都存在一样的问题。
相应的对策就是在使用 openFileOutput() 和 getSharedPreferences() 打开文件时,将第二个参数设置为 MODE_PRIVATE,这样就可以利用 Linux 的文件权限机制来确保数据不被其他进程访问。
注意:
单单依靠 MODE_PRIVATE 模式是不够的,因为可能我们的 APP 运行在一个已经被 Root 的手机上,或者手机系统出现了一些漏洞导致进程可以提升权限,所以往往敏感数据会采取加密措施。
敏感数据需要保护,基本上都是加密处理,当然有一些场合如日志打印包括 logcat 不允许出现敏感信息,这是常识,无需多言。Android SDK 提供了一些 API 供加密使用,这些 API 和 JAVA 提供的基本相似,由 Java Cryptography Architecture(JCA,Java 加密体系结构)、Java Cryptography Extension(JCE,Java 加密扩展包)、Java Secure Sockets Extension(JSSE,Java 安全套接字扩展包)、Java Authentication and Authentication Service(JAAS,Java 鉴别与安全服务)组成。
JCA 提供基本的加密框架,如证书、数字签名、消息摘要和密钥对产生器等;JCE 扩展了 JCA,提供了各种加密算法、摘要算法、密钥管理等功能;JSSE 提供了基于 SSL(安全套接层)的加密功能,供 HTTPS 加密传输使用;JAAS 提供了在 Java 平台上进行用户身份鉴别的功能,除此外,Android 还提供了 android.security 和 android.security.keystore 来管理 keychain 和 keystore。
如果使用 SQLite 数据库的时候需要加密,可以使用 SQLCipher 方案。SQLCipher 是个独立的 SQLite 数据库实现,但它并没有自己实现一套加密算法,而是使用了 OpenSSL 的 libcrypto 库,兼容性更好。
关于加密算法的选择和使用,业界有如下安全建议:
·base64 只是一种编码方式,并不是加密算法。
·生成随机数时,不要使用 Random 类,使用 SecureRandom 类的时候,不要调用 setSeed 方法,即不要设置种子。
·使用 HASH 算法时,不要使用 MD2、MD4、MD5、SHA-1、RIPEMD 算法来加密用户密码等敏感信息,因为网上有大量的库可以用来破解,建议使用 SHA-256、SHA-3 算法。
·使用消息认证算法时,建议使用 HMAC-SHA256 算法,避免使用 CBC-MAC。
·使用对称加密算法时,不建议使用 DES,建议使用 AES 算法,同时需要注意加密模式要显式指定为 CBC 或 CFB 模式,不要使用默认的 ECB 模式。
·非对称算法使用 RSA 时,建议密钥长度不低于 512,同时注意重放攻击。
·使用基于口令的加密算法 PBE 时,生成密钥时要加盐,盐的取值最好来自 SecureRandom,并指定迭代次数。
关于密钥保护,直接明文写在配置里肯定是不行的;硬编码在 Java 代码中通过 dex 也容易逆向;将密钥放在 so 文件里也还是面临 IDA 破解。为了保护密钥,各种千奇百怪的方法都有了,比如将一部分写到文件中,一部分写在 Java 代码或 C 层代码,这只是在一定程度上增加了逆向难度而已。有的利用 so 将密钥再进行二次加密保护,那这一层的密钥保护又是一个问题。针对这些问题,不得不提一下白盒加密技术。白盒加密属于对称加密,通过将算法和密钥紧密捆绑在一起,由算法和密钥生成一个加密表和一个解密表,然后可以独立用查找加密表来加密,用解密表来解密,不再依赖于原来的加解密算法和密钥。正是由于算法和密钥的合并,所有可以有效隐藏密钥,与此同时也混淆了加密逻辑。
2.数据传输安全
一般客户端使用 HTTPS 与服务端进行通信,网站启用 SSL site wide(use HTTPS only)或 HSTS(HTTP Strict Transport Security),否则存在 SSL Strip(HTTPS 降级为 HTTP)攻击风险。由于前面在 12.2.6 节中讲过相关的问题及对策,这里就不再过多重复。对于敏感信息,建议除了 HTTPS 传输加密外,还需要在应用中进行加密保护,多一层保障。
12.3.3 其他话题
1.安全输入键盘
智能手机输入需要依靠虚拟键盘,而虚拟键盘则由具体使用的输入法控制。金融行业 APP,密码往往涉及金钱,为了确保输入安全,一些企业会自行开发安全输入键盘,图 12-4 为某行 APP 的安全键盘效果图。
这样当用户输入密码的时候,处于自行开发的密码键盘保护之下,确保用户密码安全。当然除了密码,还有其他的输入数据也需要保护,图 12-5 是某行 APP 的安全键盘在输入身份证时的效果图。
注意这上面的键盘数字布局,是随机的,每次都会变化。什么时候随机,什么时候不随机,需要在用户体验和安全之间进行权衡。
2.防截屏
为了防止恶意软件通过秘密截屏的方式得到一些 APP 上显示的信息,就需要对截屏进行处理。Android 系统没有提供对截屏事件监听的接口,也没有提供对应的广播,现在主流的思路是,基于截屏中所做的动作针对性地进行检测,比如,利用 FileObserver 监听某目录资源变化,利用 ContentObserver 监听全部资源变化,利用 Activity.onPause() 机制等。当发现有截屏时做一些处理,比如用纯黑色图片对象进行覆盖处理。
图 12-4 安全输入键盘例 1
图 12-5 安全输入键盘例 2
除此之外,Android 提供了一个对应的 API,直接禁用截屏功能,只需要获取到相应的 Window 对象,给其添加一个 FLAG_SECURE 的 flag 即可。注意这个 FLAG_SECURE 是应用在 Window 对象上的,如果 Activity 有一些弹窗或突出的 UI 元素可能不受它保护,需要再单独设置。
3.钓鱼 APP
钓鱼 APP 问题在早些年比较多,随着恶意 APP 越来越多,为规范并促进 Android APP 安全加固领域的健康发展,CNCERT 牵头制定了通信行业标准 2015-0217T-YD《移动互联网应用程序安全加固能力评估要求与测试方法》,这个标准对提供 Android APP 加固服务的系统提出了以下要求:
·禁止对恶意 APP 进行加固。
·要求对 APP 加固提交者进行身份验证。
·要求具备对加固前 APP 进行追查的能力。
通过对加固服务提供商进行规范来防止钓鱼 APP,是一个不错的思路。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论