乐趣区

关于sdk:隐私合规检测第三方SDK调用的隐私权限

前言

看了一圈各大网站目前对于隐衷合规检测的分享,发现大家简直都是本人写一套动静代理、Hook 或者 ASM 来实现代码拦挡从而检测不合规的代码堆栈,尽管这也是一个很好的办法,然而对于一些很紧急的工作又或者是刚入门的开发者来说,无疑是很耗时间的,所以笔者在这里提供一种目前 Android 11 反对的 API 思路。官网文档传送门

最近被工信部下架的利用太多了,隐衷合规检测特地严,明明把所有能看失去的隐衷合规问题都解决了,然而工信部就是说你没解决。Ok,fine!你说没解决就没解决吧(欲哭无泪😭)

尽管咱们是把“看失去”的隐衷问题都解决了,然而那些看不到的呢?比如说依赖的第三方 SDK,它们会不会在你不晓得的状况下偷偷调用了一些隐衷权限呢?置信个别的公司我的项目都是比拟大型的,依赖了各种各样的第三方 SDK 吧(如果是大佬的话就当我没说,毕竟大佬们都喜爱本人造轮子~)

检测办法

为了让利用及其依赖项拜访用户私密数据的过程更加通明,Android 11 引入了数据拜访审核性能。

笔者应用的这种形式,是针对 Android 11 及以上的,因为这是 Android 11 的新个性,具体能够看官网文档。想要检测利用应用的隐衷权限的话,须要先临时把 targetSdkVersion 升到 30,而后等检测完不合规的中央后再还原回本来应用的 targetSdkVersion 版本。

其实当 Android 11 的新个性进去之后,开发者们必定或多或少的对 AppOpsManager.OnOpNotedCallback 有印象,然而真到了须要用的时候,可能就想不起这个回调了,在 App 中注册了这个回调之后,当利用每次产生以下任一事件时都执行相应操作:

  • 利用的代码拜访私密数据
  • 依赖库或 SDK 中的代码拜访私密数据

注:此博客所讲的例子是基于定位权限的,其余隐衷权限同理。

简略应用

以下代码段是用于数据拜访时的 AppOpsManager.OnOpNotedCallback 回调:

override fun onCreate(savedInstanceState: Bundle?) {val appOpsCallback = object : AppOpsManager.OnOpNotedCallback() {private fun logPrivateDataAccess(opCode: String, trace: String) {Log.i("youzi", "Private data accessed. Operation: $opCode\n Stack Trace:\n $trace")
        }

        override fun onNoted(syncNotedAppOp: SyncNotedAppOp) {logPrivateDataAccess(syncNotedAppOp.op, Throwable().stackTrace.toString())
        }

        override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) {logPrivateDataAccess(syncNotedAppOp.op, Throwable().stackTrace.toString())
        }

        override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) {logPrivateDataAccess(asyncNotedAppOp.op, asyncNotedAppOp.message)
        }
    }

    // 创立 AppOpsManager 实例并增加下面定义的回调
    val appOpsManager = getSystemService(AppOpsManager::class.java) as AppOpsManager
    appOpsManager.setOnOpNotedCallback(mainExecutor, appOpsCallback)
}

回调里须要重写的几个办法笔者就不多赘述了,官网文档里讲得曾经很具体了。这里的 logPrivateDataAccess 办法是本人写的,名字可随便定义,用于打印应用到的权限名字,还有应用到的代码堆栈,不便大家看到在哪里调用了某个权限。

这里给个笔者检测时打印进去的日志示例:
![
](https://upload-images.jianshu…)

能够看到,打印出的日志当中会有应用到的定位权限名字 fine_location,还有Stack Trace 上面的堆栈信息,此堆栈示意在 DebugDB.initialize() 中调用了定位权限,此时咱们能够点击前面括号中的的信息跳转到该办法,而后就可以看失去此办法是属于哪个 SDK 的了。

(笔者悲催的发现这个 SDK 是没有中央应用的,属于被废除掉的了,如果要一个个去查所有应用到的 SDK 里是否调用了权限,那将是十分大的工作量😭,大家也能够趁机看一下本人我的项目里哪些不再须要的 SDK,早删早轻松)

按归因标记应用

  1. 当只须要检测某个页面中是否调用了隐衷权限的话,只须要在须要检测的 Activity 中的 onCreate 注册该回调。
  2. 当须要在利用中检测所有页面是否调用了隐衷权限的话,须要在利用的 Application 中的 onCreate 中注册该回调。
  3. 当须要检测特定的权限时,能够应用 按归因标记审核数据拜访,艰深点来说就是创立专属的 Tag 标记。

这里的场景一跟场景二下面曾经阐明了,区别在于在不同的中央注册而已,当初次要是要讲一下场景三,援用一下官网文档的一句话:

如果您在某个 Activity 中拜访数据(例如申请地位信息或拜访用户的联系人列表),请在该 Activity 的 onCreate()办法中调用 createAttributionContext(),并传入您心愿与利用的一部分相关联的归因标记。

举个例子,也就是说如果你须要在某个 Activity 中调用LocationManager,你须要先创立一个与之关联的 Tag 标记,如下代码段所示:

    private lateinit var attributionContext: Context
    private lateinit var locationManager: LocationManager
    
    override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)
        
        // 创立归因标记,简称 Tag
        attributionContext = createAttributionContext("findLocation")
        // 通过下面的 attributionContext 来创立 locationManager 实例,关联归因标记
        locationManager = attributionContext.getSystemService(LocationManager::class.java) as LocationManager
    }

下面能够看到咱们通过 createAttributionContext("findLocation") 创立了一个 Tag,而后依据这个 context 来创立 LocationManager,在当前须要调用定位的中央就能够间接应用这个与 Tag 相关联的 locationManager 了,接下来咱们只须要略微批改一下刚刚所应用的AppOpsManager.OnOpNotedCallback 即可:

    private lateinit var attributionContext: Context
    private lateinit var locationManager: LocationManager
    
    override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)
        
        // 创立归因标记,简称 Tag
        attributionContext = createAttributionContext("findLocation")
        // 通过下面的 attributionContext 来创立 locationManager 实例,关联归因标记
        locationManager = attributionContext.getSystemService(LocationManager::class.java) as LocationManager
        
        val appOpsCallback = object : AppOpsManager.OnOpNotedCallback() {
            // 新增 attributionTag 参数
            private fun logPrivateDataAccess(opCode: String, attributionTag: String, trace: String) {Log.i("youzi", "Private data accessed. Operation: $opCode\n Attribution Tag:$attributionTag\n Stack Trace:\n $trace")
            }

            override fun onNoted(syncNotedAppOp: SyncNotedAppOp) {
                syncNotedAppOp.attributionTag?.let {logPrivateDataAccess(syncNotedAppOp.op, it, Throwable().stackTrace.toString())
                }
            }

            override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) {
                syncNotedAppOp.attributionTag?.let {logPrivateDataAccess(syncNotedAppOp.op, it, Throwable().stackTrace.toString())
                }
            }

            override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) {
                asyncNotedAppOp.attributionTag?.let {logPrivateDataAccess(syncNotedAppOp.op, it, Throwable().stackTrace.toString())
                }
            }
        }

        // 创立 AppOpsManager 实例并增加下面定义的回调
        val appOpsManager = getSystemService(AppOpsManager::class.java) as AppOpsManager
        appOpsManager.setOnOpNotedCallback(mainExecutor, appOpsCallback)
    }

咱们通过为刚刚的 logPrivateDataAccess 新增了一个须要传的参数 attributionTag,而后在重写AppOpsManager.OnOpNotedCallback() 中的三个办法时,判断一下 attributionTag 是否为 null,如果为 null 的话则示意与咱们须要检测的权限无关联,此时就不须要打印。

如果在利用中给多种隐衷权限设置了 Tag,则能够在回调中拿到 attributionTag 的时候,判断一下是否是本人想要检测的那个 Tag~

这个例子是写在 Activity 中的,如果须要全局应用的话,能够本人定义一个 LocationManager 的单例,而后对立设置 Tag,以便当前须要检测的时候能够应用 归因标记 形式。

总结

顺便总结一下,在应用上面这段代码的时候,居然不晓得 wifiManager.connectionInfo 这个办法会调用定位,当初“粗浅”的记住了。

    val wifiManager = applicationContext.getSystemService(WIFI_SERVICE) as WifiManager
    val wifiInfo = wifiManager.connectionInfo
退出移动版