工信部隐私问题快速排查整改方案
背景
互联网时代,隐私变得越来越重要,各个互联网公司对隐私的意识也变得逐渐增强。于此同时,工信部也开启了新一轮的印象排查,明确要求了,针对App在用户同意相关隐私政策之前不允许App开发者收集用户相关隐私信息,特别是可定位设备的唯一信息,例如:网卡硬件地址(mac地址),设备imei信息,设备已经安装的应用列表信息等等。
整改措施
- 排查App各业务层系统隐私API的调用情况
- 排查引入的第三方SDK对隐私API的调用情况
存在的问题
自身业务对于系统API隐私API的调用,是业务开发人员可控制的,因为我们可以轻而易举的完成整改要求,在用户同意隐私政策之前不进行相关调用。然而问题是,第三方SDK的相关调用,如何能保证用户同意之前不允许相关SDK调用隐私API呢?
三方SDK隐私API调用解决方案
- 用户同意隐私政策前尽量不初始化三方SDK
- 通过Hook相关隐私API的调用排查不合规的SDK,并对其进行统一修改
想要统一对隐私API进行Hook操作,那么我们肯定是需要使用AOP编程思想,在相关隐私API被调用之前,判断使用是否同意过隐私增长,如果没有同意,则返回空数据不让其调用隐私API。
AOP框架的选择
AOP框架有很多,哪一种更合适? 这个看自身业务需求,如果说自身业务已经接入了相关的AOP框架(如:ASM,AspectJ等),则直接对相关API进行Hook即可。如果目前的业务还没有使用此类框架,我们推荐基于ASM的框架,虽然AspectJ能力更强对开发者更友好,但是处理简单的隐私API调用确实是大材小用了。
处理方案示例:
下边我们就以滴滴开源的以ASM框架为基础的轻量级框架DroidAssist为例,简单处理相关业务:
4.1 首先DroidAssit接入,不在详细描述
4.2 Hook系统API调用:mac地址相关API,imei相关API,已安装的应用列表相关API
伪代码逻辑
1
2
3
4
5if(!isPrivcayAgreen) {
return ""
} else {
return do system api
}4.3 添加代码织入逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154<DroidAssist>
<Global>
<Filter>
<Include>*</Include>
</Filter>
</Global>
<Replace>
<!-- mac地址-->
<MethodCall>
<Source>
byte[] java.net.NetworkInterface.getHardwareAddress()
</Source>
<Target>
{
if (com.netease.ASMPrivacyUtil.isRejectMode()) {
$_= com.netease.ASMPrivacyUtil.getHardwareAddress();
} else {
$_ =$proceed($$);
}
}
</Target>
<Filter>
<Exclude>com.netease.ASMPrivacyUtil</Exclude>
</Filter>
</MethodCall>
<!-- mac地址-->
<MethodCall>
<Source>
java.lang.String android.net.wifi.WifiInfo.getMacAddress()
</Source>
<Target>
{
if (com.netease.ASMPrivacyUtil.isRejectMode()) {
$_= com.netease.ASMPrivacyUtil.getMacAddress();
} else {
$_ =$proceed($$);
}
}
</Target>
<Filter>
<Exclude>com.netease.ASMPrivacyUtil</Exclude>
</Filter>
</MethodCall>
<!-- imsi-->
<MethodCall>
<Source>
java.lang.String android.telephony.TelephonyManager.getSubscriberId()
</Source>
<Target>
{
if (com.netease.ASMPrivacyUtil.isRejectMode()) {
$_= com.netease.ASMPrivacyUtil.getSubscriberId();
} else {
$_ =$proceed($$);
}
}
</Target>
<Filter>
<Exclude>com.netease.ASMPrivacyUtil</Exclude>
</Filter>
</MethodCall>
<!-- imei-->
<MethodCall>
<Source>
java.lang.String android.telephony.TelephonyManager.getDeviceId()
</Source>
<Target>
{
if (com.netease.ASMPrivacyUtil.isRejectMode()) {
$_= com.netease.ASMPrivacyUtil.getDeviceId();
} else {
$_ =$proceed($$);
}
}
</Target>
<Filter>
<Exclude>com.netease.ASMPrivacyUtil</Exclude>
</Filter>
</MethodCall>
<!-- 应用列表相关 start-->
<MethodCall>
<Source>
java.util.List android.content.pm.PackageManager.getInstalledApplications(int)
</Source>
<Target>
{
if (com.netease.ASMPrivacyUtil.isRejectMode()) {
$_= com.netease.ASMPrivacyUtil.getInstalledApplications($$);
} else {
$_ =$proceed($$);
}
}
</Target>
<Filter>
<Exclude>com.netease.ASMPrivacyUtil</Exclude>
</Filter>
</MethodCall>
<MethodCall>
<Source>
java.util.List android.content.pm.PackageManager.getInstalledApplicationsAsUser(int,int)
</Source>
<Target>
{
if (com.netease.ASMPrivacyUtil.isRejectMode()) {
$_= com.netease.ASMPrivacyUtil.getInstalledApplicationsAsUser($$);
} else {
$_ =$proceed($$);
}
}
</Target>
<Filter>
<Exclude>com.netease.ASMPrivacyUtil</Exclude>
</Filter>
</MethodCall>
<MethodCall>
<Source>
java.util.List android.content.pm.PackageManager.getInstalledModules(int)
</Source>
<Target>
{
if (com.netease.ASMPrivacyUtil.isRejectMode()) {
$_= com.netease.ASMPrivacyUtil.getInstalledModules($$);
} else {
$_ =$proceed($$);
}
}
</Target>
<Filter>
<Exclude>com.netease.ASMPrivacyUtil</Exclude>
</Filter>
</MethodCall>
<MethodCall>
<Source>
java.util.List android.content.pm.PackageManager.getInstalledPackages(int)
</Source>
<Target>
{
if (com.netease.ASMPrivacyUtil.isRejectMode()) {
$_= com.netease.ASMPrivacyUtil.getInstalledPackages($$);
} else {
$_ =$proceed($$);
}
}
</Target>
<Filter>
<Exclude>com.netease.ASMPrivacyUtil</Exclude>
</Filter>
</MethodCall>
<!-- 应用列表相关 end-->
</Replace>
</DroidAssist>4.4 对比织入前后的代码差异
织入前代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51private static String getMacBelowM(Context context) {
try {
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
if (wifiInfo != null) {
return wifiInfo.getMacAddress();
}
} catch (Exception var3) {
var3.printStackTrace();
}
return "";
}
private static String getMacUpM() {
try {
String interfaseName = "wlan0";
List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
Iterator var2 = interfaces.iterator();
while (var2.hasNext()) {
NetworkInterface intf = (NetworkInterface) var2.next();
if (intf.getName().equalsIgnoreCase(interfaseName)) {
byte[] mac = intf.getHardwareAddress();
if (mac == null) {
return "";
}
StringBuilder builder = new StringBuilder();
byte[] var6 = mac;
int var7 = mac.length;
for (int var8 = 0; var8 < var7; ++var8) {
byte aMac = var6[var8];
builder.append(String.format("%02X:", aMac));
}
if (builder.length() > 0) {
builder.deleteCharAt(builder.length() - 1);
}
return builder.toString();
}
}
} catch (Exception var10) {
var10.printStackTrace();
}
return "";
}织入后代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39private static String getMacBelowM(Context context) {
try {
WifiInfo wifiInfo = ((WifiManager) context.getSystemService("wifi")).getConnectionInfo();
if (wifiInfo != null) {
return PrivacyAPINullImpl.isRejectMode() ? PrivacyAPINullImpl.getMacAddress() : wifiInfo.getMacAddress();
}
} catch (Exception var3) {
var3.printStackTrace();
}
return "";
}
private static String getMacUpM() {
String str = "";
try {
String interfaseName = "wlan0";
for (NetworkInterface intf : Collections.list(NetworkInterface.getNetworkInterfaces())) {
if (intf.getName().equalsIgnoreCase(interfaseName)) {
byte[] mac = PrivacyAPINullImpl.isRejectMode() ? PrivacyAPINullImpl.getHardwareAddress() : intf.getHardwareAddress();
if (mac == null) {
return str;
}
StringBuilder builder = new StringBuilder();
byte[] var6 = mac;
int var7 = mac.length;
for (int var8 = 0; var8 < var7; var8++) {
builder.append(String.format("%02X:", new Object[]{Byte.valueOf(var6[var8])}));
}
if (builder.length() > 0) {
builder.deleteCharAt(builder.length() - 1);
}
return builder.toString();
}
}
} catch (Exception var10) {
var10.printStackTrace();
}
return str;
}