1前言
随着移动应用越来越流行,导致的隐私安全问题越来越多,各个国家对App的隐私和安全要求也越来越高。我们自己写的代码可以把控代码中的隐私和安全内容,但是集成第三方SDK,虽然很便捷,但不确定SDK本身是否有违规收集用户隐私的行为,所以就引入了第三方SDK隐私合规检测,这一过程对于实现隐私合规和维护用户信任至关重要;本文将探讨在Android平台上实施这一检测的有效方法。
2检测手段
在SDK隐私合规检测中,检测敏感函数是我们检测SDK隐私合规的手段;敏感函数是指那些能够获取或处理用户隐私数据、影响设备功能和管理外部通信的编程接口。这些函数在应用开发中提供了必要的功能,但由于其潜在的滥用风险,因此被视为“敏感”。
具体来说,敏感函数可能包括但不限于:
1. 用户隐私信息访问:如获取联系人、通话记录、短信及位置信息等。
2. 设备管理功能:如访问相机、麦克风、GPS等硬件。
3. 网络通信功能:用于管理应用与外部服务器之间的数据传输,包括发送和接收数据。
4. 系统级功能:如安装或卸载应用程序、读取或修改系统设置等。
通过检测分析这些敏感函数的调用,我们能够及时识别是否存在不当的数据收集或隐私侵犯行为,从而确保SDK符合相关隐私法规,保护用户的隐私和数据安全。
3通过危险权限识别敏感函数
根据谷歌官方列出来的危险权限列表,其中标记为Protection level: dangerous的是危险权限,根据访问危险权限的编程代码,下面列出部分常见的敏感函数列表:
| 功能 | 所在包名 | 敏感函数(关键字) | ||
|---|---|---|---|---|
| 获取已安装应用列表 | android.content.pm.PackageManager | getInstalledPackages()获取安装包列表 | ||
| 获取位置 | android.location.LocationManager | requestLocationUpdates()获取位置 | ||
| 访问相机 | android.hardware.Camera | open()打开相机 | ||
| android.hardware.Camera | takePicture()拍照 | |||
| 获取所有联系人 | android.content.ContentResolver | query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI)获取所有联系人 | ||
| 访问麦克风 | android.media.AudioRecord | startRecording()开始录音 | ||
| android.media.MediaRecorder | start()开始录音 | |||
| 网络请求 | java.net.HttpURLConnection | getOutputStream()post上传数据 | ||
| javax.net.ssl.HttpsURLConnection | getOutputStream()post上传数据 |
4自动化检测
目前市面上主要有静态检测和动态检测两种方案,但经过调研发现都或多或少存在缺陷不够完美,所以本文旨在实现一种全新的自动化静态检测方案。
说到自动化就需要用到自动化工具,这里我选用了pipeline流水线自动化工具,可能有部分同学还没接触过pipeline流水线,不懂pipeline是什么东西,简而言之,pipeline就是一系列自动化步骤或阶段,这些步骤可以有效地处理和交付代码、数据或其他工件。
我们将编写好的流水线代码部署到Jenkins打包机,这样团队内的所有人都可以直接使用自动化检测。
在本方案中,使用pipeline流水线实现自动化检测敏感函数总共有5个步骤,如下图所示:
通过Http post请求将第三方SDK的依赖坐标传参给Jenkins流水线,流水线接收参数后开始按照顺序自动的执行每一个声明的步骤,下面分别展开介绍每个步骤的实现流程:
4.1获取配置文件
首先解释配置文件是什么,在创建一个新的 Android 项目时,Android Studio 会自动生成几个重要的 Gradle 配置文件来简化项目构建和依赖管理。新建一个Android项目自动生成的文件如图所示:
上图所示的几个Gradle配置文件,使得可以执行Gradle命令implementation("xxxx")依赖第三方SDK构建项目、编译打包,所以我们的配置文件就是这几个Gradle配置文件,这几个配置文件在我们的方案中是用于下载第三方SDK的aar包。
另外还有前面提到的敏感函数的列表,为了方便后续新增扩展敏感函数检测,采用Json格式的文件保存:
这个Json文件同时作为配置文件,在gitlab上创建一个仓库专门存放Gradle、Json这两个配置文件,仓库的文件列表如下:
准备好配置文件后,就可以配置流水线的第一个stage步骤,在流水线中新建一个stage,使用git命令将该配置仓库clone到本地,代码如下:
stage('Get Configuration') {
steps {
checkout(
[$class : 'GitSCM',
branches : [[name: "master"]],
extensions : [[$class: 'WipeWorkspace'], [$class: 'CheckoutOption', timeout: 20], [$class: 'CloneOption', noTags: false, reference: '', shallow: false, timeout: 20]],
userRemoteConfigs: [[credentialsId: 'keyId', url: 'https://xxxx.com/vesync-code-detect-configuration.git']]])
}
}
4.2自定义敏感函数检测插件
问:什么是检测插件?
答:检查插件是一个纯Java项目打造的jar包,用于下载、反编译第三方SDK aar包获取java源码、并遍历扫描java代码中的敏感函数关键字,该插件内部实现流程如下:
4.2.1接收第三方SDK的依赖坐标和敏感函数配置文件
从main方法的参数中获取在命令行执行jar包时传入的参数:
...
// 获取第三方SDK的依赖坐标,如:com.sensorsdata.analytics.android:SensorsAnalyticsSDK:6.7.9
String libUrl = args[0];
// 获取敏感函数配置文件
String jsonPath = args[1]
...
}
4.2.2下载第三方SDK aar包
通过Runtime类执行Gradle命令下载第三方SDK aar包,实现代码如下
try {
// 执行gradle命令下载依赖库
String command = "./gradlew downloadLibs -PLIB_URL=" + libUrl + " -PIS_TRANSITIVE=true";
// 使用Runtime执行命令
Process process = Runtime.getRuntime().exec(command);
// 读取命令的输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 等待命令执行完成
process.waitFor();
// 获取退出值
int exitValue = process.exitValue();
return exitValue;
} catch (Exception e) {
System.out.println("执行gradle命令出错:" + e.getMessage());
}
4.2.3使用jadx反编译aar获取java源码
获取到的aar包是编译过的二进制文件,无法直接查看源码,所以需要对aar包进行反编译获取java源码。Jadx是一个开源的反编译工具,主要用于将 Dalvik 字节码从 apk、dex、aar、jar、aab 和 zip 文件反编译为 Java 代码。Jadx不仅提供GUI,还能以library库方式集成到Java项目使用,所以我们采用library依赖库的方式,主要的反编译实现代码如下:
/**
* @param inputPath aar包的路径
* @param outputPath 反编译输出的java源码路径
* @return 反编译结果,成功返回0
*/
public int excuteJadx(String inputPath, String outputPath) {
JadxArgs jadxArgs = new JadxArgs();
jadxArgs.setInputFile(new File(inputPath));
jadxArgs.setOutDir(new File(outputPath));
jadxArgs.setCodeCache(new NoOpCodeCache());
try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) {
jadx.load();
jadx.save();
} catch (Exception e) {
e.printStackTrace();
return -1;
}
return 0;
}
4.2.4解析Josn文件获取敏感函数配置文件
配置文件是一个Json格式的敏感函数列表,内容有敏感函数关键字、敏感函数所在包名、敏感函数类型三个字段,通过解析Json文件获得敏感函数列表,由于敏感函数关键字是唯一的,所以敏感函数作为Map的key值,敏感函数所在包名作为value,保存到HashMap;同时使用HashSet保存所有敏感函数对应的包名,流程图如下:
4.2.5遍历所有java文件检测敏感函数关键字
这一步主要是判断该类是否有导入敏感函数相关的包名,以及判断文件中是否存在敏感函数关键字,如果存在则记录到xls表格,这里使用了开源库Poi实现excel表格记录功能,xls表格文件以第三方SDK的引用路径命名,主要流程图如下:
4.3下载检测插件
将检测插件的java项目打包成jar并将jar包上传到自己的Nexus仓库;在4.1中下载的Gradle配置文件,其中build.gradle.kts声明了一个下载检测插件的任务"downloadPlugin",该任务主要是将下载的检测插件jar包移动到plugin目录,并重命名为code-detect.jar方便后续在终端命令行运行jar包,gradle脚本代码如下:
dependencies {
val tempLibUrl = getProperty("LIB_URL")
// 不传LIB_URL参数,则下载代码检测插件,代码检测插件版本号在这更新即可
val realLibUrl = tempLibUrl.ifBlank { "com.vesync:lib-code-detect:2.1-SNAPSHOT" }
"implementation"(realLibUrl) {
isTransitive = getProperty("IS_TRANSITIVE").toBooleanOrDefault()
}
}
tasks.register<Copy>("downloadPlugin") {
from(configurations.getByName("implementation"))
into("plugin")
rename {
"code-detect.jar"
}
}
在流水线中执行gradle命令“./gradlew downloadPlugin”,即可将敏感函数检测插件下载到plugin目录并重命名为“code-detect.jar”,流水线脚本如下:
stage('Download jar') {
steps {
script {
// 授权
sh "chmod u+x ./gradlew"
// 执行gradle任务
sh "./gradlew downloadPlugin"
}
}
}
4.4执行检测插件
上一步骤已经将检测插件jar包下载到本地,所以可以在流水线中执行jar命令运行检测插件,将第三方SDK的依赖坐标和敏感函数配置文件传进去,即可开始自动检测,代码如下:
stage('Execute jar') {
steps {
script {
sh "java -jar ./plugin/code-detect.jar ${LIB_URL} ./sensitive-function.json"
}
}
}
其中${LIB_URL}参数就是从WebHook传过来的第三方SDK的依赖坐标参数,例如是“com.sensorsdata.analytics.android:SensorsAnalyticsSDK:6.7.9”,sensitive-function.json参数是在4.1中下载的敏感函数配置文件。
4.5输出检测报告
上一个步骤执行完后,如果有检测到敏感函数,会输出一份xls表格文件在本地存储,这一步骤是要把检测结果展示在jenkins任务页;
在流水线脚本中首先根据检测结果的文件名(以第三方SDK的依赖坐标命名)判断是否存在检测结果,如果存在则根据文件路径建立文件链接,接着通过pipeline的
currentBuild.description展示检测结果xls表格文件的下载链接在jenkins任务页。
在jenkins任务页可以直接下载检测结果xls表格,表格主要有四项信息:所在类名、行号、调用代码、敏感类型,格式如下:
5总结
以上就是Android SDK 隐私合规自动化检测方案,核心思想就是通过Jenkins pipeline自动完成整个检测过程,使用方法非常简单,发起http post请求触发Jenkins pipeline执行检测任务即可进行自动化检测,在http请求的body数据传参要检测的第三方SDK的依赖坐标,数据格式如下:
{"lib_url":"com.sensorsdata.analytics.android:SensorsAnalyticsSDK:6.7.9"}
经实战检测Vesync App正在使用的177个第三方SDK,均能正确检测出SDK存在哪些敏感函数调用。
通过Jenkins pipeline实现自动化检测第三方SDK是否存在敏感函数的功能,达到了一键自动化检测的效果,相比于目前市面上常见的检测方式,不仅解决了需要手动操作的痛点还能防止出现遗漏检测,还简单易用使用成本低方便在团队中推广应用、通过自定义检测插件和检测规则,使得检测范围更全面、且还可以扩展敏感函数检测类型,大大提高隐私合规检测的准确性和灵活性。希望本文能对开发者在隐私合规检测方面有所帮助和启发,让我们的App都能符合相关隐私法规,保护用户的隐私和数据安全,避免App出现隐私不合规导致下架的问题。
参考资料:
1.谷歌危险权限官方链接:developer.android.com/reference/a…
2.Jadx反编译工具Github链接:github.com/skylot/jadx
3.集成Jadx参考链接:github.com/skylot/jadx…