APP合规开发指南

移动安全 11个月前 admin
286 0 0

APP合规开发指南


围绕工信部开展的《APP侵害用户权益专项整治》337号函、《纵深推进APP侵害用户权益专项整治行动》164号函涉及的相关要求,展开说说,在APP开发过程中技术层面的合规注意事项。

一、违规收集个人信息、违规使用个人信息
  • 问题类型:APP、SDK未告知用户收集个人信息的目的、方式、范围且未经用户同意,私自收集用户个人信息的行为。

  • 问题类型:APP、SDK未向用户告知且未经用户同意,私自使用个人信息,将用户个人信息用于其提供服务之外的目的,特别是私自向其他应用或服务器发送、共享用户个人信息的行为。

根据《个人信息安全规范》、《移动互联网应用程序(App)收集个人信息基本要求》中的定义,个人信息指“以电子或其他方式能够单独或与其他信息结合识别特定自然人身份或反映特定自然人活动情况的各种信息。”除了常规理解的姓名、出生日期、身份证号码、生物特征信息、通讯录、本机号码等个人信息,在APP开发层面还会涉及到各种设备信息,因其不可变更的唯一性或追踪属性,同样也在个人信息范畴内。
  • Android系统:IMEI、MAC地址、MEID、IMSI、SN、ICCID等设备唯一标识符,Android ID、WiFi(WiFi名称、WiFi MAC地址以及设备扫描到的所有WiFi信息),SIM卡信息(IMSI、SIM卡序列号ICCID、手机号、运营商信息),应用安装列表(设备所有已安装应用的包名和应用名),传感器(传感器列表、加速度传感器、温度传感器等),蓝牙信息(设备蓝牙地址和设备扫描到的蓝牙设备信息),基站定位、GPS(用户地理位置信息),账户(各类应用注册的不同账号信息)、剪切板、IP地址、硬件序列号、SDCard信息(公有目录)等。

  • iOS系统:IDFA,IDFV,WiFi(bssid, ssid), GPS,运营商,传感器(加速器、陀螺仪、磁力器)、IP地址等。尤其注意苹果上架会检测是否调用TrueDepth APIs(面部追踪)。

1、用户同意《隐私政策》前不应收集任何个人信息,注意APP、引入SDK的初始化时机,主要涉及以下两个场景:
  • 同意《隐私政策》弹窗前不进行调用

  • 不同意《隐私政策》弹窗进入APP时不进行调用(访客态或停留在APP内)

2、对APP、SDK收集的个人信息包括设备信息在《隐私政策》中进行详细的收集声明,对于无使用需求的SDK不应接入(注意,若使用海外SDK则涉及跨境传输问题)。
3、SDK应接入官方最新版本(工信逐步开展对SDK的合规检测,SDK也在逐步合规化):
  • 结合官方合规接入指南,注意不要在未同意隐私政策前初始化收集信息。

  • SDK无合理必要功能不能超范围收集信息,如,非定位型SDK默认收集位置信息等。

✅  比如某 PUSH SDK 旧版本存在漏洞,导致攻击者可以窃取用户隐私信息,应及时更新到最新版
APP合规开发指南
二、超范围收集个人信息
  • 问题类型:APP、SDK非服务所必需或无合理应用场景,特别是在静默状态下或在后台运行时,超范围收集个人信息的行为。

1、非必要功能后台及静默运行时不收集信息,注意不要设计轮询机制(如,一些定位SDK设置固定n分钟收集信息)。

每隔一分钟请求一次位置更新:

public class LocationService extends Service {
private static final long INTERVAL = 60 * 1000; // 一分钟 private LocationManager locationManager;
@Nullable @Override public IBinder onBind(Intent intent) { return null; }
@Override public int onStartCommand(Intent intent, int flags, int startId) { // 获取位置管理器 locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); // 请求位置更新 locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, INTERVAL, 0, locationListener); return super.onStartCommand(intent, flags, startId); }
@Override public void onDestroy() { super.onDestroy(); // 停止位置更新 locationManager.removeUpdates(locationListener); }
private final LocationListener locationListener = new LocationListener() { @Override public void onLocationChanged(Location location) { // 处理位置更新 Log.d("LocationService", "latitude: " + location.getLatitude() + ", longitude: " + location.getLongitude()); } };}

2、收集频率尽可能保证全局只收集1次(最多不超过3次),收集频次不要超过1次/秒。注意:

  • 非必要情况下,禁止每使用一次用户信息,就调用API获取一次用户个人信息。

  • 建议通过缓存技术将收集的用户信息储存在缓存中,当需要使用用户信息时从缓存中调用。

✅  将Android ID保存到缓存中,从缓存中读取:

private String getAndroidId() {    String androidId = null;    SharedPreferences preferences = getSharedPreferences("my_app", Context.MODE_PRIVATE);    if (preferences.contains("android_id")) {        // 如果缓存中已经有android_id,则从缓存中读取        androidId = preferences.getString("android_id", null);    } else {        // 如果缓存中没有android_id,则从系统设置中获取        androidId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);        // 将android_id保存到缓存中        preferences.edit().putString("android_id", androidId).apply();    }    return androidId;}

3、默认不调用全量应用列表或不通过shell命令获取全量应用列表。

调用应用列表相关方法包括但不限于:

getInstalledApplications

getInstalledApplicationsAsUser
getInstalledPackages
getInstalledPackagesAsUser
queryIntentActivitiesAsUser(此方法可以输出包名,如果输出了手机已安装应用的全部包名就属于全量)
4、注册流程中不应强制申请权限,因用户拒绝授权而影响注册登录,会被认定为过度索权,即拒绝非必要信息影响正常注册登录。注册必要信息建议参考网信《常见类型移动互联网应用程序必要个人信息范围规定》 http://www.cac.gov.cn/2021-03/22/c_1617990997054277.htm

5、按Home键退出APP后(后台静默状态下),APP或SDK不能有收集行为。

三、APP强制、频繁、过度索取权限
1、调用时机:需遵循场景化授权,即在服务所必要的场景中,用户主动触发功能后申请,在用户主动触发前不应有相关调用行为。
2、权限声明:不需要的权限不应在Android的manifest.xml文件、iOS的info.plist声明,且Android系统中targetSDKVersion应不低于23。
3、敏感权限(通讯录、定位、相册(存储)、相机、麦克风等):Android端申请时用顶栏浮窗等形式同步告知目的,iOS端可直接编辑系统弹窗。

✅  Android端顶栏浮窗代码实现示例

private static final int REQUEST_LOCATION_PERMISSION = 1;
private void requestLocationPermissionWithAlert() { // 弹出悬浮窗提示用户申请权限原因 showPermissionAlert();
// 向用户索取定位权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION);}
private void showPermissionAlert() { WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT); params.gravity = Gravity.TOP; params.y = 0;
View permissionAlertView = LayoutInflater.from(this).inflate(R.layout.permission_alert, null); TextView tvAlertMessage = permissionAlertView.findViewById(R.id.tv_alert_message); tvAlertMessage.setText("为您提供导航功能,需要您授权开启地理位置权限");
windowManager.addView(permissionAlertView, params);}
@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == REQUEST_LOCATION_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 用户同意授权 // TODO: 定位相关操作 } else { // 用户拒绝授权 // TODO: 处理用户拒绝授权的情况 } }}

4、APP运行时场景化向用户申请授权,用户拒绝授权后,APP不应退出、关闭、循环弹窗申请权限使用户无法继续使用或者影响正常注册或登录。

用户拒绝授权APP退出或关闭
private static final int REQUEST_LOCATION_PERMISSION = 1;
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 检查是否已经获得了定位权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // 如果未获得权限,则请求定位权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION); } else { // 如果已经获得权限,则可以执行定位操作 // TODO: do something with the location information }}
@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_LOCATION_PERMISSION) { // 如果用户授权,则继续执行定位操作 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // TODO: do something with the location information } else { // 如果用户拒绝授权,则退出应用程序 finish(); } }}
❌  用户拒绝授权重复向用户申请权限,使用户陷入弹窗循环
private static final int REQUEST_LOCATION_PERMISSION = 1;private boolean locationPermissionGranted = false;
private void requestLocationPermission() { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) { // 如果用户之前拒绝了权限申请,则弹出提示信息,向用户解释为什么需要此权限 new AlertDialog.Builder(this) .setTitle("需要访问定位权限") .setMessage("为您提供导航功能,需要您授权地理位置权限") .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 向用户索取定位权限 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION); } }) .setNegativeButton("取消", null) .show(); } else { // 向用户索取定位权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION); }}
@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == REQUEST_LOCATION_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 用户同意授权 locationPermissionGranted = true; // TODO: 定位相关操作 } else { // 用户拒绝授权 locationPermissionGranted = false; // 重复向用户索取定位权限 requestLocationPermission(); } }}
5、电话权限(READ_PHONE_STATE):属于Android系统权限,APP可通过此权限获取设备 IMSI(国际移动用户识别码)、IMEI(国际移动设备识别码)等设备唯一标识信息,建议不做申请。不可变更的唯一设备标识(IMEI、MAC地址、MEID、IMSI、SN、ICCID),建议采用可变标识(AndroidID、OAID等)代替。
❌  收集不可变更的唯一设备标识:
// 禁止收集不可变更的唯一设备标识,如 IMEIprivate String getIMEI(Context context) {    TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {        return tm.getImei();    } else {        return tm.getDeviceId();    }}

6、存储权限(android.permission-group.STORAGE/photos):安卓申请存储权限可用mediastore或SAF框架实现,iOS可用进程外选取器或共享列表替代申请photos权限,不能频繁提示用户更改授权方式。

7、特殊敏感权限(设备管理器、辅助功能、监听通知栏、悬浮窗):需APP内弹窗,用户单独同意授权后才能使用。



APP合规开发指南
THANKFOWATCHING




About us
APP合规开发指南
陌陌安全
致力于以务实的工作保障陌陌旗下所有产品及亿万用户的信息安全
以开放的心态拥抱信息安全机构、团队与个人之间的共赢协作
以自由的氛围和丰富的资源支撑优秀同学的个人发展与职业成长
/   往 期 分 享   /
 
APP合规开发指南
「陌陌安全」
扫上方二维码码关注我们,惊喜不断哦

M   O   M   O   S   E   C   U   R   I   T   Y

    

原文始发于微信公众号(陌陌安全):APP合规开发指南

版权声明:admin 发表于 2023年5月6日 下午3:00。
转载请注明:APP合规开发指南 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...