【干货分享】手把手教你Android Logcat组件安全

在应用的开发、调试和运行期间,往往需要日志信息跟踪,以此作为辅助手段定位问题、洞察程序运行细节等。在不同的开发阶段,日志信息存在差异。一般而言,开发版(Development Version)作为正在开发的内测版本,会有很多调试信息;而发行版(Release Version)是签名后发布给用户的正式版本,日志量会较少。那么,如何避免日志输出敏感信息呢?

1. 日志输出介绍

1.1 Log

android系统提供了android.util.Log类输出日志信息,一共包含五种输出级别:


从左到右,冗长度越来越小。

1.2 Log Native源码

Log.v()/ Log.d()/ Log.i()/ Log.w()/ Log.e()最终都是调用println_native()方法:

而println_native()是Native方法:

println_native()定义如下:

总的来说println_native()的操作就是打开设备文件然后写入数据。

2. 风险分析

2.1 Android 4.1之前

在Android 4.1(API Level 16 JELLY_BEAN)之前,应用只需要申请android.permission.READ_LOGS权
限即可读取日志。所以对于Android 4.1之前的版本,当目标应用使用Log输出敏感的日志信息时,恶意应用
是可以获取目标应用的敏感信息(见3.2)。

2.2 Android 4.1 以及之后

而在Android 4.1以及更高版本,谷歌已经意识到允许应用读取其他应用的日志信息是存在安全风险的,
所以对Logcat的签名调整为“signature|system|development”。这意味着只有系统签名的应用(每个手机厂商
或者ROM厂商发布ROM使用的签名)或者系统应用(System/App/目录下的应用,一般指预置应用)才有
使用该功能的权限。还有一个例外是具有root权限的应用。

具有root权限应用获取读取日志权限的实例代码:

2.3 测试判断标准

1)正式版本,如果通过Logcat输出敏感信息的话,认定为存在安全漏洞;

2)基于第一点的前提下,根据是否兼容Android 4.1以下版本(minSdkVersion < 16,目测超过80%最
低版本小于16),调整风险等级。

3. 测试

3.1 Android 4.1之前读取日志信息

假设:目标应用通过Log.i()输出用户的登录账号信息,tag是LOGIN_ACTIVITY。
恶意应用读取目标应用输出的敏感日志信息代码示例:

调用getLog():

相当于执行 adb logcat -d -f /sdcard/dump_log.txt -s LOGIN_ACTIVITY:I。
从保存文件中获取用户的账户信息:

另外,获取到的日志信息也可以通过网络、短信等方式回传给攻击者。

3.2 Android 4.1及以后版本读取日志信息

需要root权限,参照2.2。

3.3 smali注入logcat

公司申总曾经在drops上发布一篇文档(一次app转包引发的Android分析(续)),在分析APP提交的
HTTP加密参数时,通过在smali代码中注入logcat,获取加密前的明文信息。

使用改之理注入logcat非常方便,大致为:
invoke‐static {v0, v0}, Landroid/util/Log;‐>e(Ljava/lang/String;Ljava/lang/String;)I

4. 安全建议

4.1 准则

关于Logcat的使用,google给出的建议是:

需要补充一点的是,google的建议是基于开发人员知晓恰当使用Log的级别,但是很有可能不同的开发
人员会出现Log.i(“xxx”,”敏感信息”),Log.d(“xxx”,”敏感信息”)等情况。所以笼统的建议是在生产环境中,不
要输出敏感信息日志。

4.2 最佳实践

为了调试,很难杜绝不会通过Log输出敏感信息,在开发版本中存在这种情况应该认定是合理的。所
以,不同版本差异化输出显然是一个需求。

满足这个需求的做法,一方面可以定制自己的Log输出,另一方面借助IDE插件区别对待不同版本。

4.2.1 Log统一管理类

4.2.2 Proguard 移除相关代码

eclipse中progurad是一个代码优化插件(也常常用于代码混淆),可以配置proguard移除
Log.v()/Log.d()等。配置之后,APK文件中相应的代码将会被移除。

测试:

配置proguard(proguardproject.txt):

assumenosideeffects (assume no side effects) ,假定无效,该属性用于标识无效代码。上
面配置我们将Log.v()/Log.d()/Log.w()方法从正式发布非APK中删除。

反编译查看源码,Log.d()被删除,而没有配置的Log.e()仍保留。

安装APK,运行,通过adb查看日志也只有log.e():

 

Spread the word. Share this post!

Meet The Author

Leave Comment