The Android Developer Challenge is back! Submit your idea before December 2.

唯一标识符最佳做法

本文提供了根据用例为应用选择合适标识符的指导。

要大致了解 Android 权限,请参阅权限概览。有关使用 Android 权限的具体最佳做法,请参阅应用权限最佳做法

使用 Android 标识符的最佳做法

在使用 Android 标识符时,请遵循以下最佳做法:

#1:避免使用硬件标识符。 在大多数用例中,您可以避免使用硬件标识符,例如 SSAID (Android ID) 和 IMEI,而不会限制所需的功能。

#2:只针对用户分析或广告用例使用广告 ID。 在使用广告 ID 时,请始终遵循用户关于广告跟踪的选择。此外,请确保标识符无法关联到个人身份信息 (PII),并避免桥接广告 ID 重置。

#3:尽一切可能针对防欺诈支付和电话以外的所有其他用例使用实例 ID 或私密存储的 GUID。 对于绝大多数非广告用例,使用实例 ID 或 GUID 应该足矣。

#4:使用适合您的用例的 API 以尽量降低隐私权风险。 使用 DRM API 保护重要内容,并使用 SafetyNet API 防止滥用行为。SafetyNet API 是能够确定设备真伪而不会招致隐私权风险的最简单方法。

本指南剩下的部分将以开发 Android 应用为背景详细介绍这些规则。

Android 8.0 及更高版本中的标识符

MAC 地址具有全局唯一性,无法由用户重置,在恢复出厂设置后也不会变化。因此,一般不建议使用 MAC 地址进行任何形式的用户标识。在 Android 6.0(API 级别 23)及更高版本中,本地设备 MAC 地址(例如 WLAN 和蓝牙)无法通过第三方 API 获取WifiInfo.getMacAddress() 方法和 BluetoothAdapter.getDefaultAdapter().getAddress() 方法都会返回 02:00:00:00:00:00

此外,您还必须拥有下列权限,才能获取通过蓝牙和 WLAN 扫描获得的附近外部设备的 MAC 地址:

方法/属性 所需权限
WifiManager.getScanResults() ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION
BluetoothDevice.ACTION_FOUND ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION
BluetoothLeScanner.startScan(ScanCallback) ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION

使用广告 ID

广告 ID 是一种可由用户重置的标识符,适用于广告用例。不过,在使用此 ID 时,请注意以下要点:

始终尊重用户重置广告 ID 的意图。在未经用户同意的情况下,请勿使用其他标识符或指纹将后续广告 ID 关联起来,对用户重置进行桥接。Google Play 开发者内容政策声明如下:

“…重置后,在未获得用户明确许可的情况下,新的广告标识符不得与先前的广告标识符或由先前的广告标识符所衍生的数据相关联。”

始终尊重关联的个性化广告标记。 广告 ID 是可配置的,用户可以限制与 ID 关联的跟踪数量。始终使用 AdvertisingIdClient.Info.isLimitAdTrackingEnabled() 方法来确保您没有忽视用户的愿望。Google Play 开发者内容政策声明如下:

“…您还必须遵循用户的‘选择停用针对用户兴趣投放广告’或‘选择停用广告个性化功能’设置。如果用户已启用此设置,您不得出于广告目的而使用广告标识符创建用户个人资料,也不得使用广告标识符向用户投放个性化广告。允许的活动包括:内容相关广告定位、频次上限、转化跟踪、生成报表以及安全性和欺诈检测。”

注意与您使用的 SDK 有关、涉及广告 ID 用途的任何隐私权或安全性政策。例如,如果您将 true 传递到 Google Analytics(分析)SDK 中的 enableAdvertisingIdCollection() 方法,请务必查看和遵守所有适用的 Google Analytics(分析)SDK 政策

此外,还要注意,Google Play 开发者内容政策要求广告 ID“不得与个人身份信息或任何永久性设备标识符(例如:SSAID、MAC 地址、IMEI 等)相关联。”

举例来说,假设您想要收集信息来填充包含以下列的数据库表:

TABLE-01
timestamp ad_id account_id clickid
TABLE-02
account_id name dob country

在本例中,ad_id 列可通过两个表中都存在的 account_id 列与 PII 关联,如果您未获得用户的明确许可,则会违反 Google Play 开发者内容政策

请记住,广告主 ID 与 PII 之间的关联并非总是这样明确。PII 和广告 ID 键控表中可能都存在一些“准标识符”,它们也会引发问题。例如,假设我们对 TABLE-01 和 TABLE-02 做如下更改:

TABLE-01
timestamp ad_id clickid dev_model
TABLE-02
timestamp demo account_id dev_model name

在此情况下,点击事件足够罕见的话,仍然可以利用事件的时间戳和设备型号在广告主 ID TABLE-01 与 TABLE-02 中包含的 PII 之间建立关联。

尽管通常难以保证数据集内不存在此类准标识符,但您可以通过尽可能泛化唯一数据来预防最明显的关联风险。在前面的示例中,这意味着需要降低时间戳的准确性,让每个时间戳都对应多台同型号的设备。

其他解决方案包括:

  • 设计表时不在 PII 与广告 ID 之间建立明确关联。在上面的第一个示例中,这意味着 TABLE-01 中不能包含 account_id 列。

  • 对于同时拥有广告 ID 键控数据和 PII 访问权限的用户或角色,隔离并监控其访问控制列表。通过严格控制和审核同时访问两个来源(例如,在两个表之间建立关联)的权限,您可以降低广告 ID 与 PII 之间有关联的风险。一般来说,控制访问权限需要执行以下操作:

    1. 断开广告主 ID 键控数据 ACL 与 PII ACL 之间的关联,以尽量减少同时存在于两个 ACL 中的用户或角色的数量。
    2. 实现访问日志记录和审核,以检测和管理此规则的任何例外情况。

如需详细了解如何以负责任的方式使用广告 ID,请参阅 AdvertisingIdClient API 参考。

使用实例 ID 和 GUID

标识运行在设备上的应用实例最简单明了的方法就是使用实例 ID,在大多数非广告用例中,这是建议的解决方案。只有进行了针对性配置的应用实例才能访问该标识符,并且标识符重置起来(相对)容易,因为它只存在于应用的安装期。

因此,与无法重置的设备级硬件 ID 相比,实例 ID 具有更好的隐私权属性。如需了解详细信息,请参阅 FirebaseInstanceId API 参考。

对于实例 ID 不实用的情况,您还可以使用自定义全局唯一 ID (GUID) 对应用实例进行唯一标识。最简单的方式是使用以下代码生成您自己的 GUID。

Kotlin

    var uniqueID = UUID.randomUUID().toString()
    

Java

    String uniqueID = UUID.randomUUID().toString();
    

由于该标识符具有全局唯一性,您可以使用它来标识特定应用实例。为了避免与跨应用关联标识符有关的问题,请将 GUID 存储到内部存储空间,而不是外部(共享)存储设备。有关详情,请参阅数据和文件存储概览页面。

标识符特性

Android 操作系统提供了多种具有不同行为特性的 ID。您应该使用何种 ID 取决于以下特性适合您用例的程度。然而,这些特性还涉及到隐私权,因此您必须要了解这些特性彼此之间是如何互动的。

作用域

标识符作用域说明了哪些系统可以访问标识符。Android 标识符的作用域一般分为三种:

  • 单一应用 - ID 仅限应用内部使用,其他应用无法访问。
  • 一组应用 - ID 可供一组预先定义的相关应用访问。
  • 设备 - ID 可供安装在设备上的所有应用访问。

向标识符授予的作用域越大,其作跟踪用途的风险就越大。相反,如果标识符只能由单一应用实例访问,就无法用于跨不同应用的事务跟踪设备。

重置性与持久性

重置性和持久性定义了标识符的生命周期并说明了如何对其进行重置。常见的重置触发器包括:应用内重置、通过系统设置重置、启动时重置以及安装时重置。Android 标识符具有不同的生命周期,但生命周期通常与 ID 的重置方式有关:

  • 仅限会话期间 - 每次用户重新启动应用时都使用新的 ID。
  • 安装重置 - 每次用户卸载并重新安装应用时都使用新的 ID。
  • FDR 重置 - 每次用户恢复设备出厂设置时都使用新的 ID。
  • FDR 持久性 - ID 在恢复出厂设置后保持不变。

重置性让用户能够创建与任何现有个人资料信息无关的新 ID。标识符持久存在得越久、越可靠(例如在恢复出厂设置后继续存在的标识符),用户被长期跟踪的风险就越高。如果应用重新安装时标识符被重置,即使没有显式用户控件可以在应用或系统设置内将其重置,这样做也能缩短其持续时间并提供一种重置 ID 的手段。

唯一性

唯一性可以确定发生冲突的可能性,而发生冲突是指在相关作用域内存在完全相同的标识符。在最高级别,全局唯一标识符永远不会有冲突项,即使在其他设备/应用上也是如此。唯一性级别取决于标识符的熵和用来创建标识符的随机性来源。例如,带有安装日期(例如 2019-03-01)的随机标识符的冲突几率要比带有 Unix 安装时间戳(例如 1551414181)的标识符高得多。

一般而言,您可以将用户帐号标识符视为具有唯一性。也就是说,每个设备/帐号组合都具有一个唯一 ID。另一方面,标识符在某一群体内的唯一性越低,隐私权保护效果就越好,因为它用于跟踪个别用户的有效性会降低。

完整性保护和不可否认性

您可以使用难以欺诈或重播的标识符证明关联的设备或帐号具有某些属性。例如,您可以证明设备并非被垃圾邮件制作者利用的虚拟设备。难以欺诈的标识符还能提供不可否认性。如果设备用密钥签署了一条消息,就难以辩称这条消息是由他人的设备发出的。不可否认性可能是用户需要的(例如,进行付款身份验证),也可能成为令人讨厌的属性(例如,用户会后悔发送某条消息)。

常见用例和适用的标识符

此部分为使用 IMEI 等硬件 ID 提供了替代方案。我们不建议您使用硬件 ID,因为用户无法重置它们,并且它们的作用域仅限于相应设备。在许多情况下,作用域仅限于应用的标识符就足以满足您的需求。

跟踪已注销用户的偏好设置

在此情况下,您要在服务器端保存每个设备的状态,而不使用用户帐号。

使用:实例 ID 或 GUID

为什么这样建议?

不建议让信息在重新安装后依然存在,因为用户可能想通过重新安装应用重置其偏好设置。

使用相同的签名密钥跟踪应用之间的已注销用户偏好设置

在此情况下,您要在服务器端保存每个设备的状态,并在同一设备上使用相同的密钥签名的不同应用之间传输此状态信息。

使用:SSAID

为什么这样建议?

在 Android 8.0(API 级别 26)及更高版本中,SSAID 提供了一个在由同一开发者签名密钥签名的应用之间通用的标识符。借助它,您可以在这些应用之间共享状态,而无需要求用户登录帐号。

跟踪已注销用户的行为

在此情况下,您已经根据用户在同一设备上不同应用/会话中的行为创建了他们的配置文件。

使用:广告 ID

为什么这样建议?

按照 Google Play 开发者内容政策,在广告用例中使用广告 ID 是强制性要求,因为用户可以对其进行重置。

生成已注销/匿名用户的分析数据

在此情况下,您需要衡量已注销或匿名用户的使用情况统计信息和分析数据。

使用:实例 ID;如果实例 ID 不足以满足需求,则使用 GUID

为什么这样建议?

实例 ID 或 GUID 的作用域为创建它的应用,这样可以防止他人利用该标识符跟踪用户在不同应用中的行为。此外,此标识符还可以轻松地进行重置,因为用户可以清除应用数据或重新安装应用。创建实例 ID 和 GUID 的过程简单明了:

  • 创建实例 ID:参阅 Firebase 云消息传递指南。
  • 创建 GUID:在您的应用中实现逻辑,如以下代码段所示:

    Kotlin

        val uniqueID: String = UUID.randomUUID().toString()
        

    Java

        String uniqueID = UUID.randomUUID().toString();
        

请注意,如果您已告知用户您要收集的是匿名数据,则应该确保不将标识符关联到 PII 或其他可能关联到 PII 的标识符。

您还可以使用 Google Analytics for Mobile Apps,它提供了一种按应用进行分析的解决方案。

跟踪已注销用户的转化

在此情况下,您需要通过跟踪转化情况来确认营销策略是否成功。

使用:广告 ID

为什么这样建议?

这是一种与广告有关的用例,需要使用在不同应用中均可用的 ID,因此使用广告 ID 是最合适的解决方案。

处理跨不同设备的多个安装

在此情况下,您需要在将应用安装到同一用户的多个设备上时识别此应用的正确实例。

使用:实例 ID 或 GUID

为什么这样建议?

实例 ID 明确针对这一用途而设计;其作用域被限定在应用范围,这样就无法被用于跟踪用户在不同应用中的行为,并且在应用重新安装后会重置。如果遇到实例 ID 无法满足需求的罕见情况,您还可以使用 GUID。

防欺诈:强制执行免费内容限制并检测女巫攻击

在此情况下,您希望限制用户在设备上能够查看的免费内容(例如文章)的数量。

使用:实例 ID 或 GUID。在 Android 8.0(API 级别 26)及更高版本中,您也可以选择 SSAID,因为其作用域仅限于应用签名密钥。

为什么这样建议?

如果使用 GUID 或实例 ID,用户要想规避内容限制就必须重新安装应用,这已足以让大多数人打消这样的念头。如果这样的保护还不够,还可以利用 Android 提供的 DRM API 限制对内容的访问,包括针对每个 APK 的标识符、Widevine ID。

运营商功能

在此情况下,您的应用要使用运营商帐号与设备的电话和短信功能进行交互。

使用:IMEI、IMSI 和 Line1

为什么这样建议?

如果运营商相关功能要求使用硬件标识符,则可以使用硬件标识符。例如,您可以使用这些标识符切换手机运营商/SIM 卡插槽,或者通过 IP(适用于 Line1)发送短信(基于 SIM 卡的用户帐号)。不过,对于非特权应用,我们建议您使用帐号登录在服务器端检索用户设备信息。这样做的其中一个原因是,在 Android 6.0(API 级别 23)及更高版本中,这些标识符只能通过运行时权限来使用。用户可能会关闭此权限,因此您的应用应妥善处理这些异常。

滥用检测:发现漫游器和 DDoS 攻击

在此情况下,您希望发现多台正在攻击后端服务的虚假设备。

使用:SafetyNet API

为什么这样建议?

单独的标识符并不能有效地说明设备的真伪。您可以利用 SafetyNet API 的 attest() 方法来验证发起请求的设备的完整性,借此验证请求来自真实的 Android 设备(而不是模拟器或仿冒其他设备的其他代码)。如需了解详细信息,请参阅 SafetyNet API 文档。

欺诈和滥用检测:检测重要被盗凭据

在此情况下,您希望发现是否在某一台设备上多次使用了重要被盗凭据(例如,进行欺诈性支付)。

使用:本质上来看,防欺诈需要专有信号,这些信号可以随着时间的推移而更改,因此超出了本文档的讨论范围。不过,请注意,您可以在已启用 root 权限的设备或模拟设备上轻松地修改硬件标识符(例如 IMEI 和 IMSI),因此它们并不是防欺诈的可靠标识。