SafetyNet Attestation API は、アプリが動作している Android デバイスをアプリのデベロッパーが評価するための不正利用防止 API です。この API は、不正利用検出システムの一部として、サーバーとやり取りしているのが正規の Android デバイスで動作している正規のアプリかどうかを判断するために使用します。
SafetyNet Attestation API は、デバイスの完全性を評価し、暗号技術を使って署名された構成証明を提供します。構成証明を作成するために、API はデバイスのソフトウェアとハードウェアの環境を検査して、完全性の問題がないか調べ、承認済みの Android デバイスの基準データと比較します。 生成される構成証明は、呼び出し側のアプリから与えられる nonce に紐付けされます。 また、この構成証明には、生成時のタイムスタンプと、リクエストするアプリに関するメタデータも含まれています。
以下にあげるものは、この API の設計で意図したユースケースではありません。
- スタンドアロンの不正利用防止あるいはアプリ セキュリティの仕組みとして機能させる。公開されているアプリ セキュリティのおすすめの方法、およびプロダクト固有の不正利用防止情報と組み合わせて利用してください。
- デバイスがインターネットに接続されていないときに機能させる。この場合、API はエラーを返します。
- レスポンスを呼び出し元のアプリで直接解釈させる。すべての不正利用防止の判断ロジックをご自身の管理下にあるサーバーに移動してください。
- システムの変更に関する詳細な情報を得る。この API から取得できるのは、さまざまなレベルのシステム完全性をブール値で表したものです。
- デバイス固有 ID、GPS エミュレーション ステータス、画面ロックのステータスなど、アプリ固有のユースケースに関する情報を含める。
- 強力な DRM 検査を置き換える、または実装する。
- デバイスがルート権限取得されているかどうかのみをチェックさせる。この API はデバイスの全体的な完全性をチェックするように設計されているためです。
概要
SafetyNet Attestation API では、次のワークフローが使用されます。
- SafetyNet Attestation API がアプリからの呼び出しを受けます。この呼び出しには nonce が含まれます。
- SafetyNet Attestation サービスは、ランタイム環境を評価し、Google のサーバーに評価結果の署名済み構成証明をリクエストします。
- Google のサーバーは、署名済み構成証明をデバイスの SafetyNet Attestation サービスに送信します。
- SafetyNet Attestation サービスは、この署名済み構成証明をアプリに返します。
- アプリは署名済み構成証明をご自身のサーバーに転送します。
- このサーバーはレスポンスを検証し、不正利用防止の判断に使用します。ご自身のサーバーが調査結果をアプリに伝えます。
このプロセスを図 1 に示します。

注: 追加のドキュメントとチェックリスト
SafetyNet Attestation API の初期化、設定、有効化では、このメインのドキュメントに加えて、以下のアドバイスを念頭に置いてください。
API キーを取得する
SafetyNet Attestation API のメソッドを呼び出すには、API キーを使用する必要があります。キーを作成して埋め込む手順は次のとおりです。
- Google API Console のライブラリ ページに移動します。
- 「Android Device Verification API」を検索して選択します。Android Device Verification API ダッシュボードの画面が表示されます。
- API がまだ有効になっていない場合は、[有効にする] をクリックします。
- [認証情報を作成] ボタンが表示された場合は、それをクリックして API キーを生成します。表示されなかった場合は、[すべての API 認証情報] プルダウン リストをクリックしてから、Android Device Verification API を有効にしたプロジェクトに関連付けられている API キーを選択します。
- 左のサイドバーで、[認証情報] をクリックします。表示された [API キー] をコピーします。
- この API キーは、
SafetyNetClient
クラスのattest()
メソッドを呼び出すときに使用します。
この API キーを作成したら、SafetyNet API Clients メーリング リストに参加してください。
API の割り当てとモニタリング
重要: SafetyNet Attestation API の呼び出しの割り当ては、デフォルトで 1 日あたり 10,000 リクエストです。
アプリで割り当てを増やす必要がある場合は、ユーザーにアプリをデプロイする前にリクエストしてください。方法は次のとおりです。
- この API に関するドキュメントを確認してください。推奨されるベスト プラクティスに従っていないリクエストは却下される可能性があります。
- 割り当てリクエスト フォームに入力します。
- リクエストが処理されたことを示す確認メールが届くまでお待ちください。通常、リクエストは 2~3 営業日以内に処理されます。
注: プロジェクトにプロビジョニングされた割り当てにかかわらず、個々のアプリ インスタンスは 1 分あたり最大 5 リクエストまでに抑制されます。この上限を超えると、その 1 分間の残りのリクエストはすべてエラーを返します。
アプリの再試行メカニズムを実装する際は、この動作を念頭に置いてください。
アプリの API 割り当てにかかわらず、割り当てのモニタリングとアラートのセットアップをおすすめします。
Google Play 開発者サービスのバージョンを確認する
SafetyNet Attestation API を使用する前に、正しいバージョンの Google Play 開発者サービスがユーザーのデバイスにインストールされていることを確認する必要があります。誤ったバージョンがインストールされていると、API の呼び出し後にアプリが応答しなくなります。誤ったバージョンがインストールされていることがアプリで検出された場合は、デバイスの Google Play 開発者サービスアプリを更新するようユーザーに依頼してください。
Google Play 開発者サービスのバージョンが、使用している Android SDK のバージョンと互換性があるかどうかを確認するには、次のコード スニペットに示すように、isGooglePlayServicesAvailable()
メソッドを呼び出します。
if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)
== ConnectionResult.SUCCESS) {
// The SafetyNet Attestation API is available.
} else {
// Prompt user to update Google Play services.
}
Google Play 開発者サービス v13.0 以降を搭載するデバイスでは、SafetyNet Attestation API はアプリ制限のある API キーもサポートしています。この機能を使用すると、割り当て制限のある API キーが誤って使用されるリスク、および不正に使用されるリスクが軽減されます。このオプション機能を使用するには、次のコード スニペットに示すように、デバイスの Google Play 開発者サービスの最小バージョンが v13.0 以降であることを確認します。
if (GoogleApiAvailability.getInstance()
.isGooglePlayServicesAvailable(context, 13000000) ==
ConnectionResult.SUCCESS) {
// The SafetyNet Attestation API is available.
} else {
// Prompt user to update Google Play Services.
}
SafetyNet 構成証明をリクエストする
Google API Console で Android Device Verification API 用の有効な API キーを取得すると、アプリで SafetyNet Attestation サービスを使用できるようになります。手順は次のとおりです。
- nonce を取得します。
- SafetyNet 構成証明をリクエストします。
- レスポンスをサーバーに転送します。
- サーバー上のレスポンスと他の不正利用防止情報を使用して、アプリの動作を管理します。
アプリの応答性を維持するには、アプリのメイン実行スレッドの外でこれらの手順を実行してください。別の実行スレッドを作成する方法については、複数スレッドへの処理の分散をご覧ください。
アプリ内のログインや購入イベント、新しいアプリ内アイテムの取得などの重要な操作をすべて保護するために、このチェックを行う必要があります。しかし、SafetyNet Attestation API を呼び出すと、レイテンシ、モバイルデータ使用量、および電池使用量が増加するため、セキュリティと使いやすさのバランスをとることは理にかなっています。たとえば、ログイン時に SafetyNet 構成証明をリクエストし、30 分ごとに再確認することもできます。また、アプリが構成証明を要求するタイミングをサーバーに決定させて、攻撃者がチェックのタイミングを予測しにくくすることもできます。
nonce を取得する
SafetyNet Attestation API を呼び出すときは、nonce を渡す必要があります。結果として得られる構成証明にはこの nonce が含まれており、構成証明がこの API 呼び出しの結果であって、攻撃者によるリプレイではないことがわかります。
SafetyNet リクエストで使用される nonce は 16 バイト以上である必要があります。nonce に変化を持たせ、同じ nonce を 2 回使用しないようにします。サーバーに送信されたデータから nonce の一部を導出することをおすすめします。たとえば、ユーザー名のハッシュをリクエストのタイムスタンプと連結して nonce を作成するなどです。
重要: nonce には、できるだけ多くのデータを入れてください。そうすることで、リプレイ攻撃が難しくなります。たとえば、ユーザー名から nonce を導出すると、リプレイ攻撃は同じアカウントに制限されます。しかし、購入イベントのすべての詳細から nonce を導出すると、API のレスポンス メッセージはその購入イベント以外で使用できなくなります。
API から署名済みのレスポンスを受け取ったら、署名済みのレスポンスの nonce と、サーバーに送信されたメッセージの残りから再現した nonce の比較を常に行います。このチェックにより、攻撃者が正規のデバイスから収集した署名済みの証明書を再利用して、悪意のあるリクエストを作成することができなくなります。
暗号機能の使用方法の詳細については、暗号技術の使用方法に関するガイドをご覧ください。
構成証明をリクエストする
Google Play 開発者サービスへの接続が確立され、nonce が作成されると、SafetyNet 構成証明リクエストを発行できるようになります。リクエストに対するレスポンスはすぐには返ってこないため、サービスからのレスポンスを処理するコールバック リスナーをセットアップすることをおすすめします。リスナーの例を次のコード スニペットに示します。
Kotlin
SafetyNet.getClient(this).attest(nonce, API_KEY) .addOnSuccessListener(this) { // Indicates communication with the service was successful. // Use response.getJwsResult() to get the result data. } .addOnFailureListener(this) { e -> // An error occurred while communicating with the service. if (e is ApiException) { // An error with the Google Play services API contains some // additional details. val apiException = e as ApiException // You can retrieve the status code using the // apiException.statusCode property. } else { // A different, unknown type of error occurred. Log.d(FragmentActivity.TAG, "Error: " + e.message) } }
Java
// The nonce should be at least 16 bytes in length. // You must generate the value of API_KEY in the Google APIs dashboard. SafetyNet.getClient(this).attest(nonce, API_KEY) .addOnSuccessListener(this, new OnSuccessListener<SafetyNetApi.AttestationResponse>() { @Override public void onSuccess(SafetyNetApi.AttestationResponse response) { // Indicates communication with the service was successful. // Use response.getJwsResult() to get the result data. } }) .addOnFailureListener(this, new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // An error occurred while communicating with the service. if (e instanceof ApiException) { // An error with the Google Play services API contains some // additional details. ApiException apiException = (ApiException) e; // You can retrieve the status code using the // apiException.getStatusCode() method. } else { // A different, unknown type of error occurred. Log.d(TAG, "Error: " + e.getMessage()); } } });
onSuccess()
メソッドは、サービスとの通信が成功したことを示しますが、デバイスが SafetyNet 構成証明に合格したかどうかはわかりません。次のセクションでは、構成証明の結果を読み取って、完全性を検証する方法について説明します。
SafetyNet 構成証明レスポンスをサーバーに転送する
アプリが SafetyNet と通信するとき、このサービスは、SafetyNet 構成証明の結果に加えて、メッセージの完全性を検証するための追加情報をレスポンスに入れて返します。この結果は、SafetyNetApi.AttestationResponse
オブジェクトとして提供されます。このオブジェクトの getJwsResult()
メソッドを使用して、リクエストのデータを取得します。レスポンスは JSON Web Signature(JWS)の形式になっています。
検証と使用のために、JWS オブジェクトをサーバーに送り返します。
サーバーで SafetyNet 構成認証レスポンスを使用する
次の JWS の抜粋は、ペイロード データの形式と内容のサンプルを示しています。
{
"timestampMs": 9860437986543,
"nonce": "R2Rra24fVm5xa2Mg",
"apkPackageName": "com.package.name.of.requesting.app",
"apkCertificateDigestSha256": ["base64 encoded, SHA-256 hash of the
certificate used to sign requesting app"],
"ctsProfileMatch": true,
"basicIntegrity": true,
}
通常、署名済みの構成証明のペイロードには、次のフィールドがあります。
レスポンスのタイムスタンプ
timestampMs
: JWS レスポンス メッセージが Google のサーバーによって生成された時刻を、UNIX エポックからの経過時間(ミリ秒単位)で表したもの。
呼び出し元のアプリから与えられたデータ
nonce
: 呼び出し元のアプリが API に渡した使い捨てトークン。
呼び出し元のアプリに関するデータ
apkPackageName
: 呼び出し元のアプリのパッケージ名。apkCertificateDigestSha256
: 呼び出し元のアプリの署名証明書に SHA-256 ハッシュをかけて Base-64 エンコードしたもの。
完全性判定の結果
ctsProfileMatch
: デバイスの完全性に関する厳密な判定結果。ctsProfileMatch
の値がtrue
の場合、アプリを実行しているデバイスのプロファイルは、Android 互換性テストに合格したデバイスのプロファイルと一致しています。basicIntegrity
: デバイスの完全性に対する緩やかな判定結果。basicIntegrity
の値のみがtrue
の場合、アプリを実行しているデバイスは、おそらく改ざんされていません。ただし、デバイスが Android の互換性テストに合格していない可能性はあります。Android 互換性テストの詳細については、Android デバイスの設計と互換性テストスイート(CTS)をご覧ください。
省略可能項目
error
: 現在の API リクエストに関連するエラー情報をエンコードしたもの。advice
: デバイスを適切な状態に戻す方法の提案。
考えられる完全性判定の結果
JWS メッセージには、デバイスの互換性チェックの結果を表す 2 つのパラメータ(ctsProfileMatch
と basicIntegrity
)が含まれています。表 1 に示すように、アプリが動作しているデバイスのステータスは、それぞれのパラメータの値に影響します。
表 1. basicIntegrity
と ctsProfileMatch
の値に影響するデバイス ステータスの例
デバイス ステータス | ctsProfileMatch の値 |
basicIntegrity の値 |
---|---|---|
CTS に合格した認証済みの正規デバイス | true |
true |
ブートローダーがロック解除されている認証済みデバイス | false |
true |
正規だが認証されていないデバイス(製造者が認証を申請していないなど) | false |
true |
カスタム ROM を搭載したデバイス(ルート権限取得されていないデバイス) | false |
true |
エミュレータ | false |
false |
デバイスがない(プロトコルをエミュレートするスクリプトなど) | false |
false |
システムの完全性が侵害されている兆候(ルート権限取得など) | false |
false |
その他のアクティブな攻撃の兆候(API フックなど) | false |
false |
エラーとなるケース
JWS メッセージには、次のようなエラー状態が示される場合もあります。
- 結果が
null
の場合、サービスへの呼び出しが正常に完了しなかったことを示します。 - JWS の error パラメータは、ネットワーク エラーや攻撃者が偽造したエラーなどの問題が発生したことを示します。ほとんどのエラーは一時的なもので、もう一度サービスを呼び出すとエラーがなくなるはずです。時間間隔を広げながら再試行することをおすすめします。
- デバイスが改変されている場合、つまり、レスポンスの basicIntegrity が false に設定されている場合、
apkPackageName
やapkCertificateDigestSha256
など、呼び出し元のアプリに関するデータは判定結果に含まれない場合があります。これは、システムが呼び出し元のアプリを確実に判断できない場合に発生します。
署名済み構成証明がエラーを示している場合の対処方法
- 再試行する。正規のデバイスで発生したエラーは一時的なものであり、再度サービスを呼び出せばなくなるはずです。
- 対象のデバイスで、アプリが 1 秒間に 5 回を超えて API を呼び出していないこと、およびプロジェクトの API 割り当てが残っていることを確認する。
- 意図的にエラーケースをトリガーして自分のアクティビティを偽装している攻撃者だと見なす。
将来のチェックに合格するためのアドバイス
advice
パラメータは、それが存在する場合、SafetyNet Attestation API が特定の結果で ctsProfileMatch
または basicIntegrity
のいずれかを false に設定する理由を説明する情報を示します。このパラメータの値には、次のような文字列のリストが入っています。
{"advice": "LOCK_BOOTLOADER,RESTORE_TO_FACTORY_ROM"}
アプリでは、advice パラメータの値を次に示すようなユーザーが理解しやすいメッセージに翻訳して、将来の SafetyNet 構成証明に合格するためのヒントをユーザーに提示できます。
LOCK_BOOTLOADER
- デバイスのブートローダーをロックする必要があります。
RESTORE_TO_FACTORY_ROM
- デバイスの ROM を工場出荷時の状態に戻す必要があります。
SafetyNet 構成証明のレスポンスを検証する
いくつかの手順を踏んで、SafetyNet 構成証明のレスポンスが本当に SafetyNet サービスから送信され、リクエストに一致するデータが含まれていることを確認する必要があります。
次の手順により、JWS メッセージの送信元を確認します。
- SSL 証明書チェーンを JWS メッセージから抽出します。
- SSL 証明書チェーンを検証し、SSL のホスト名マッチングを使用して、リーフ証明書がホスト名
attest.android.com
に対して発行されていることを確認します。 - 証明書を使用して、JWS メッセージの署名を検証します。
- JWS メッセージのデータが元のリクエスト内のデータと一致していることを確認します。具体的には、タイムスタンプが検証済みで、nonce、パッケージ名、アプリの署名証明書のハッシュが期待どおりの値になっていることを確認します。
GitHub の android-play-safetynet のサンプル API の使用法にあるような標準的な暗号ソリューションを使用して、JWS ステートメントを検証する必要があります。
最初のテストと開発(製品版以外)では、オンライン API を呼び出して JWS ステートメントの署名を検証できます。このプロセスは、GitHub の android-play-safetynet サンプル API の使用方法にも記載されています。オンラインの検証 API は初期段階のテスト専用であり、1 日あたり 10,000 リクエストの固定割り当てがあります。
重要: オンラインの検証 API は、JWS メッセージが SafetyNet Attestation API のサーバーによって署名されたことの検証にのみ使用します。このオンライン API は、ペイロード内のフィールドが、サービスが期待する値と一致するかどうかを確認できません。
予期しないケースに対する備え
変更および機能停止を考慮した使用方法をおすすめします。
- API の変更
- 判定結果に新しい(試験運用版)フィールドが現れることは常にあり得ます。追加されたフィールドによりパーサーや使用ロジックが誤動作しないようにしてください。特に、SafetyNet API Clients メーリング リストでアナウンスされていない試験運用版のフィールドに依存しないでください。
- SafetyNet Attestation API のダウン
万が一、SafetyNet Attestation API が使用できない場合、この API のユーザーは、可用性、およびこの API とそのレスポンスの品質への依存を動的に制御する機能をサーバー側で構築することを強くおすすめします。
典型的な戦略として、アプリがこの API を呼び出さないように動的に指示する機能の実装、および特定のデバイスやユーザーに対する SafetyNet Attestation API の結果を無視するためのデバイスおよびユーザーのホワイトリストの作成があります。
サンプルコード
SafetyNet API の使用について詳しくは、GitHub にあるサンプルコードをご覧ください。
アナウンス用メーリング リスト
SafetyNet API Clients メーリング リストに参加して、SafetyNet Attestation API の最新情報を入手することを強くおすすめします。
フィードバック
この API についてのフィードバックをお寄せください。頂いたフィードバックは、この API の新機能に優先的に適用されます。
詳細
SafetyNet Attestation API を使用する際のおすすめの方法については、次のリンクをご覧ください。
追加利用規約
SafetyNet API にアクセス、またはこれを使用すると、Google API 利用規約と追加規約に同意したことになります。 適用されるすべての利用規約とポリシーを確認し、理解したうえで API にアクセスしてください。
SafetyNet 利用規約
現場での計測で大量に収集したデータと同様に、偽陽性と偽陰性の両方の可能性があります。Google は、理解しうる限りデータを提示しています。Google は、検出メカニズムを広範囲にテストして正確性を確保し、これを維持するために改善し続けるよう努めています。
お客様は、適用される法律、規制および第三者の権利(データまたはソフトウェアの輸出入に関連する法律、プライバシーに関連する法律、および現地の法律を含むがこれらに限定されない)を遵守することに同意するものとします。お客様は、違法行為あるいは第三者の権利の侵害を促進または助長するために API を使用することはないものとします。お客様は、Google(またはその関連会社)のその他の利用規約に違反することはないものとします。
お客様は、SafetyNet API が、ハードウェアやソフトウェアの情報(デバイスやアプリケーションのデータ、SafetyNet 構成証明の結果など)を収集し、分析のためにそのデータを Google に送信することにより動作することを理解し、了承するものとします。 Google API 利用規約の第 3 条(d)に基づき、この API を利用するにあたり、サイト管理者の責任において、これらのデータの収集および Google との共有に関して必要な告知を行い、ユーザーの同意を得ることに合意するものとします。