App安全

前言

近几年来随着网络信息技术日新月异的发展,移动互联网的生态系统日益庞大,安卓手机的用户量在逐年增高,自主研发、运营的移动App数量也越来越多。但是在移动App为人们提供便捷服务的同时,隐私数据泄露、核心代码窃取、程序逻辑破解、应用二次打包、API接口暴露等移动App安全问题也一直环绕着我们。倘若公司或开发者的安全防护意识不够,对代码中的安全问题不能够及时发现和处理,一旦出现问题,无论是从公司团队自身还是用户的角度,都将受到不可估量的伤害。

背景

什么是安全问题?
从攻击者的角度看:只要能够从App的数据或代码中找到一些明显的字段或逻辑,直接或间接地拦截、修改已达到一定的目的,都属于安全问题。

2019和2020年是玩物得志高速发展的2年,随着公司的体量越来越大,用户数日益增多,App工程体积也逐步庞大。
对于电商类App来说,我们同样面临着一些安全问题,并不断困扰着我们,从业务角度来说,如大量黑产用户的羊毛党、虚假用户、批量刷单、群发广告、诈骗信息、刷回复率等;从开发角度来看,存在着数据传输不安全导致订单、用户数据泄露、被篡改,本地数据剽取导致用户隐私泄露、Apk反编译导致重要逻辑、算法被暴露等问题。
随着这些安全性问题的暴露,我们也逐渐提高了对数据、对代码安全的敬畏以及防护意识,并针对性地对这些问题进行了逐步的改善。

实践

一、Apk程序安全
早期的App工程比较简陋,只有基本的混淆和签名,工程几乎处于裸奔状态,极易被反编译,面临着终端漏洞、应用重打包、应用仿冒等威胁,当Apk被反编译后,部分关键代码逻辑和三方key可能泄漏,某些SDK未做包名校验等,导致SDK资源流失、部分关键业务代码泄漏导致应用被重打包实现一些商家功能等。

  • 应用基础安全配置
    1.AllowBackup漏洞:AndroidManifest.xml文件中的allowBackup被设置为true时,用户可通过adb backup来对应用数据进行备份,在无 root 的情况下可导出应用中存储的所有数据,造成用户数据的严重泄漏,这里改为false,不能对应用数据备份。
    2.Java层代码调试风险:应用级配置文件build.gradlel中的调试标记默认是开启,即可以被Java调试工具如JDB进行调试,但是这样可能会出现被获取和篡改用户敏感信息,甚至可以分析并且修改代码实现的业务逻辑,例如窃取用户密码、绕过验证码防护等。在正式发布应用之前,显示设置android:debuggable属性为false,关闭Java动态调试功能、不允许被调试。
    3.正式包日志控制:调试信息函数可能输出重要的调试信息,一些开发者习惯于在开发过程中将调试日志输出,却没有在调试完毕后即使删除日志,其中的Log日志可能导致一些用户信息、重要代码逻辑的泄漏,为攻击这提供便利。应用内Log日志统一封装控制基类,测试环境允许打印日志,正式环境关闭日志的输出。
    4.WebView常见问题:WebView组件默认开启了密码保存功能,会提示用户是否保存密码,这些数据会明文保存到应用数据目录的
    databases/webview.db中,攻击者可能通过root的方式访问该应用的WebView数据库,从而窃取本地明文存储的用户名和密码。我们需 显示设置WebView.getSettings().setSavePassword(false)关闭密码存储功能。
    5.启动签名校验,安全气囊:当Apk被二次打包时,因为原始的apk签名无法获取,反编译后的apk只能通过自己新生成的签名文件签名,所以,签名信息校验变得至关重要,我们可以在启动时做Apk签名校验,最用户进及时的提醒,包括应用连续闪退、强制升级等,可统一进入到安全气囊页面进行合理的下一步操作。
  • 混淆、签名、加固;
    1.混淆:开启代码混淆minifyEnabled true可将部分代码转为无规则的字母,增加攻击者反编译的成本,且打包时可移除无用资源,减少 APK 体积;资源混淆如AndResGuard针对资源文件可以将原本冗长的资源路径变短,例如将res/drawable/wechat变为r/d/a,且资源混淆后使用ApkTool工具是无法直接回编生成二次打包Apk的,也能够一定程度上减少Apk体积和增加反破解难度。
    2.签名:发布应用签名是必不可少的,签名一是保证消息来源的真实性,二是确保消息不会被第三方篡改,早期的V1签名会将Apk内文件进行逐一签名并将签名结果保存在Apk的META-INF目录下,但是V1签名一是签名校验速度较慢,在Apk安装校验过程中需要对apk中所有文件进行摘要计算,在 APK 资源很多、性能较差的机器上签名校验会花费较长时间,导致安装速度慢,影响用户体验,且META-INF目录用来存放签名,自然此目录本身是不计入签名校验过程的,可以随意在这个目录中添加文件。因此我们升级到V2版本签名,V2 签名将验证归档中的所有字节,而不是单个 ZIP 条目,因此在签署后无法再运行 ZIP重压等,能够及时发现对 APK 的受保护部分进行的所有更改,从而有助于加快验证速度并增强完整性保证。
    3.加固:只混淆和签名后的Apk,对于攻击者来说仍是处于裸奔状态,应用极易被反编译,得到明文资源文件和dex代码文件,且不论是java代码生成的dex文件或C/C++代码生成的so文件,反编译成本仍不是特别高,常规Android程序它的所有代码都在dex文件中,程序启动时要先把这个dex文件载入到内存中,加壳可以把原dex文件加密或者隐藏起来,放一个新的壳dex到Apk中,程序启动时运行这个壳dex,然后这个壳dex在运行时再加载原dex,使得攻击着无法直接得到dex文件进行代码查看,我们引入第三方加固平台对签名后的Apk进行加固,从而进一步保证dex、so、资源文件、数据文件等重要的Apk文件信息的安全性。
  • 组件暴露,路由收拢;
    1.公共组件暴露风险:Activity、Service、Provider、Receiver四大组件若配置为android:exported = "true",将可以被外部应用调用,这样存在安全隐患的风险,可能导致数据泄露和恶意的Dos攻击,引起应用连续崩溃等。因此我们最小化组件暴露,尤其对于第三方页面拉起App,完善原有路由框架,进行统一的入口收拢和安全校验等措施。
    2.路由框架完善:在之前的应用工程中,每个页面的路由控制不够完善,外部可以随意拉起任何一个App页面,如上所述存在数据不完整、组件攻击等安全性问题。我们通过路由标准化,收拢App路由到一个统一入口,将后续链路转为应用内跳转,且通过白名单、入口来源、开关控制、路由降级等多重安全控制,形成一条安全调用链路保证路由安全调转。

二、数据通信安全
作为电商类应用,数据安全极为重要,早期的Http协议是明文传输,攻击者可以轻易的通过Charles或Fiddler等抓包工具来拦截和篡改请求数据,虽然Android9以后谷歌官方限制了默认不允许非加密的链接来改进对设备和数据安全的保护,但是Https链接就真的安全么?当设备添加了受信任的中间人证书以后,仍然可以对接口数据进行抓取,存在着接口被篡改和重复调用的风险,进而导致用户订单数据、隐私数据泄漏,接口被频刷导致拒绝服务、资源损失,以及一些新用户优质资源被“薅羊毛”等问题的存在。

  • 昆仑网关标准化
    对于接口数据的处理增加前置的网关层预处理,统一封装接口的标准化请求头、通用入参/出参、请求签名、参数加解密、拦截器处理等一系列功能,减少客户端开发时不必要的模板代码同时,增强工程的稳定性和数据安全性。
    为了保证数据通信对于双端的安全性,我们主要关心:
    1.传输内容可能被窃听?
    2.无法验证双方的身份?
    3.传输内容可能被篡改?
    Https并不能直接阻挡攻击者分析请求接口并发起恶意请求攻击,为了增加攻击者分析请求的难度,通常采用对请求数据加签和加密2种手段:
    使用签名:对请求数据、请求头参数进行一定规则的拼接和运算并进行结果签名,生成唯一的结果sign作为校验字段,服务端接收到请求后使用同样的签名规则进行唯一性校验,至于签名规则可自行定义,这里签名规则相当于秘钥的作用,因此可以将签名逻辑Native化,或加一些花指令来提高破解难度。
    数据加密:Post到服务器和从服务器接收到的数据都进行加密后传输,至于加密类型有多种手段,一般使用对称加密和非对称加密的混合方式进行,另外可以由后端控制秘钥过期时间来增加秘钥的动态性以提高安全等级。

这里对接口请求进行一个简单的安全等级排序:
L1:Http请求,抓包简单
L2:Https请求,无签名,可以修改参数
L3:Https请求,有参数签名校验,可以查看请求,无法修改
L4:Https请求,签名校验,响应加密,无法修改请求和查看内容
L5:无法抓取请求

  • IM数据加密
    即时通讯这块我们使用的是某云厂商服务,起初IM消息这块是直接使用SDK实现即时通讯功能,对于电商交易类应用,容易出现程序被破解进而导致的刷IM回复率、批量群发广告、发送风险图片、二维码引流、诈骗信息、敏感词等一些列IM相关风控问题。
    1.登录设备指纹:对IM登录设备指纹上传,进行设备、用户未读的黑名单管理;
    2.消息数据加密:对所有涉及IM通讯消息进行加解密处理、前置校验,增加黑产用户的mock消息成本;
    3.消息前置风控:图片、语音类、文本类消息接入前置风控校验,预拦截风险消息或敏感词等,达到阈值自动拉黑对应用户并预警通知;
    4.自动拉黑、风险提示、官方标签等:设备黑库管理,对风险用户或设备给对方风险提示以及官方账号的打标等,从一定程度上减少诈骗类信息的泛滥。
    三、本地数据保护
    一般我们会将一些常用信息保存在客户端本地,包括用户登录信息、缓存信息、本地Log日志等,其中不免会包含一些隐私数据或重要的程序运行信息,当应用被破解或设备处于root环境下,本地沙盒数据也将完全暴露出来,同样存在数据泄露的风险。
    因此,本地数据同样需要加密后存储,如Sqlite数据库、SP文件、程序运行日志、assets资源文件等,加密过程可根据数据重要程度进行对称加密或非对称加密等,建议同样需要将加密代码Native化,相对于Java代码容易被反编译,C++代码编译后生成的库是一个二进制文件,能够阻挡一些逆向者,且Native层也需要对调用者应用进行包名、签名等安全性校验等确保处于相对安全的环境。
    四、安全&风控建设
    电商应用使用流程中,很多环节可能被黑产用户通过一些自动化工具完成,如批量注册账号、刷单等,强占商家优质资源,损害商家和用户的双向利益,服务端又难以辨别请求者属于真实用户还是虚假身份。
    一般我们通过自研和接入第三方风控服务来逐步完善我们的风控机制,如数据统一加解密、设备指纹上报、业务/接口/用户/设备等多维度的接口风控来杜绝一些风险设备、虚假设备的鉴别、过滤。

总结

随着客户端、服务端以及安全风控部门的共同努力下,我们对以上几个方面遇到的安全问题进行了一定的完善。但是移动安全是个长久的话题,安全是相对的,混淆、加壳、加密等手段只是增加了应用破解的难度,攻与防之间的博弈也只是时间和成本的问题,不过我们总不能让对方轻松取胜吧?提高自我的安全意识,做好基本的安全策略,让应用更安全更可靠的运行是开发者义不容辞的责任,我们也后续也会持久的关注安全问题,不断提高玩物得志代码和应用的安全质量。

你可能感兴趣的:(App安全)