Reference version

This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.

AppIntegrity

一个库,提供对 Android 上 Google 的 Play Integrity API 和 iOS 上 Apple 的 App Attest 服务的访问。

Android
iOS
Included in Expo Go

@expo/app-integrity 提供了一组 API,帮助确保你的后端资源只能被运行在真实设备上的、你应用的合法安装实例访问。它在 Android 上使用 Google 的 Play Integrity APIs,在 iOS 上使用 Apple 的 App Attest service 来验证应用的真实性,帮助防止未授权客户端、被修改的应用或自动化脚本向你的服务器发起请求。

通常,@expo/app-integrity 帮助你的服务器区分以下两者:

  • 运行在 真实设备 上的你的 真实应用
  • 其他任何东西(被修改的应用、脚本、模拟器)

它通过使用平台推荐的应用证明服务来实现这一点。

安装

Terminal
npx expo install @expo/app-integrity

If you are installing this in an existing React Native app, make sure to install expo in your project.

Android 上的使用

@expo/app-integrity 使用 Play Integrity 的 标准请求流程 进行完整性检查。

配置

请参阅 Play Integrity 设置指南 了解如何在你的应用中启用完整性 API。

准备完整性令牌提供方(一次性)

在发起完整性检查请求之前,你需要先准备完整性令牌提供方。你可以在应用启动时执行此操作,或在需要完整性检查之前在后台完成。

import * as AppIntegrity from '@expo/app-integrity'; const cloudProjectNumber = 'your-cloud-project-number'; await AppIntegrity.prepareIntegrityTokenProviderAsync(cloudProjectNumber);

请求完整性令牌(按需)

每当你的应用发起需要检查真实性的服务器请求时,你都需要请求一个完整性令牌,并将其发送到应用的后端服务器进行解密和验证。之后,你的后端服务器可以决定如何处理。

const requestHash = '2cp24z...'; const result = await AppIntegrity.requestIntegrityCheckAsync(requestHash);

在调用 requestIntegrityCheckAsync 之前,请确保已成功调用 prepareIntegrityTokenProviderAsync

在此示例中,requestHash 是一个与正在验证的特定用户操作唯一对应的哈希值。你可以针对不同的用户操作,使用不同的哈希值多次调用 requestIntegrityCheckAsync

成功后,将结果发送到你的服务器进行验证。

解密并验证完整性判定结果

请参阅 Play Integrity 指南,以在服务器中验证完整性令牌。

其他资源

  • Google Play Integrity 文档:请参阅 Google 官方指南,了解为 @expo/app-integrity 提供支持的 API 和验证流程。

  • Play Integrity 标准请求流程:此页面介绍了用于完整性判定的标准 API 请求方式,该方式支持 Android 5.0(API 级别 21)及以上版本。每当你的应用发起服务器调用以检查交互是否真实时,都可以发起标准 API 请求来获取完整性判定结果。

  • 关于完整性判定结果:完整性判定结果会传达有关设备、应用和账号有效性的信息。你的应用服务器可以使用解密并验证后的判定载荷,来决定如何针对应用中的特定操作或请求采取最佳处理方式。

  • 处理错误代码:如果你的应用发起 Play Integrity API 请求但调用失败,应用会收到一个错误代码。这些错误可能由多种原因引起,例如网络连接较弱等环境问题、API 集成问题,或恶意活动和主动攻击。

iOS 上的使用

配置

在 Xcode 中,前往 Signing & Capabilities,点击 + Capability,添加 App Attest。Xcode 会自动为你的应用添加所需的 entitlement。

关于服务端的验证逻辑,请参阅 验证连接到你服务器的应用

检查设备是否支持应用证明

并非所有设备都可以使用 App Attest 服务,因此在访问该服务之前,让应用先进行兼容性检查非常重要。如果用户的应用未通过兼容性检查,请优雅地绕过该服务。你可以通过读取 isSupported 属性来检查可用性。

import * as AppIntegrity from '@expo/app-integrity'; if (AppIntegrity.isSupported) { // 执行密钥生成和证明。 } // 继续访问你的服务器 API。

创建密钥对

对于运行你应用的每台设备上的每个用户账户,通过调用 generateKey 方法生成一个唯一的、基于硬件的加密密钥对。

const keyId = await AppIntegrity.generateKeyAsync();

成功后,该方法会返回一个密钥标识符(keyId),你稍后会用它来访问该密钥。请将该标识符记录到持久存储中,因为没有标识符就无法使用该密钥,而且之后也无法再获取该标识符。设备会自动将关联的私钥存储在 Secure Enclave 中,App Attest 服务可以从那里使用它来创建签名,但任何进程都无法直接读取或修改它,从而确保其安全性。

不要在同一设备上为多个用户重复使用同一个密钥,因为这会削弱安全防护。尤其是,这会使得检测一种攻击变得困难:攻击者使用一台被攻破的设备,为运行你应用的多个远程用户提供服务。更多信息请参阅 评估欺诈风险

从服务器获取挑战值

向服务器请求一个唯一的一次性挑战值。此挑战值会嵌入到下面的证明步骤中,确保它不能被攻击者重复使用。挑战值至少应为 16 字节长,以提供足够的熵,使其难以被猜测。

将密钥对认证为有效

将前面步骤中从服务器创建的挑战值与 keyId 一起传递给 attestKey 方法,如下所示:

const attestationObject = await AppIntegrity.attestKeyAsync(keyId, challenge);

成功后,将接收到的 attestationObjectkeyId 发送到你的服务器进行验证。

如果该方法返回 ERR_APP_INTEGRITY_SERVER_UNAVAILABLE 错误,请稍后使用同一密钥再次尝试证明。对于任何其他错误,请丢弃该密钥标识符,并在你想再次尝试时创建一个新密钥。

如果服务器能够成功验证证明对象,则会认为该应用实例有效。在这种情况下,请务必在你的应用中持久化存储密钥标识符——而不是证明对象——以便将来为服务器请求签名。

为敏感请求生成断言

在成功验证某个密钥的证明后,服务器可以要求应用为未来的任意或全部服务器请求断言其合法性。应用通过对请求进行签名来实现这一点。在应用中,从服务器获取一个唯一的一次性挑战值。这里像证明一样使用挑战值,是为了避免重放攻击。

const challenge = 'A string from your server'; const request = { action: 'getGameLevel', levelId: '1234', challenge: challenge, }; const assertion = await AppIntegrity.generateAssertionAsync(keyId, JSON.stringify(request));

成功后,将断言对象连同客户端数据一起传递给服务器。如果断言对象验证失败,由你负责决定如何处理该请求。

使用一个密钥可以生成的断言次数没有限制。尽管如此,你通常会将断言保留给应用生命周期中处于敏感时刻的请求,例如应用下载高级内容时。

在重新安装后重新开始

你生成的密钥在正常应用更新期间仍然有效,但在应用重新安装、设备迁移或从备份恢复设备后不会保留。在这些情况下,你需要从头开始并生成一个新密钥。尽量将新密钥的生成限制在这些事件发生时,或新增用户时。保持设备上的密钥数量较少,有助于检测某些类型的欺诈行为。

其他资源

API

import * as AppIntegrity from '@expo/app-integrity';

Constants

AppIntegrity.isSupported

Only for:
iOS

Type: boolean

A boolean value that indicates whether a particular device provides the App Attest service. Not all device types support the App Attest service, so check for support before using the service.

Methods

AppIntegrity.attestKeyAsync(keyId, challenge)

Only for:
iOS

ParameterTypeDescription
keyIdstring

The identifier you received by calling the generateKey function.

challengestring

A challenge string from your server.


Asks Apple to attest to the validity of a generated cryptographic key.

Returns:
Promise<string>

A Promise that is fulfilled with a string that contains the attestation data. A statement from Apple about the validity of the key associated with keyId. Send this to your server for processing.

AppIntegrity.generateAssertionAsync(keyId, challenge)

Only for:
iOS

ParameterTypeDescription
keyIdstring

The identifier you received by calling the generateKey function.

challengestring

A string to be signed with the attested private key.


Creates a block of data that demonstrates the legitimacy of an instance of your app running on a device.

Returns:
Promise<string>

A Promise that is fulfilled with a string that contains the assertion object. A data structure that you send to your server for processing.

AppIntegrity.generateHardwareAttestedKeyAsync(keyAlias, challenge)

Only for:
Android

ParameterTypeDescription
keyAliasstring

A unique identifier for the key.

challengestring

A challenge string from your server.


Generates a hardware-attested key pair in the Android Keystore. This key can be used for attestation on GrapheneOS and other secure Android distributions.

Returns:
Promise<void>

A Promise that resolves when the key is generated successfully.

AppIntegrity.generateKeyAsync()

Only for:
iOS

Creates a new cryptographic key for use with the App Attest service.

Returns:
Promise<string>

A Promise that is fulfilled with a string that contains the key identifier. The key itself is stored securely in the Secure Enclave.

AppIntegrity.getAttestationCertificateChainAsync(keyAlias)

Only for:
Android

ParameterTypeDescription
keyAliasstring

The identifier of the key to get certificates for.


Retrieves the attestation certificate chain for a hardware-attested key. The certificate chain can be validated on your server to verify device integrity.

Returns:
Promise<string[]>

A Promise that is fulfilled with an array of base64-encoded X.509 certificates.

AppIntegrity.isHardwareAttestationSupportedAsync()

Only for:
Android

Checks if hardware attestation is supported on this device.

Returns:
Promise<boolean>

A Promise that is fulfilled with a boolean indicating support.

AppIntegrity.prepareIntegrityTokenProviderAsync(cloudProjectNumber)

Only for:
Android

ParameterTypeDescription
cloudProjectNumberstring

The cloud project number.


Prepares the integrity token provider for the given cloud project number.

Returns:
Promise<void>

A Promise that is fulfilled if the integrity token provider is prepared successfully.

AppIntegrity.requestIntegrityCheckAsync(requestHash)

Only for:
Android

ParameterTypeDescription
requestHashstring

A string representing the request hash.


Requests an integrity verdict for the given request hash from Google Play.

Returns:
Promise<string>

A Promise that is fulfilled with a string that contains the integrity check result.