编写进度
Table of Contents
- Android App安全基础
- 程序代码安全测试 - APP文件、程序进程
- 服务交互安全测试 - 程序进程、前段界面、接口端口
- 鉴权认证安全测试 - 接口端口、内存数据、网络通信
- 本地数据安全测试 - 内存数据、本地存储
- 网络传输安全测试 - 网络通信
- App加固技术
Android App安全基础
Android APP生成过程
graph LR
java[.java文件] -->|javac|cls[.class文件]
cls[.class文件] --> dx[dx]
jar[.jar文件] --> dx[dx]
dx[dx] --> dex[class.dex]
dex[class.dex] -->|dex2oat| oat[oat格式的class.dex文件]
dex[class.dex] -->aapt[aapt]
resource[resource] -->aapt[aapt]
aapt --> .apk文件
.apk文件 --> jarsigner
jarsigner--> zipalign
zipalign --> signsign[signed .apk文件]
- 通过
javac将java源代码生成字节码.class文件 - 通过
dx –dex –output=class.dex Test.class将.class文件和jar包生成Android App可执行文件class.dex
oat格式文件是android系统自带的ELF文件格式,包括classes.dex文件内容,及classes.dex文件转换的机器指令,存储在/data/dalbik-cache/arm/data@App@com.demo.test-1@test.apk@classes.dex - 通过
aapt将.dex文件和其他音视频资源文件打包成.apk文件 - 通过jarsigner对生成的apk文件进行数字签名,防止APP被篡改
- 通过zipalign使apk文件压缩部分在字节边界上使对齐
安全测试工具
静态分析工具 - 测试App是否存在防反编译和防篡改的问题
apktool 检测防篡改能力
反编译
java -jar apktool.jar d -f Test.apk
重打包
java -jar apktool.jar b -f directory_name -o Test.apk java -jar signapk.jar testkey.x509.pem testkey.pk8 old.apk new.apk
baksmali 作用 classes.dex -反编译-> smali
java -jar baksmali.jar classes.dex -o smalifile
smail 作用 smali –> classes.dex
java -jar smali.jar smalifile -o classes.dex
dex2jar 作用 dex –> jar
sh d2j-dex2jar.sh -f ~/path/to/apk_to_decompile.apk
[ ] jd-gui 作用 展示jar包中源码
[ ] jeb 用于逆向工程/审计apk文件的反编译工具
动态分析工具 - 测试防调试、防注入、防内存转储、漏洞测试等问题
- [ ] DDMS(Dalvik debug monitor service)是安卓开发环境中的Dalvik虚拟机调试监控服务
- [ ] gdb(GNU project debugger)是LInux系统的GCC调试工具
- [ ] IDA Pro 逆向神器 脱壳
- [ ] Drozer是一个进行综合安全评估的Android安全测试框架
数据包分析工具- 测试数据通信时存在明文传输、数据弱加密、中间人攻击漏洞
- [ ] Burpsuite/Fiddler
- [ ] Wireshark
挂钩工具 - 解决加密时上述工具无法使用的问题
- [ ] Xposed框架1 在不修改APK文件的情况下控制程序运行
- [ ] Frida 开源的跨平台挂钩框架,用来脱壳关键的函数,达到内存转储的目的
- [ ] inject App进程注入评测工具
App相关的信息资产
程序代码安全测试 - APP文件、程序进程
运行环境测试4
防反编译测试5
反编译工具检测
检测目的
检测App是否可以防止反编译工具,是否具有防逆向保护措施
检测方法
- 通过反编译工具对apk文件进行反编译,查看是否具有防逆向保护措施
- 通过IDA Pro等反汇编工具对动态库so文件进行反汇编,查看App是否具有防反汇编的能力
检测结论
若App的dex文件和so文件无法正常反编译或者App经过加固处理,则通过测试
修复建议
对App文件结构进行变形或加密,让反编译工具无法识别,或对App文件进行加固处理
代码混淆检测
检测目的
检测App反编译后的源码是否经过混淆处理
检测方法
通过反编译工具对apk文件进行反编译,查看代码中的类、字段和方法是否经过混淆处理
检测结论
若反编译后源码中的类、字段和方法使用a、b、c、d等无意义的字符重命名,则通过测试
修复建议
对App源码进行混淆处理
混淆强度检测
检测目的
检测App反编译后的源码的混淆强度,查看是否能够有效地保护代码安全
检测方法
- 检测dex文件代码中所有的类名、函数名、字段、方法,是否都经过混淆处理,例如反编译后无法正常识别Java层函数的功能
- 检测so文件中所有类名、函数名、字段、方法,是否都经过混淆处理,例如反汇编后无法正常识别Native层函数功能
检测结论
若反编译后代码*不能识别*出App函数的功能,则通过测试
修复建议
针对dex文件和so文件的类名、函数名、字段、方法进行高强度混淆
关键代码(敏感逻辑和数据保护)检测
检测目的
检测App是否对关键代码和数据实施有效的保护措施,是否暴露业务逻辑
检测方法
通过反编译工具对apk文件进行反编译,结合manifest.xml配置文件,分析App注册、登陆、支付过程、加密算法、数据通信等关键功能代码,查看相关代码逻辑是否有明显的暴露
检测结论
若App关键业务代码(如相关业务字符串)未暴露,且关键数据经过加密和隐藏保护处理,则通过测试
修复建议
将App关键代码进行隐藏、混淆、加壳等处理,从而无法逆向出重要的代码信息
防篡改测试6
程序文件防篡改检测
检测目的
检测App启动时是否进行了完整性校验,是否对客户端代码、资源文件进行修改,是否具有防篡改机制
检测方法
- 使用反编译工具Apktool对目标文件进行反编译
java -jar apktool.jar d -f /path/to/test.apk
- 修改相关代码,篡改AndroidManifest.xml、assets文件、res文件等
- 使用apktool重新打包签名后再运行App查看运行结果
java -jar apktool b -f /path/to/test
- 使用反编译工具Apktool对目标文件进行反编译
检测结论
若打包后安装后*不能正常运行*,则通过测试
修复建议
采用完整性校验技术对安装包进行校验,校验对象包括原包中代码、资源文件、配置文件等所有文件,一旦校验失败,立即退出
防调试测试7
TODO 调试工具防护检测
检测目的
检测方法
检测结论
修复建议
TODO 调试行为防护检测
检测目的
检测方法
检测结论
修复建议
内存防护检测
检测目的
检测App内存是否具有内存防护功能,防止内存转储
检测方法
- 运行App,使用ps命令查看进程PID
- 使用
gdb -p <PID>
挂载App进程后,使用(gdb) gcore
转储内存
检测结论
若未生成corefile
core.<pid>
,则通过测试修复建议
通过监控
/proc/pid/mem
和/proc/pid/pagemep
来防止内存转储
防注入测试8
进程防护检测
检测目的
检测App进程空间是否可以被注入第三方动态so文件
检测方法
- 运行App,通过注入工具或脚本,将第三方动态库文件注入App进程空间,查看第三方动态库是否在进程的内存空间中
检测结论
若第三方动态库文件*不能注入*到目标进程空间,则通过测试
修复建议
- 增加ptrace函数的检测功能,使第三方无法使用该函数附加进程
- 修改linker中的dlopen函数,防止第三方进行so加载
- 定时检测App加载的第三方so库,若发现使被注入的so库,程序进程立即报异常
服务交互安全测试 - 程序进程、前段界面、接口端口
进程间交互
检测目的
检测进程间数据通信是否具有泄露用户信息的风险
检测方法
查看AndroidManifest.xml文件中的
-
<activity android:exported="true"
则可以被第三方App启动 -
<provider android:authorities="com.bgy.ssm.fileprovider" android:exported="true"
则可以被第三方app调用,实现增、删、改、查 -
<receiver android:exported="true"
则可以接收第三方App发送的广播消息 -
<service android:enabled="true" android:exported="true"
则可以被第三方app启动
检测结论
客户端App用于跨进程通信的4种组建分别为Activity、ContentProvider、BroadCast、Service
在 未明确要求 的情况下,只要以上配置中存在任一 exported=true
则测试 不通过
修复建议
在未明确要求的情况下,在AndroidManifest.xml配置文件中设置该组建的exported属性为false,或对组建进行权限
屏幕交互
界面劫持检测
检测目的
App是否具有防界面劫持(UI欺骗)功能,防止黑客伪造界面对原有界面进行覆盖,窃取用户和密码等敏感信息
检测方法
反编译源码,查看是否具有检测程序进入后台运行的代码, 当程序不是因为触摸返回键和HOME键今后后台运行时,提醒用户具有被劫持的风险
@Override public boolean onKeyDown(int keyCode, KeyEvent event){ // 判断程序进入后台运行是否未触摸返回键和HOME键造成的 if((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME) && event.getRepeatCount()==0){ flag = false; } return super.onKeyDown(keyCode, event); } @Override protected void onPause(){ // 程序进入后台如果不是触摸返回键和HOME键造成的,则进行劫持风险提示 if(flag){ // 弹出警示信息 Toast.makeText(getApplicationContext(), "程序已经进入后台运行,具有劫持的风险", Toast.LENGTH_SHORT).show(); } super.onPause(); }
编写透明界面,当运行至登陆、支付等界面时进行覆盖,查看是否具有风险提示
修复建议
对App的UI界面进行校验,强制将自身UI时刻设置成顶层显示,其中HOME键除外,或自身UI界面进入后台运行后弹框提示用户App已经进入后台运行,有界面劫持风险
防截/录屏检测
检测目的
检测App运行后是否存在防截/录屏保护措施
检测方法
- 通过
screencap
命令进行连续截屏,查看界面后的图片是否显示敏感信息 - 通过
screenrecord
命令进行录屏,查看录制后的视频是否显示敏感信息
- 通过
修复建议
App要实现防截录屏的保护措施
WebView交互
克隆攻击检测
检测目的
检测App中是否存在设置为可被导出的Activity组件,且组建中包含WebView调用,存在导致敏感信息泄露的风险
检测方法
- 通过JEB攻击反编译dex文件的源码,查看客户端是否使用了WebView空间,并将
setAllowFileAccessFromeFileURLs
或setAllowUninvertedFileAccessFromFileURLs
API设置为true
- 检测file域的路径是否做了严格限制
- 通过JEB攻击反编译dex文件的源码,查看客户端是否使用了WebView空间,并将
检测结论
若App使用WebView组建,并将
setAllowFileAccessFromeFileURLs
或setAllowUninvertedFileAccessFromFileURLs
API设置为false
,则通过测试
若App使用WebView组建,并将setAllowFileAccessFromeFileURLs
或setAllowUninvertedFileAccessFromFileURLs
API设置为true
,且file域的路径做了严格限制,则通过测试修复建议
严格限制包含WebView调用的Activity组建的导出权限,关闭导出权限或限制导出组建的发起者
WebView安全检测
检测目的
检测App使用的WebView空间加载的外部资源是否存在潜在风险
检测方法
检测App源码,查看客户端是否对WebView对空间加载的资源文件进行了校验,过滤风险代码
检测结论
App使用WebView空间减灾的HTML未明确要求使用Javascript,
未对加载文件进行校验,或未使用安全的通信协议,
并在WebView加载的程序中有实现发送短信,拨打电话等敏感行为的操作代码,则本项测试不通过修复建议
- WebView加载的HTML页面,在未明确要求的情况下,禁用Javascript
- 对WebView加载的外部文件进行校验
- 采用HTTPS安全通信协议,不要在WebView加载的外部文件中实现敏感操作的代码
接口端口交互
对象反序列化检测
检测目的
检测App是否使用安全的API实现序列化和反序列化,是否存在反序列化漏洞
检测方法
- 通过JEB工具反编译dex文件的源码,查看客户端App是否具有实现序列化和反序列化的源代码
- 检测实现序列化和反序列化的API是否具有潜在风险和漏洞
检测结论
若客户端App不具有序列化和反序列化的代码,或实现序列化和反序列化的API不具有潜在风险和漏洞,则通过测试
修复建议
App采用安全框架的API实现序列化和反序列化
Wormhole漏洞检测
检测目的
检测App是否存在Wormhole漏洞
检测方法
- 检测App是否私自开启HTTP服务,是否进行身份认证
- 通过nmap工具扫描,检测App代码中是否开放某个TCP端口
检测结论
如App私自开启了HTTP服务,开放某个TCP端口,同时该服务无身份认证,则本测试 不通过
修复建议
App关闭HTTP服务和端口,增加App的访问权限控制机制
鉴权认证安全测试 - 接口端口、内存数据、网络通信
注册过程测试
注册信息保护检测
检测目的
检测App注册密码的复杂度(密码内容要求字母大小写、数字、特殊字符组合,长度等)和注册信息在本地存储时的保护程度是否足够高
检测方法
检测App注册密码的复杂度
public static boolean isPasswordChecked(CharSequence data){ return Pattern.compile("^((a-z0-9A-Z)+[_]?){6,20}$").matcher(data).find(); }
检测App在本地存储的注册信息是否加密存储,加密密钥是否进行了隐藏处理
检测结论
如果对注册密码复杂度、长度进行了限制处理,且对本地存储的注册信息进行了加密保护,加密密钥隐藏,则通过测试
修复建议
- 对注册密码的复杂度和长度进行限制
- 对在本地存在的用户注册信息进行加密处理,隐藏加密密钥
注册信息传输检测
检测目的
检测App将用户注册信息传输到服务器端的过程中是进行了加密保护
检测方法
使用Burpsuite拦截App注册用户的数据包,查看数据包是否加密
检测结论
若App在将用户注册信息传输到服务器端时进行了加密处理,则通过测试
修复建议
在将用户注册信息传输到服务器端的过程中,对用户注册信息进行加密处理
注册过程防爆破检测
检测目的
检测App在注册账户时,是否可以爆破获取正确的验证码,注册任意用户
检测方法
在注册界面填写完注册信息后,点击“获取验证码”,使用抓包工具对其抓包,对数据包中的验证码进行暴力破解,爆破成功后,便可注册任意账号
检测结论
若在注册App时验证码被爆破,可以任意注册账户,则本项测试不通过
修复建议
- 使用复杂的验证码,验证码长度不低于6位,包含数字及字母
- 对发送验证码请求进行时间和次数限制
- 验证码在传输时进行有效的加密处理
注册过程防嗅探检测
检测目的
检测在App注册过程中是否可以利用已有社工库中的手机号、邮箱、用户名、密码等信息,通过撞库方式频繁嗅探注册账户
检测方法
利用Burpsuite拦截注册用户时的数据包,分析查看是否暴露账户、密码参数,然后利用社工库数据替换账户、密码参数,进行撞库,从而获取用户注册
检测结论
若在注册时暴露账户、密码参数,具有利用撞库对用户注册信息进行嗅探的风险,则本项测试不通过
修复建议
- 对传输的注册账户、密码等敏感信息进行强加密处理
- 服务器端限制访问次数
登陆过程测试
密码安全验证检测
检测目的
检测App登陆密码的验证方案是在本地验证还是在服务器端验证,验证过程中是否加入了设备信息
检测方法
利用JEB逆向分析App登陆功能的源码,分析密码验证中是否加密了设备信息IMEI
检测结论
若密码验证在服务器端进行,且加入了设备信息,避免在非法设备登陆,则通过测试
修复建议
App登陆密码在服务器端进行验证,并加入设备信息,以降低用户登陆密码泄露的风险
登陆信息加密传输检测
检测目的
检测App在将用户登录信息传输到服务器端的过程中是否进行了加密保护,以免被攻击者拦截网络流量,窃取用户登陆信息
检测方法
使用Burpsuite拦截app登陆操作的数据包,分析是否明文传输用户信息
检测结论
若App在将用户注册信息传输到服务器端时进行了加密处理,则通过测试
修复建议
App在将用户登陆信息传输到服务器端的过程中,要对用户登陆信息进行加密处理
登陆过程防爆破检测
检测目的
检测App在登录时,是否可以抓取数据包,利用数据包中的验证码字段/密码字段进行暴力破解
检测方法
检测App在登陆时,是否可以爆破验证码/密码,获取正确的验证码和登陆密码
检测结论
若App在登陆时验证码和登陆密码可以被爆破,则本项测试不通过
修复建议
- 使用复杂的验证码和登陆密码
- 对发送验证码的请求进行时间和次数限制
- 对验证码、登陆密码进行输入错误次数限制
- 验证码、登录密码在传输时进行有效的加密处理
登陆过程防嗅探检测
检测目的
检测App是否可以通过爆破验证码实现任意账户登陆、任意重置用户密码等操作
检测方法
- 验证码爆破检测
在登陆界面填写完手机号等信息后点击“获取验证码”,使用抓包工具对其抓包,对数据中的验证码进行暴力破解,爆破成功后便可实现登陆任意账户、任意重置用户密码 - 短信轰炸检测
在登陆界面填写完手机号等信息后点击“获取验证码”,若短信验证码无获取时间、获取次数限制,便可重放发送短信验证码数据进行短信轰炸 - 探测是否具有撞库风险
利用工具拦截用户登陆时的数据包,分析查看是否暴露账户、密码参数,然后利用社工库数据替换账户、密码参数进行撞库,从而获取用户登陆信息
- 验证码爆破检测
检测结论
若App通过爆破验证码实现登陆任意账户、任意重置用户密码、短信炸弹,或通过撞库获取用户登陆信息,则此项测试 不通过
修复建议
- 使用复杂验证码、登陆密码
- 对发送验证码的请求进行时间和次数限制
- 对验证码输入错误次数进行限制
- 验证码在传输时进行有效的加密处理
- 服务端限制访问次数
登陆过程防绕过检测
检测目的
检测App是否可以绕过验证码登陆任意账户、修改用户ID获取其他用户信息
检测方法
- 抓取登陆成功后的响应包,之后退出,在登陆其他用户账户时,用登陆成功的响应包替换登陆失败的数据包,检测是否可以绕过验证码、密码验证,进而成功登陆其他用户的账户
- 修改用户ID,检测是否可以获取任意用户信息,若用户身份认证采用单一ID值判断,攻击者可以修改数据包中的用户ID进行重放,从而获取其他用户信息
检测结论
若App可以绕过验证码登陆其他用户,或可以修改用户ID获取其他用户信息,则本项测试 不通过
修复建议
加强身份验证机制,使用Token或Session机制,设置访问控制策略,敏感数据采用高强度加密传输
加强认证检测
检测目的
检测App客户端是否具有双因子认证机制,保护用户登陆信息
检测方法
- 检测App在登陆时,是否具有双因子认证机制(密码+令牌/指纹/设备信息/短信)
- 使用用户登陆信息在新设备登陆时,查看是否具有短信提醒
检测结论
如Apple具有双因子认证机制和不同设备登录时的短信提醒认证机制,则通过测试
修复建议
App采用双因子认证机制和不同设备登陆短信提醒认证机制,保护用户登陆信息安全
会话过程测试
有状态会话标志检测
检测目的
检测客户端与服务器端交互的会话,是否存在复杂的会话ID,同时服务器是否对其进行校验
检测方法
- 模拟客户端与服务器端登陆,查看是否采用 简单的Sessionid方式标识 客户端
- 利用服务端返回的 _Sessionid构建新的URL 访问服务器端,查看是否能够绕过验证
- 查看客户端与服务器交互时是否采用 *复杂的Key*,是否存在时间有效性校验,防止被伪造
检测结论
若客户端与服务器端会话时采用来复杂加密的Key,同时服务器端对客户端发送的Key进行了校验,攻击这无法伪造,服务器端无响应,则通过测试
修复建议
客户端与服务器端通信会话时采用复杂的算法对随机的Sessionid进行加密,同时服务器端对随机的Sessionid进行校验
无状态会话Token检测
检测目的
在客户端与服务器端通信会话过程中,检测是否存在Token机制,是否容易被攻击者截取利用
检测方法
- 检测客户端与服务器端通信会话的URL中是否使用携带Token,Token是否明文显示
- 检测客户端与服务器端认证的复杂性,是否采用类似 UID+Toekn+timestamp+密钥 的Toekn机制,并尝试破解
检测结论
若客户端与服务器端通信会话的Toekn能被轻易获取利用或被破解,则本项测试不通过
修复建议
- 每次登陆时重新生成Token,并设置有效期,每次操作后更新Token的时间戳,保证Token有效期往后延续
- 为了避免Token被截获,伪造非法请求,在每次请求时,建议采用 UID+Token+timestamp+密钥+请求参数签名 ,服务器同时验证Token和签名,以保证请求的安全性
会话不活跃检测
检测目的
若客户端与服务器端通信临时终端或长时间不活跃,检测服务器是否立即终止会话
检测方法
- 在客户单与服务器端通信过程中,若长时间不操作,然后再操作时,查看客户端与服务器端是否已中断
- 在客户单与服务器端通信过程中,若临时中断,例如打开微信等其他操作,让服务在后台运行,查看客户端与服务器端是否已中断
检测结论
若客户端与服务器端通信临时中断或长时间不活跃时,服务器端立即与客户端中断会话,需要重新认证,则本项测试结果为通过
修复建议
在客户端与服务器端通信会话过程中,增加时间的有效性,例如设置时间为5min,若客户端与服务器长时间不活跃或者客户端服务在后台运行,服务器立即中断本次会话
加强认证检测
检测目的
在客户端与服务器端进行敏感交易时,检测服务器端是否存在双因子身份认证机制
检测方法
- 检测客户端与服务器端进行支付、转账等敏感交易时,客户端是否需要多个身份认证方式,同时服务器端是否对其双因子进行校验
- 检测客户端与服务器端进行身份认证过程中,数据是否进行加密处理,加密强度如何
检测结论
若客户端与服务器端存在双因子身份认证,则通过测试
修复建议
App中涉及敏感用户信息的界面,要求使用双因子身份认证机制,例如采用支付密码和用户预留短信验证码等认证方式
登出过程测试
会话终止检测
检测目的
在用户执行登出操作后,检测服务器端是否立即终止与客户端之间的会话连接
检测方法
- 登陆App,执行一些需要在App进行身份验证的操作,并拦截
- 退出App
- 重放步骤1中的操作,显示错误消息或重定向到登录页面
检测结论
若在客户端用户执行登出操作后,服务器立即终止与客户端之间的会话连接,需要用户重新进行登陆认证
修复建议
在用户执行登出操作后,立即终止客户端与服务器端的会话连接
残留数据检测
检测目的
当用户执行登出操作后,检测服务器是否及时删除客户端对应的Token或Sessionid
检测方法
操作客户端登出功能,通过用户名、密码、及之前的Token值或Sessionid值能够登陆成功,则本项测试不通过
检测结论
在用户执行登出操作后,若客户端使用之前的Token或Sessionid值能够登陆成功,则本项测试不通过
修复建议
当用户执行登出操作后,服务端及时删除户端对应的Token或Sessionid值
注销过程测试
重新注册检测
检测目的
检测在客户端注销后,使用相同账户能否重新注册
检测方法
- 检测客户端是否存在注销功能
- 在客户单注销之后,使用相同账户注册,查看是否可以重新注册,测试第三方关联的账户是否也已经注销,还能否正常登陆
检测结论
若注销账户后仍可以使用相同的账户注册,关联的第三方数据无法使用,则通过测试
修复建议
在客户端注销操作后,可以使用相同账户重新注册,确认原来的账户信息已经清除
数据清除检测
检测目的
检测在App卸载后,本地存储的数据或账户缓存等信息是否全部清除
检测方法
- 安装App,先注册,登录试用,然后卸载,查看本地注册的用户账户信息等数据是否及时删除
- 重新安装App,查看使用之前的账户和密码是否可以直接登陆
检测结论
若卸载App后,本地数据完全及时清除,则通过测试
修复建议
在App卸载后,及时删除本地存储的全部数据
本地数据安全测试 - 内存数据、本地存储
数据创建测试
用户协议检测
检测目的
检测App是否存在用户协议声明。若存在,是否对使用用户信息用户及保护措施进行声明,是否存在违规行为
检测方法
- 安装运行App,试用App的所有主要功能,并抓包,通过数据包和源代码了解其行为特征
- 查看是否存在用户协议,以及协议内容是否声明App需要手机的用户信息及保护措施
检测结论
若App存在用户服务协议,且声明了用户信息用途及保护措施,则通过测试
修复建议
App手机用户个人信息前,必须在用户服务协议中声明,需要收集用户设备的哪些信息、具体用途、及保护用户信息的安全措施和具体承诺
数据采集检测
检测目的
检测App是否过度申请系统敏感权限,使用该权限时是否提示用户授权,是否过度手机用户数据,数据传输过程是否安全
检测方法
- 查看AndroidManifest.xml文件
标签,分析App所申请的系统权限,是否存在过度申请的敏感权限 - 查看App调用系统的敏感权限时是否提示用户授权
- 分析App源码及数据包内容,查看是否过度收集在用户协议声明范围外的用户数据,确认数据传输过程中的安全性
- 查看AndroidManifest.xml文件
检测结论
若App无过度申请系统敏感权限,且在使用该权限时提示用户授权,同时没有过度收集用户数据,则通过测试
修复建议
App发布时需要删除不需要的系统敏感权限,在申请系统敏感权限时,需要提示用户授权,不得私自上传在协议中未声明的用户信息
数据输入检测
检测目的
检测App是否实现了自带的安全键盘,且启动键盘时数字是否随机分布,关键的输入框是否禁用复制粘贴功能,是否存在验证码校验机制,验证码是否安全
检测方法
- 检测App是否实现了自定义软键盘,在每次启动时按键数字随机分布,且按键时不存在按键阴影,按键回显等特效
- 检测要求输入敏感数据(登陆密码、支付密码、银行卡账户等)输入框禁用复制粘贴功能
- 检测验证码是否由图形验证码或短信验证码组成,是否通过服务器端返回给客户端
检测结论
若App实现了自定义软键盘,键盘数字实现了随机分布,具有安全的验证码,同时密码输入框禁用了复制粘贴功能,则通过测试
修复建议
- App客户端实现自定义的软键盘,软键盘每次启动时都要随机分布,且按键无回显、阴影等特效
- 要求输入登陆、支付密码、银行卡账户等输入框禁用复制粘贴功能 设置
android:longClickable="false"
关闭其功能 - 增加复杂图形验证码或短信验证码,且在传输过程中对数据进行加密
数据生成检测
检测目的
检测App生成数据的存储形式时是结构话还是非结构话,数据是否经过加密后存储
检测方法
- 检测App生成的结构化数据,要求数据内容加密后存储
- 检测App生成的非结构化数据,要求数据内容加密后存储
检测结论
若本地存储的数据经过加密处理,则通过测试
修复建议
不管生成的数据是采用结构化还是非结构化形式存储,都要求加密后存储
数据存储测试
访问控制检测
检测目的
检测App是否具备完善的权限管理机制,是否能够与其他App隔离,是否在权限允许的范围之外存在数据被其他客户端访问的风险
检测方法
查看App本地存储文件的权限
ls -al files # 本地存储file文件权限 ls -al shared_pref # 本地存储xml文件权限 ls -al app_webview # 本地存储的cache文件权限 ls -al databases # 本地存储的db文件权限
检测结论
如客户端具备完善的权限管理机制,以最小权限为原则,则通过测试
修复建议
App客户端严格控制本地生成敏感数据访问权限,避免被第三方App非法访问导致用户信息泄露
数据加密检测
检测目的
检测App在本地存储的用户信息是否经过了加密处理,加密密钥是否进行了保护,加密算法是否合理,生成的随机数强度是否较高,避免造成用户信息泄露风险
检测方法
检测App在本地生成的数据文件是否加密,检测App在本地存储的文件是否加密
检测结论
若本地数据进行了加密处理,加密密钥进行了保护处理,且采用多种加密算法组合加密,对不同的数据采用了不同的加密算法,采用安全的方式生成随机数,则通过测试
修复建议
- 对App在本地存储的用户信息进行加密处理
- 对对称加密算法的加密密钥进行加密保护和隐藏处理
- 对APp在本地存储的用户信息进行多重加密,并对用户数据采用多种加密方式
- 避免使用不安全的随机数生成类
- 避免使用不安全的加密算法
数据处理测试
程序日志检测
检测目的
检测App源码中的调试信息是否关闭,在调试信息中是否写入敏感信息
检测方法
反编译源码,查看是否存在日志调试代码,要求不得存在日志调试代码
private void save(){ String mName=etUsername.getText().toString(); String mPwd=etPwd.getText().toString(); mEditor.putString("Name",mName); mEditor.putString("Pwd",mPwd); mEditor.commit(); Log.d("TEST","本地存储"+"用户名"+mName+"密码"+mPwd); }
动态运行App客户端,使用
logcat查看后台打印日志是否存在用户敏感数据,要求后台不得打印日志调试信息
检测结论
若App关闭了源码中的调试信息,则通过测试
修复建议
App发布时应删除源码中的日志调试代码
敏感数据不当使用检测
检测目的
检测App源码和行为特征是否符合App安全相关标准的规定
检测方法
反编译App代码,查看是否私自手机用户敏感信息,抓包拦截,检测是App是否私自上传用户隐私到服务器
检测结论
逆向分析源码和数据包,若符合App安全相关标准的规定,则通过测试
修复建议
App源码要进行严格审核处理,禁止在用户未知情的情况下私自收集用户信息
数据共享测试
第三方SDK用户协议检测
检测目的
检测在App服务协议中是否声明第三方SDK收集用户信息的用途,是否过度收集用户个人信息
检测方法
查看用户协议内容是否声明共享用户信息给第三方SDK,并通过抓包查看第三方SDK的行为特征
检测结论
若用户协议中明确声明App信息与第三方共享情况,则通过测试
修复建议
App要明确声明是否会与第三方共享用户信息,以及共享用户信息的具体用途
与第三方SDK数据共享检测
检测目的
检测App是否在用户未知情的情况下,私自共享用户个人信息给第三方SDK,以及第三方SDK是否私自收集用户个人信息到指定服务器
检测方法
- 分析App源码和数据包,查看是否在用户未知情的情况下,将收集的用户信息私自上传至第三方服务器
- 分析App嵌入的第三方SDK源码和数据包,查看是否存在第三方SDK在用户不知情的情况下,将收集的用户信息私自上传至第三方服务器
检测结论
若分析源码内容和数据包后,符合App安全相关标准的规定,则通过测试
修复建议
在App共享数据给第三方SDK的服务协议之外,禁止App和第三方SDK私自采用短信或数据包等形式,收集用户信息并上传到指定服务器
数据备份测试
敏感数据备份检测
检测目的
检测App应用数据是否可以备份,是否能够防止攻击者复制App数据
检测方法
查看Androidmanifest.xml文件中的allowBackup属性是否为true
<application android:allowBackup="true"
检测结论
在App不具备备份功能的情况下,若
<application android:allowBackup="false"
则通过测试修复建议
在App不具备备份功能的情况下,应将
<application android:allowBackup="false"
备份数据加密强度检测
检测目的
检测App备份的数据是否进行加密处理,并且要求使用复杂的加密强度高的算法
检测方法
若采用对称算法加密,则判断对称算法的密码是否存储安全,加密算法的源代码是否可以被破解
检测结论
若App备份的数据进行了加密处理,则通过测试
修复建议
采用多种混合算法加密,例如AES256,MD5,HASH,DES,BASE64
数据销毁测试
后台运行数据检测
检测目的
检测App客户端在切入后台运行时是否对收集存储的文件、数据库、配置文件、缓存文件等进行及时清理操作
检测方法
- App切入后台运行时,查看本地生成的db文件、xml文件或者内存中的数据是否进行了删除
- 导出本地的缓存信息文件,查看是否有敏感信息暴露
检测结论
若App在切入后台运行时,本地生成的临时文件db、xml、cache中的数据或者运行时内存中的用户数据做到了及时清理,则通过测试
修复建议
App在切入后台运行后,应及时清理本地存储的用户敏感信息和内存中的数据信息
敏感数据清除检测
检测目的
检测App在退出或被卸载时,是否彻底删除在手机本地存储的文件、数据库、缓存、配置信息等文件
检测方法
使用反编译工具apktool对目标文件进行反编译,查看App代码中是否具有清除缓存信息的方法
removeSessionCookie()/deleteCookie()
if("ClearWebView" , "webView.clearCache"){ try{ CookieSyncManager.createInstance(this.Y.getApplicationContext()); CookieSyncManager.getInstance().removeSessionCookie(); CookieSyncManager.getInstance().sync(); }catch(Exception v0_1){ } }
检测结论
若本地生成文件仍然存在,则本项测试 不通过
修复建议
检测App在退出或被卸载时,应彻底删除在手机本地存储的文件、数据库、缓存、配置信息等信息
网络传输安全测试 - 网络通信
安全传输层测试
TLS实现检测
检测目的
检测客户端与服务器端交互核心的通信会话是否采用HTTPS,同时是否为现有最佳实践方式
检测方法
使用Burpsuite/Wireshark抓包,判定用户登录、交易等私密连接是否使用HTTPS进行网络通信,查看TLS版本是否高于1.0
检测结论
若客户端与服务器端通信采用HTTPS,且TLS版本高于1.0,则通过测试
修复建议
客户端与服务器端核心的通信会话均采用HTTPS,同时TLS版本高于1.0
CA证书检测
检测目的
检测客户端与服务器建立安全通道时,客户端是否验证远程端点的X.509证书,是否只接受受信任的CA签名证书
检测方法
检测CA证书的合法性,是否为受信任的CA签名证书,App是否只接受受信任的CA签名证书
- 抓取App与服务器端交互的数据,校验证书的颁发机构
- 在源码中检查客户端是否只接受受信任的CA签名证书
检测结论
若截获的数据包中证书由可信任机构签发,且在有效期内,且访问服务器与证书绑定的一致,同时只接受信任的CA签名证书,则通过测试
修复建议
客户端验证远程端点的X.509证书,只接受受信任的CA签名的证书
证书校验检测
检测目的
检测客户端和服务器是否对证书进行双向校验
检测方法
- 反编译App代码,检测是否存在客户端验证服务器端证书的代码
- 验证证书内容有效性、数字摘要是否一致
- 反编译App代码,检测是否存在客户端发送本地证书给服务器端认证的代码
- 反编译App代码,检测是否存在客户端验证服务器端证书的代码
检测结论
若客户端对服务器端返回的证书进行了验证,同时服务器端也对客户端证书进行了校验,则通过测试
修复建议
建议一般的App要实现客户端对服务器端证书的单向验证,对于安全要求比较高的App,要实现客户端与服务器端证书的双向验证
主机名校验
检测目的
检测客户端是否对主机名进行校验
检测方法
反编译App代码,查找App通信的代码,查看
setHostnameVerifier()
方法接受任意域名还是进行了主机名验证public static SSLSocketFactory getFixedSocketFactory(){ MySSLSocketFactory v0; try{ v0=new MySSLSocketFactory(MySSLSocketFactory.getKeystore()); //缺陷代码 ((SSLSocketFactory)v0).setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); }catch(Throwable v1){ v1.printStackTrace(); SSLSocketFactory v0_1 = SSLSocketFactory.getSocketFactory(); } return ((SSLSocketFactory)v0); }
检测结论
若App接受任意域名,则本项测试 不通过
修复建议
App对主机名进行校验,不能接受任意域名
数据加密检测
检测目的
检测在客户端与服务器端通信过程中,业务数据是否以明文方式在网络中传输,数据加密的复杂度如何
检测方法
- 对客户端与服务器端通信的登陆、支付、转账等核心功能进行抓包,查看业务数据是否以明文方式在网络中传输
- 检测数据加密方式的复杂度,url编码、Base64编码、AES\DES加密等
检测结论
若客户端与服务器端交互的业务数据经过多个复杂的算法加密,且无法破解,则通过测试
修复建议
客户端与服务器端交互的上行/下行数据要经过多个复杂算法进行加密,同时加密存储对称加密算法密钥
中间人攻击测试
HTTP中间人会话劫持检测
检测目的
检测客户端与服务器端交互的数据是否可以被任意篡改,导致重放攻击漏洞
检测方法
运行测试App,点击登陆,拦截数据包,并进行修改后放行,查看App运行结果是否能够修改成功
检测结论
若客户端与服务器端交互的数据经过加密处理,且数据无法修改,则通过测试
修复建议
- 采用高强度的加密算法对交互数据进行加密/或使用HTTPS
- 对客户端请求的数据和服务器端返回的数据进行完整性校验,防止被篡改
HTTPS中间人会话劫持检测
检测目的
检测App在使用HTTPS时,是否存在中间人攻击漏洞
检测方法
反编译源码,查看是否校验服务器端是否可信- 查看实现X509TrustManager接口中
checkServerTrusted()
方法实现是否为空public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException{ this.sslContext = SSLContext.getInstance("TLS"); this.sslContext.init(null,new TrustManager[]{new X509TrustManager(){ public X509Certificate[] getAcceptedIssuers(){ return null; } public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException{ //实现逻辑为空 } public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException{ //实现逻辑为空 } }},null); }
反编译源码,查看站点域名与站点证书的域名是否匹配- 查看
HostnameVerifier()
方法中的verify()
函数是否存在域名校验NetworkUtils.conn=null;
NetworkUtils.is =null;
NetworkUtils.os=null;
NetowrkUtils.DO_NOT_VERIFY = new HostanmeVerifier(){public boolean verify(String s, SSLSession sslSession){ return 1; // 不检查站点域名和站点证书的域名 } }
查看
sethostnameverifier()
方法是否接受任意域名public static SSLSocketFactory getFixedSocketFactory(){ MySSLSocketFactory v0; try{ v0=new MySSLSocketFactory(MySSLSocketFactory.getKeystore()); //缺陷代码 ((SSLSocketFactory)v0).setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); }catch(Throwable v1){ v1.printStackTrace(); SSLSocketFactory v0_1 = SSLSocketFactory.getSocketFactory(); } return null; }
检测结论
若客户端不进行服务器端是否可信、不进行域名校验、接受任意域名,对APP数据包进行拦截和篡改,则造成中间人攻击的风险。
若客户端对服务器端返回的SSL证书进行强校验,则通过测试修复建议
对SSL证书进行签名CA是否合法、证书是否自签名、主机域名是否匹配、证书是否过期等校验。
App加固技术
第一代加固技术: 通过对源码进行
- 压缩: 检测并一处代码中无用的类、字段、方法和特性
- 优化: 对字节码进行优化,移除无用指令
- 混淆: 使用a、b、c、d等无意义字符对类、字段、方法进行重命名
- 预检: 在Java平台上对处理后的代码进行预检,确保加载的class文件时可执行
第二代加固技术: 对原始App的dex文件加密,并外包一层克,将App的核心代码进行隐藏
第三代加固技术: 对dex文件中所有的类及方法函数内容进行抽取、加密和隐藏,单独加密后存放在apk中的特定文件内
第四代加固技术: DVMP(dex虚拟机保护) 具有自定义虚拟机、指令集和解释器,未保护的代码在系统虚拟机中运行,保护代码在自定义虚拟机运行
Table of Contents
应用脱壳
安装Frida客户端
在Android上安装Frida Server
adb shell getprop ro.build.version.release # 获取Android版本号 adb shell getprop ro.product.cpu.abi # 查看CPU架构,根据架构下载对应的frida-server-14.2.18-android-arm64.xz xz -d frida-server-14.2.18-android-arm64.xz # 解压frida-server adb push frida-server-14.2.18-android-arm64 /data/local/tmp # 传输到/data/local/tmp目录下 adb shell su chmod a+x /data/local/tmp/frida-server-14.2.18-android-arm64 adb forward tcp:27042 tcp:27042 adb forward tcp:27043 tcp:27043 ./frida-server-14.2.18-android-arm64 python dexDump.py com.test.aspiredoctor
在macOS上安装Frida Client
pip3 install frida frida-tools
Footnotes
1 替换Android系统/system/bin/appprocess文件
2 如apk文件和dex文件
3 可导致调用系统功能截取屏幕和录像窃取用户信息,界面劫持,对用户进行钓鱼
4 检测客户端程序是否对已经root的android系统、模拟器和逆向框架进行检测
5 检测客户端程序是否进行代码加密、代码混淆和代码加固,是否易被逆向并泄露程序的设计原理和运行流程
6 检测客户程序是否对自身进行校验
7 检测客户端程序是否可被外部程序动态调试并输出敏感信息
8 检测客户端程序是否存在进程保护和内存保护,防止被外部程序动态注入so文件到指定进程、以及任意修改、转储内存代码行为
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 askding@qq.com