
challenge_nonceを生成し、アプリクライアントに送信します。このチャレンジノンスは、プロセスの重要な要素です。これはサーバー側で生成されるものであり、リプレイ攻撃を防ぐためには同じチャレンジノンスを再利用しないことが重要です。Packages/Meta XR Platform SDK/Scripts/Platform.csファイルにGetIntegrityToken(string challenge_nonce)APIが定義されているのが分かります。using Oculus.Platform;
using System.Security.Cryptography;
// The "challenge_nonce" parameter must be a non-wrapping Base64URL-encoded
// string, formed from a random byte array by the application server.
string GetChallengeNonceFromAppServer()
{
byte[] randomBytes = new byte[16];
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(randomBytes);
}
// Convert to Base64 string
string base64Nonce = Convert.ToBase64String(randomBytes);
// Replace standard Base64 characters to make it URL safe
string challenge_nonce = base64Nonce.Replace('+', '-').Replace('/', '_');
// The length of the challenge_nonce must range from 22 to 172 characters.
return challenge_nonce;
}
void SampleCode()
{
string challenge_nonce = GetChallengeNonceFromAppServer();
Debug.Log("Calling the Attestation API");
DeviceApplicationIntegrity
.GetIntegrityToken(challenge_nonce)
.OnComplete(GetIngegrityTokenCallback);
}
void GetIngegrityTokenCallback(Message<string> msg)
{
if (msg.IsError)
{
Debug.Log("GetAttestationToken() Error: " + msg.GetError().Message);
}
else
{
String attestation_token = msg.Data;
// Send the Attestation token to the app server for token verification
}
}
OC|App_ID|App_Secretの形式を使用します。{access_token}パラメーターをそのアクセストークンに置き換え、{attestation_token}パラメーターを検証対象のアプリクライアントから受け取った構成証明トークンに置き換えます。https://graph.oculus.com/platform_integrity/verify?token=<attestation_token>&access_token=<access_token>
{
"data": [
{
"message": "success",
"claims": "<base64url_encoded_claim>"
}
]
}
{base64url_encoded_claim}は、認証済み構成証明トークンのクレームを表すBase64URLエンコード文字列です。 {
"data": [
{
"message": "invalid signature"
}
]
}
{
"data": [
{
"message": "token expired"
}
]
}
{
"request_details": {
"exp": 1684606153,
"nonce": "JMxMPp1H6kCxGzPRsjKFLw==",
"timestamp": 1684519753
},
"app_state": {
"app_integrity_state": "NotEvaluated",
"package_cert_sha256_digest": [
"c8a2e9bccf597c2fb6dc66bee293fc13f2fc47ec77bc6b2b0d52c11f51192ab8"
],
"package_id": "com.example.name123",
"version": "1"
}
"device_state": {
"device_integrity_state": "NotTrusted",
"unique_id": "cd81cd7e7e0525f99442ab6392117e89759e8872852de0495aa58f22bd8a1253",
}
}
DeviceApplicationIntegrity.GetIntegrityToken(string challenge_nonce) APIを呼び出す際に呼び出し元が提供したchallenge_nonceと全く同じ文字列です。Replay Attack (リプレイ攻撃)とは、攻撃者が有効なトークンを傍受し、その後のリクエストで再利用するセキュリティ侵害のことです。これは、トークンが不正利用された場合によく発生します。これに対処するため、システムはノンス検証を利用します。DeviceApplicationIntegrity.GetIntegrityToken(string challenge_nonce) APIに渡されたchallenge_nonceとトークンのクレームに返されるnonce thatが一致していることを検証することが重要です。これらが同一でない場合、トークンが再利用されている可能性があります。この場合、アプリサーバーはこのトークンを無効なものとして扱い、対象クライアントに対するアプリ固有のサービスを拒否する必要があります。また、アプリサーバーはノンスを1回のみ使用できるようにすることが非常に重要です。同じノンスを何度も再利用すると、リプレイ攻撃が発生する可能性があります。システムの安全性を確保し、潜在的なリプレイ攻撃を防ぐため、再利用はせずに、使用ごとに一意のノンスを生成する必要があります。app_stateには、アプリの構成証明情報が含まれます。各フィールドの詳細は以下のとおりです。StoreRecognized、不正インストールの場合はNotRecognized、識別不能なアプリの場合はNotEvaluatedが返されます。com.example.name123です。device_stateのセクションは、デバイスの構成証明についての情報を提供します。各フィールドの詳しい説明を以下に示します。device_integrity_state: このフィールドは、Meta Questデバイスの信頼性に関するインサイトを提供します。指定可能な値は次の3つです。
Advanced: デバイスのブートローダーがロックされており、起動されたAndroid OSは有効なシステムイメージであり、システムへの不正アクセスや改ざんの兆候は確認されていない。Basic: デバイスのブートローダーはロックされており、起動されたAndroid OSは有効なシステムイメージとなっているが、他のシステム整合性チェックの一部に失敗している。これは、起動以降にシステムが侵害された可能性を示唆するもので、例えばルート化されている場合などが該当します。NotTrusted: デバイスのブートローダーがロック解除されているか、起動したAndroid OSイメージが有効なシステムイメージではない。unique_id: この識別子はアプリごとにユニークであり、同じデバイスの異なるアプリが確実に別個のIDを持つようにします。ランダムな文字列として生成され、特定のアプリのためにデバイスをユニークに識別するものとなります。ユーザーのプライバシーを保護し、長期にわたるデバイストラッキングを阻止するため、unique_idは30日ごとに更新されます。しかし、このほぼ1か月の期間内であれば、アプリはこのIDをハードウェア禁止行為検出のために活用することができます。
is_banned:trueがあるかどうかがチェックされます。
{
"request_details": {
"exp": 1684606153,
"nonce": "JMxMPp1H6kCxGzPRsjKFLw==",
"timestamp": 1684519753
},
"app_state": {
"app_integrity_state": "StoreRecognized",
"package_cert_sha256_digest": [
"c8a2e9bccf597c2fb6dc66bee293fc13f2fc47ec77bc6b2b0d52c11f51192ab8"
],
"package_id": "com.example.name123",
"version": "1"
}
"device_state": {
"device_integrity_state": "Advanced",
"unique_id": "442ab6392117e89759e8872852de0495aa58f22bd8a1253123123",
}
}
{
"request_details": {
"exp": 1684606153,
"nonce": "JMxMPp1H6kCxGzPRsjKFLw==",
"timestamp": 1684519753
},
"app_state": {
"app_integrity_state": "StoreRecognized",
"package_cert_sha256_digest": [
"c8a2e9bccf597c2fb6dc66bee293fc13f2fc47ec77bc6b2b0d52c11f51192ab8"
],
"package_id": "com.example.name123",
"version": "1"
}
"device_state": {
"device_integrity_state": "Advanced",
"unique_id": "442ab6392117e89759e8872852de0495aa58f22bd8a1253123123",
}
"device_ban": {
"is_banned": true
"remaining_ban_time": 34 days
}
}
is_banned:falseを設定してデバイスのブロックを撤回することができます。ブロックの期間を調整することもできます。unique_idに、ステップ1に示す構成証明トークンにある疑似の固有IDを設定。is_bannedに、デバイスをブロックする場合はtrueを、ブロックしない場合はfalseを設定。is_bannedの設定がfalseの場合でも必要ですが、無視されます。remaining_time_in_minuteに、デバイスのブロック時間として0~52560000 (100年)の整数値を設定。access_tokenパラメーターに、ステップ5に示すアクセストークンを設定。https://graph.oculus.com/platform_integrity/device_ban?method=POST&unique_id=<unique_id>&is_banned=<is_banned>&remaining_time_in_minute=<remaining_time_in_minute>&access_token=<access_token>
{
"message": "Success",
"ban_id": "NjIxMzI3MTc0MTgzMzkzNw"
}
{
"error": {
"message": "There is no record of this Unique ID. Please check that the Unique ID matches what is given in the Attestation Token and try again. Note that Unique IDs will change every 30 days for each application and device, make sure the Unique ID is up to date.",
"type": "OCApiException",
"code": 1,
"error_data": {
},
"error_subcode": 1614026,
"fbtrace_id": "AUhV19OFsDqs6gTXn7zVybm"
}
}
{
"error": {
"message": "The App ID of the Access Token does not match the Unique ID, please check the Unique ID/Access Token and try again",
"type": "OCApiException",
"code": 1,
"error_data": {
},
"error_subcode": 1614027,
"fbtrace_id": "AHBbS8rEDcgajcgpQcqtVO1"
}
}
{
"error": {
"message": "The Remaining Time In Minute field is invalid. Please check that the time inputted is between 0 and 52560000.",
"type": "OCApiException",
"code": 1,
"error_data": {
},
"error_subcode": 1614028,
"fbtrace_id": "AtFBCvCIb4IXSGvvc4GudY8"
}
}
[App Credentials (アプリ認証情報)] のセクションで、アクセストークンをコピーします。これは**OCという形式になっています。 | App_ID | App_Secret**. |
https://graph.oculus.com/platform_integrity/device_ban_ids?access_token=<access_token>
{
"data": [
{
"message": "Success",
"all_ban_ids": [
{
"ban_id": "MTI0MjQ3MTc0MDk2NTYwNg",
"creation_date": "2025-05-27"
},
{
"ban_id": "NTgyOTAyMTc0MDk2NTgyNQ",
"creation_date": "2025-05-09"
},
{
"ban_id": "ODUyMTk1MTc0NjY1MjUzMw",
"creation_date": "2025-05-07"
}
]
}
],
"paging": {
"cursors": {
"before": "QVFIUmcwb3MtZAkhkWUhjZAElXQ2tHUXMwbjdjRi1NeUg5aWlIN3liSzZAfN1FmdnVIRkhmSVU3OUtpQ3FweUp3NE1TYWJob3k1NGIzeDRIdjdlMXRZAcWtyWGxn",
"after": "QVFIUmcwb3MtZAkhkWUhjZAElXQ2tHUXMwbjdjRi1NeUg5aWlIN3liSzZAfN1FmdnVIRkhmSVU3OUtpQ3FweUp3NE1TYWJob3k1NGIzeDRIdjdlMXRZAcWtyWGxn"
}
}
}
{
"data": [
{
"message": "Success",
"all_ban_ids": [
{
"ban_id": "MTI0MjQ3MTc0MDk2NTYwNg",
"creation_date": "2025-05-27"
},
.
.
<500 ban_ids here>
.
.
.
]
}
],
"paging": {
"cursors": {
"before": "QVFIUkIxLS1XNDhSdHJFQXlELXplbnZAINnd6cmc5ZAkhEOVJPU3ViR1F4TFQ4V3N3NWwxV1Y0T05rLWk4NWhKM2dGY2d2aGpaZAERfd0lSOXRYejdWbTJYYUhB",
"after": "QVFIUjlMcGJuRGxHUFRjT1lLOS04cGdRZAHpPSDFBQnA0aEJOZA3hmeDVaWEVSdXozM2FkOV9jVFhoVy1Jb08xRzlZAOVlROWNvTlNxMUpveUphZAmEzY2ZAHNFJ3"
},
"next": "https://graph.oculus.com/platform_integrity/device_ban_ids?access_token=<access_token>&pretty=1&limit=500&after=QVFIUjlMcGJuRGxHUFRjT1lLOS04cGdRZAHpPSDFBQnA0aEJOZA3hmeDVaWEVSdXozM2FkOV9jVFhoVy1Jb08xRzlZAOVlROWNvTlNxMUpveUphZAmEzY2ZAHNFJ3"
}
}
[App Credentials (アプリ認証情報)]のセクションで、access_tokenを取得します。これは**OCという形式になっています。 | App_ID | App_Secret**. |
https://graph.oculus.com/platform_integrity/device_ban?method=POST&ban_id=<ban_id>&is_banned=<is_banned>&remaining_time_in_minute=<remaining_time_in_minute>&access_token=<access_token>
{
"message": "Success",
"ban_id": ""
}
{
"message": "Success",
"ban_id": "NjIxMzI3MTc0MTgzMzkzNw"
}
{
"error": {
"message": "There is no record of this ban_id. Please check the ban_id and try again. If you don't know the specific ban_id, query the Device Ban IDs endpoint to see a list of all the ban_ids currently active for your application.",
"type": "OCApiException",
"code": -1,
"error_data": {
},
"error_subcode": 1614033,
"fbtrace_id": "AzBdHW5FhJ8xtfLLzQr90Gz"
}
}
{
"error": {
"message": "The App ID of the Access Token does not match the Ban ID, please check the Ban ID/Access Token and try again",
"type": "OCApiException",
"code": -1,
"error_data": {
},
"error_subcode": 1614036,
"fbtrace_id": "AIp_qT4f8skiZrs1NI2xPjN"
}
}
[App Credentials (アプリ認証情報)]のセクションで、access_tokenを取得します。これは**OCという形式になっています。 | App_ID | App_Secret**. |
https://graph.oculus.com/platform_integrity/device_ban?method=POST&unique_id=<unique_id>&is_banned=<is_banned>&remaining_time_in_minute=<remaining_time_in_minute>&access_token=<access_token>
{
"message": "Success",
"ban_id": ""
}
{
"message": "Success",
"ban_id": ""
}
{
"message": "Success",
"ban_id": "NjIxMzI3MTc0MTgzMzkzNw"
}
アプリ認証情報セクションで、アクセストークン(形式は**OC)をコピーします。 | App_ID | App_Secret**. |
https://graph.oculus.com/platform_integrity/device_ban_status?unique_id =<unique_id>&access_token=<access_token>
{
"data": [
{
"message": "Success",
"is_banned": true,
"remaining_time_in_minute": 12
}
]
}
{
"error": {
"message": "There is no record of this Unique ID. Please check that the Unique ID matches what is given in the Attestation Token and try again. Note that Unique IDs will change every 30 days for each application and device, make sure the Unique ID is up to date.",
"type": "OCApiException",
"code": 1,
"error_data": {
},
"error_subcode": 1614026,
"fbtrace_id": "AUhV19OFsDqs6gTXn7zVybm"
}
}
{
"error": {
"message": "The App ID of the Access Token does not match the Unique ID, please check the Unique ID/Access Token and try again",
"type": "OCApiException",
"code": 1,
"error_data": {
},
"error_subcode": 1614027,
"fbtrace_id": "AHBbS8rEDcgajcgpQcqtVO1"
}
}
Device Integrity State Bypass: これはデフォルトで有効になっています。これが有効になっている場合、Metaの内部テストデバイスでは、構成証明トークンのdevice_state内のdevice_integrity_stateが常にAdvancedに設定されます。これは、アプリサーバーがNotTrustedデバイス上でサービスをブロックする場合に特に便利です。このバイパス機能を使えば、アプリをMetaのデバイス上でシームレスにテストできるため、Metaの内部テストチームによる効率的なサポートが可能になります。これは特に、予備ビルドリリースの評価において有効です。Token Expiration Duration: この設定により、構成証明トークンの寿命が決まります。デフォルトの有効期限は24時間です。期間が短いと、デバイスやアプリの不正アクセスを検出するための所要時間が短縮されてセキュリティは強化されますが、必要になる構成証明のチェックの頻度は高くなります。逆に期間が長いと、アプリへの負荷は減りますが、侵害された場合に不正アクセスが拡大するリスクが高くなります。device_integrityがNotTrustedとなる有効な構成証明の判決と同じように扱う必要があります。exp (トークン有効期限)が24時間以内であるnonceが構成証明APIリクエストで使われるchallenge_nonceと同じであるtimestampが最新である(数分以内)app_integrity_stateはパッケージがHorizonストアからインストールされていることを示しているpackage_cert_sha256_digestハッシュが想定どおりであるdevice_integrityがAdvancedまたは少なくともBasicである
challenge_nonceに一意の要素を含めるchallenge_nonceとして使用されます。challenge_nonceと一致することを確認する。保存された一意の値は、単一の構成証明クレームの検証にのみ使われるため、破棄してください。
challenge_nonceに機密性の高いリクエストをバインドするchallenge_nonceとして使用される。