作者 / Oscar Rodriguez, Developer Relations Engineer
我們近期發(fā)布了 Play Integrity API,希望幫助開發(fā)者們保護自己的應用和游戲,使其免受可能存在風險的欺詐性互動 (例如欺騙和未經(jīng)授權的訪問) 的危害,讓您能夠采取適當措施來防范攻擊并減少濫用行為。

除了與應用完整性、設備完整性和許可信息相關的有用信號外,Play Integrity API 還提供了一個簡單卻非常實用的功能,即 “nonce”。如果使用得當,開發(fā)者可以進一步加強 Play Integrity API 的現(xiàn)有保護措施,并降低特定類型攻擊的風險,例如中間人 (PITM) 篡改攻擊和重放攻擊。
在這篇文章中,我們將深入介紹什么是 nonce、它的工作原理,以及如何使用 nonce 字段來進一步保護您的應用和游戲。

什么是 nonce?

在密碼學和安全工程學中,nonce (number once) 是一個在安全通信中僅能被使用一次的數(shù)字。nonce 用途廣泛,如身份驗證、數(shù)據(jù)加密和哈希處理等。

在 Play Integrity API 中,nonce 是您在調(diào)用 API 完整性檢查前設置的不透明 Base64 編碼二進制 blob,并通過被簽名的響應中原樣返回。根據(jù)創(chuàng)建和驗證 nonce 的方式,您可以使用它來進一步加強 Play Integrity API 的現(xiàn)有保護措施,并緩解特定類型的攻擊,例如中間人 (PITM) 篡改攻擊和重放攻擊。

除了在被簽名的響應中按原樣返回 nonce,Play Integrity API 不會對 nonce 實際數(shù)據(jù)進行任何處理,因此您可以設置任意值,只要它是一個有效的 Base64 值即可。也就是說,為了對響應進行數(shù)字簽名,nonce 值將被發(fā)送到 Google 服務器,因此請勿將 nonce 設置為任何類型的個人身份信息 (PII),例如用戶姓名、電話或電子郵件地址。

設置 nonce

將您的應用設置為使用 Play Integrity API?之后,您可以使用?setNonce()?方法,或其適當?shù)淖凅w設置 nonce,這些變體適用于 API 的?Kotlin、Java、Unity 和 Native 版本。

Kotlin:?

val nonce: String = ...

// 創(chuàng)建 manager 的實例
val integrityManager =
IntegrityManagerFactory.create(applicationContext)

// 通過 nonce 獲取完整性令牌
val integrityTokenResponse: Task<IntegrityTokenResponse> =
integrityManager.requestIntegrityToken(
IntegrityTokenRequest.builder()
.setNonce(nonce) // 設置 nonce
.build())

Java:?

String nonce = ...

// 創(chuàng)建 manager 的實例
IntegrityManager integrityManager =
IntegrityManagerFactory.create(getApplicationContext());

// 通過 nonce 獲取完整性令牌
Task<IntegrityTokenResponse> integrityTokenResponse =
integrityManager
.requestIntegrityToken(
IntegrityTokenRequest.builder()
.setNonce(nonce) // 設置 nonce
.build());

Unity:?

string nonce = ...

// 創(chuàng)建 manager 的實例
var integrityManager = new IntegrityManager();

// 通過 nonce 獲取完整性令牌
var tokenRequest = new IntegrityTokenRequest(nonce);
var requestIntegrityTokenOperation =
integrityManager.RequestIntegrityToken(tokenRequest);

Native:?

// 創(chuàng)建 IntegrityTokenRequest 對象
const char* nonce = ...
IntegrityTokenRequest* request;
IntegrityTokenRequest_create(&request);
IntegrityTokenRequest_setNonce(request, nonce); // 設置 nonce
IntegrityTokenResponse* response;
IntegrityErrorCode error_code =
IntegrityManager_requestIntegrityToken(request, &response);

驗證 nonce

Play Integrity API 的響應以?JSON 網(wǎng)絡令牌 (JWT)?的形式返回,其負載為純文本 JSON,格式如下:?

您可以在?requestDetails?結(jié)構中查看 nonce,其格式如下:?

requestDetails: {
requestPackageName: "...",
nonce: "...",
timestampMillis: ...
}

nonce?字段的值應與您之前調(diào)用 API 傳過去的值完全匹配。此外,由于 nonce 值位于 Play Integrity API 的加密簽名響應中,收到響應之后是無法改變它的。通過這些屬性,您就可以使用 nonce 進一步保護您的應用。

保護重要操作

試想這個場景,一名攻擊者正在試圖惡意將玩家得分虛報給游戲服務端。這種情況下,設備和應用都是完整的,但攻擊者仍可以通過代理服務器或者虛擬專用網(wǎng)絡查看并修改與游戲服務器之間的通信數(shù)據(jù)流,從而達到虛報分數(shù)的目的。
在這種情況下,僅調(diào)用 Play Integrity API 不足以保護應用:?設備沒有被破解、應用也是合法的,因此該操作可以通過 Play Integrity API 的所有檢查。
但您可以使用 Play Integrity API 的 nonce 來保護這種報告游戲分數(shù)的特定高價值操作,即在 nonce 中編碼操作的值。實現(xiàn)方法如下:?

  1. 用戶發(fā)起重要操作;
  2. 應用準備好要保護的消息,例如 JSON 格式的消息;
  3. 應用計算要保護的消息的加密哈希值。例如,使用 SHA-256 或 SHA-3-256 哈希算法;
  4. 應用調(diào)用 Play Integrity API,并調(diào)用 setNonce() 以將 nonce 字段設置為在上一步計算的加密哈希值;
  5. 應用將要保護的消息以及 Play Integrity API 的簽名結(jié)果發(fā)送給服務器;
  6. 應用服務器驗證其收到的消息的加密哈希值是否與簽名結(jié)果中的 nonce 字段值匹配,并拒絕任何不匹配的結(jié)果。

下面的序列圖說明了相關步驟:?

只要受保護的原始消息與簽名結(jié)果一起發(fā)送,且服務器和客戶端都使用完全相同的機制來計算 nonce,通過這樣的方式來保證消息不會被篡改。
請注意,在上述場景下,安全模型的有效性僅限攻擊行為發(fā)生在網(wǎng)絡中 (而不是發(fā)生在設備或應用),因此驗證 Play Integrity API 提供的設備和應用完整性信號也尤為重要。

防范重放攻擊


我們再試想另外一種場景,一個應用或游戲使用了 Play Integrity API 來保護自己的 C/S 架構,但攻擊者試圖通過用已破解的設備與服務端交互,并且不讓服務器端監(jiān)測到。
若要 “達成” 這種攻擊目標,攻擊者會首先在合法的設備上讓應用與 Play Integrity API 進行交互,并獲得已經(jīng)簽名的響應內(nèi)容,然后再在破解設備上運行應用并攔截 Play Integrity API 的調(diào)用,使用此前記錄的、已獲得簽名的響應內(nèi)容進行響應,這樣一來就不會執(zhí)行完整性檢查了。
由于已簽名的響應并未以任何方式被更改,所以數(shù)字簽名看似正常,應用服務器就會誤以為它正在與合法設備進行通信。我們將此稱為重放攻擊。
抵御此類攻擊的第一道防線是驗證簽名響應中的?timestampMillis?字段。這個字段包含創(chuàng)建響應時的時間戳,即使在數(shù)字簽名通過驗證的情況下,也能用于服務器端檢測是否為可疑的舊響應。

也就是說,應用服務器也可以利用 Play Integrity API 中的 nonce,為每個響應分配一個唯一值,并驗證該響應是否與之前設置的唯一值匹配。實現(xiàn)方法如下: 

  1. 服務器以攻擊者無法預測的方式創(chuàng)建全局唯一值。例如,128 位或位數(shù)更多的加密安全隨機數(shù);
  2. 應用調(diào)用 Play Integrity API,并將 nonce 字段設置為應用服務器接收的唯一值;
  3. 應用將 Play Integrity API 的簽名結(jié)果發(fā)送到服務器;
  4. 服務器驗證簽名結(jié)果中的 nonce 字段是否與之前生成的唯一值匹配,并拒絕所有不匹配的結(jié)果。

下面的序列圖說明了相關步驟:?

實現(xiàn)上述流程后,每次服務器要求應用調(diào)用 Play Integrity API 時,它都會使用不同的全局唯一值,因此只要攻擊者無法預測該值,nonce 與預期值不匹配,就無法重用之前的響應。

結(jié)合兩種保護措施


雖然上述兩種機制的工作方式不同,但如果應用同時需要兩種保護,則可以將這兩種機制組合在一個 Play Integrity API 調(diào)用中,例如,將兩種保護措施的結(jié)果附加到一個更大的 Base64 nonce 中。結(jié)合兩種保護措施的實現(xiàn)方法如下:?

  1. 用戶發(fā)起重要操作;
  2. 應用要求服務器提供一個標識請求的唯一值;
  3. 應用服務器生成全局唯一值,防止攻擊者做出預測。例如,您可以使用加密安全的隨機數(shù)生成器創(chuàng)建此類值。我們建議創(chuàng)建不小于 128 位的值;
  4. 應用服務器向應用發(fā)送全局唯一值;
  5. 應用準備好要保護的消息,例如 JSON 格式的消息;
  6. 應用計算要保護的消息的加密哈希值。例如,使用 SHA-256 或 SHA-3-256 哈希算法;
  7. 應用通過附加從應用服務器收到的唯一值以及要保護的消息的哈希值來創(chuàng)建一個字符串;
  8. 應用調(diào)用 Play Integrity API,并調(diào)用 setNonce() 以將 nonce 字段設置為在上一步中創(chuàng)建的字符串;
  9. 應用將要保護的消息以及 Play Integrity API 的簽名結(jié)果發(fā)送給服務器;
  10. 應用服務器拆分 nonce 字段的值,然后驗證消息的加密哈希值以及之前生成的唯一值是否與預期值相匹配,并拒絕任何不匹配的結(jié)果。

下面的序列圖說明了相關步驟:?

如果您的應用會處理敏感數(shù)據(jù),或容易被濫用,我們建議您考慮借助 Play Integrity API,采取相關措施緩解威脅。

本文章轉(zhuǎn)載微信公眾號@Android 開發(fā)者

上一篇:

一文帶你了解大模型——智能體(Agent)

下一篇:

HTTP API 設計指南
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費