唯一标识符最佳做法

尽管您的应用需要识别设备而非应用实例或通过身份验证的设备用户有着各种正当理由,但对绝大多数应用而言,最终目标都是识别应用的特定安装(而不是实际的物理设备)。

幸运的是,在 Android 上识别安装的方式简单明了,可以使用实例 ID,也可以在安装时创建自己的 GUID。本文提供了根据您的用例为您的应用选择合适标识符的指导。

要概括了解 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 6.0+ 中的标识符

MAC 地址具有全局唯一性,无法由用户重置,在恢复出厂设置后也不会发生变化。一般不建议使用 MAC 地址进行任何形式的用户标识。因此,从 Android M 开始,无法再通过第三方 API 获得本地设备 MAC 地址(例如,WLAN 和蓝牙)。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 链接起来,对用户重置进行桥接。Google Play 开发者内容政策规定:

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

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

...您必须遵循用户的“Opt out of interest-based advertising”或“Opt out of Ads Personalization”设置。如果用户已启用此设置,您不得出于广告目的而使用广告标识符创建用户个人资料,也不得使用广告标识符向用户投放个性化广告。允许的活动包括:内容相关广告定位、频次上限、转化跟踪、生成报表以及安全性和欺诈检测。

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

此外,还要注意,Google Play 开发者内容政策要求广告 ID“在未获得用户明确许可的情况下,不得与个人身份信息或任何永久性设备标识符(例如:SSAID、MAC 地址、IMEI 等)建立关联。”

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

timestamp ad_id account_id clickid

TABLE-01

account_id name dob country

TABLE-02

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

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

timestamp ad_id clickid dev_model

TABLE-01

timestamp demo account_id dev_model name

TABLE-02

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

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

其他解决方案包括:

  • 设计表时不在 PII 与广告 ID 之间建立明确关联。在上面的第一个示例中,这意味着 TABLE-01 中不能包括 account_id 列。
  • 对于同时拥有广告 ID 键控数据和 PII 访问权限的用户或角色,隔离并监控其访问控制列表。如果同时访问两个来源(例如,在两个表之间建立关联)的权限得到严格控制和审查,就能降低广告 ID 与 PII 之间关联的风险。一般而言,控制访问意味着:
    1. 断开广告客户 ID 键控数据与 PII 访问控制列表 (ACL) 之间的关联,以尽量减少同时存在于两个 ACL 中的人员或角色的数量;并
    2. 实现访问日志记录和审计,以检测和管理此规则的任何例外情况。

如需了解有关负责任地使用广告 ID 的详细信息,请参阅广告 ID 帮助中心文章。

使用实例 ID 和 GUID

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

因此,与无法重置的设备作用域硬件 ID 相比,实例 ID 具有更好的隐私权属性。它们还自带用于消息签名(及类似操作)的键对,并且在 Android、iOS 和 Chrome 上均可使用。如需了解详细信息,请参阅什么是实例 ID?帮助中心文档。

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

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

由于该标识符具有全局唯一性,您可以使用它来标识特定应用实例。为了避免与跨应用关联标识符有关的问题,应将 GUID 存储在内部存储空间而不是外部(共享)存储设备内。如需了解详细信息,请参阅存储选项指南。

了解标识符特性

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

作用域

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

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

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

重置性与持久性

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

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

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

唯一性

唯一性表示在关联作用域内存在完全相同标识符的几率。在最高级别,全局唯一标识符永远不会有冲突项,即使在其他设备/应用上也是如此。唯一性级别取决于标识符的大小和用来创建它的随机性来源。例如,带有安装日期(例如 2015-01-05)的随机标识符的冲突几率要比带有 Unix 安装时间戳(例如 1445530977)的标识符高得多。

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

完整性保护和不可否认性

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

常见用例和适用标识符

此部分为大多数用例提供了使用 IMEI 或 SSAID 等硬件 ID 的替代方案。我们不建议依赖硬件 ID,因为用户无法重置它们,并且对这些 ID 集合的控制力通常也十分有限。

跟踪已注销用户的首选项

在此情况下,您要在服务器端保存每个设备的状态。

我们建议:实例 ID 或 GUID。

为何这样建议?

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

跟踪已注销用户的行为

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

我们建议:广告 ID。

为何这样建议?

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

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

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

我们建议:实例 ID;如果实例 ID 无法满足需要,您还可以使用 GUID。

为何这样建议?

实例 ID 或 GUID 的作用域为创建它的应用,这样可以防止他人利用它们跟踪用户在不同应用中的行为。此外,您还可以通过清除应用数据或重新安装应用方便地对其进行重置。创建实例 ID 和 GUID 的过程简单明了:

  • 创建实例 ID:String iid = InstanceID.getInstance(context).getId()
  • 创建 GUID:String uniqueID = UUID.randomUUID().toString

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

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

跟踪已注销用户的转化

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

我们建议:广告 ID。

为何这样建议?

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

处理多处安装

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

我们建议:实例 ID 或 GUID。

为何这样建议?

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

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

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

我们建议:实例 ID 或 GUID。

为何这样建议?

如果使用 GUID 或实例 ID,用户要想克服内容限制就必须重新安装应用,这已足以让大多数人打消这样的念头。如果这样的保护还不够,还可以利用 Android 提供的 DRM API 来限制对内容的访问。

管理电话和运营商功能

在此情况下,您的应用要与设备的电话和短信功能进行互动。

我们建议:IMEI、IMSI 和 Line1(需要 Android 6.0(API 级别 23)及更高版本中的 PHONE 权限组)。

为何这样建议?

如果使用电话/运营商相关功能有相应要求,则可以利用硬件标识符;例如,在切换手机运营商/SIM 卡插槽或通过 IP(适用于 Line1)发送短信时 - 基于 SIM 卡的用户帐户。但必须注意,在 Android 6.0+ 中,这些标识符只能通过运行时权限使用,并且用户可能会关闭该权限,因此您的应用应妥善处理这些异常。

滥用检测:发现自动程序和 DDoS 攻击

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

我们建议:Safetynet API。

为何这样建议?

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

滥用检测:检测高价值被盗凭据

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

我们建议:IMEI/IMSI(需要 Android 6.0(API 级别 23)及更高版本中的 PHONE 权限组)。

为何这样建议?

通过被盗凭据,您可以利用设备将多个高价值被盗凭据(如令牌化信用卡)兑现。在这些情形下,软件 ID 可能会被重置以规避检测,因此可以使用硬件标识符。