APIリファレンス
OZeroSecurity SDKのすべての公開クラスとメソッドの完全なリファレンスです。特に記載がない限り、すべてのクラスはOZeroSDK.Security名前空間に属します。
OZeroSecurityManager OZeroSDK.Security
アクティブなセキュリティモジュールとセキュリティイベントを管理するグローバルマネージャーです。シーン遷移時も維持され、Instanceプロパティからアクセスできます。SDKがアプリ起動時に自動生成するため、直接インスタンス化する必要はありません。
プロパティ
| 名前 | 型 | 説明 |
|---|---|---|
| Instance | OZeroSecurityManager | 静的シングルトンアクセサ。アクティブなインスタンスを返します。 |
メソッド
プロジェクト側でセキュリティイベントを受け取りたいときにコールバックを登録します。OZero の基本対応は別の流れで実行されるため、このコールバックを登録または解除しても SDK の保護動作は無効になりません。コールバックには、検出領域、公開 abort code、メッセージキー、診断メッセージ、アプリ終了予定の有無を含む OZeroSecurityEvent が渡されます。
以前に登録したユーザーコールバックを削除します。メモリリーク防止のため、OnDisableまたはOnDestroyで必ず呼び出してください。
CustomCallbackOnly で使用するプロジェクトコールバックを登録します。ネイティブコードが取り消せない致命的終了期限を設定した後にのみ呼び出されます。通知表示や短いクリーンアップは可能ですが、終了の取り消しや期限延長はできません。
登録済みのセキュリティ終了通知コールバックを解除します。
すでに保留中の致命的セキュリティ終了を直ちに完了します。通常は通知ダイアログの OK ボタンから呼び出し、保留中の終了がなければ何もしません。
Pro device policy heartbeat を通じてサーバーから送られる policy action を受け取るコールバックを登録します。ポータルの Callback 対応ポリシーをゲームコードへ接続するための API で、ローカルのセキュリティ検知コールバックとは別に動作します。
以前に登録した policy action コールバックを削除します。シーンオブジェクトで使う場合は OnEnable で登録し、OnDisable で解除してください。
ユーザー向けの Activation / DRM 状態変化を受け取るコールバックを登録します。OZero-managed Steam DRM と既定の Managed Verification UI を使う場合、SDK がオンライン必須、再試行、timeout、ブロック状態を表示します。カスタム UI、telemetry、ゲーム固有の session gate が必要な場合だけ登録してください。
以前に登録した Activation 状態コールバックを削除します。シーンオブジェクトで使う場合は OnEnable で登録し、OnDisable で解除してください。
Registers a callback for the shared OZero Managed Verification state used by Pro Build Integrity and Steam DRM. The default SDK UI handles these states automatically when enabled; use this callback only for custom UI, custom telemetry, or game-specific session gates.
Removes a previously registered managed verification state callback. Register in OnEnable and unregister in OnDisable when using a scene object.
Replaces individual built-in verification UI strings at runtime. Use JSON files under Assets/OZeroSecurity/Resources/OZeroLocalization for normal localization, and use this provider only when text must be supplied dynamically.
Removes a previously registered managed verification text provider.
Retries SDK-managed Build Integrity attestation after an online-required or retry-timeout state. The default Managed Verification UI calls this automatically. If your own game server validates OZA tokens, retry your own login or session-refresh request instead.
OZero サーバーが管理する Steam Activation / DRM 検証を再試行します。既定の Managed Verification UI は必要な retry フローを SDK 内で呼び出します。ゲームコードから呼ぶのは既定 UI を意図的に置き換えるカスタム UI の場合だけです。自社ゲームサーバーが Steam 検証を行う構成では、このメソッドではなく自社のログインまたはセッション要求を再送してください。
デリゲート
RegisterUserCallback で使うコールバックシグネチャです。警告 UI、独自サーバーログ、短い保存処理が必要なときは evt.Type、evt.AbortCodeHex、evt.MessageKey、evt.Message、evt.WillAbort を確認してください。evt.WillAbort が true の場合、アプリ終了が予定されているため、時間のかかる処理は避けてください。
サンプルコード
using OZeroSDK.Security;
using UnityEngine;
public class MySecurityListener : MonoBehaviour
{
void OnEnable()
=> OZeroSecurityManager.Instance.RegisterUserCallback(OnThreat);
void OnDisable()
=> OZeroSecurityManager.Instance.UnregisterUserCallback(OnThreat);
void OnThreat(OZeroSecurityEvent evt)
=> Debug.Log(
$"Threat={evt.Type}, Code={evt.AbortCodeHex}, Message={evt.Message}");
}
OZeroSecurityTerminationNoticeEvent class
ネイティブの致命的終了期限が設定された後にのみ発行されるユーザー向けペイロードです。元の安全なセキュリティイベントと、サポートへ報告できる安定したサポートコードを含みます。
| 名前 | 型 | 説明 |
|---|---|---|
| SecurityEvent | OZeroSecurityEvent | 元の安全なイベントです。WillAbort は true で、ログとローカライズには AbortCode と MessageKey を使用します。 |
| SupportCode | string | プレイヤーがカスタマーサポートへ報告できる、安定した非機密コードです。 |
UI 表示やコールバック呼び出しは、ネイティブの致命的終了期限を一時停止、取り消し、延長しません。処理は短時間に限定し、早期終了するときだけ
OZeroSecurityManager.ExitNow() を呼び出してください。OZeroSecurityEvent class
RegisterUserCallback に渡される顧客公開用の違反イベントです。内部の検知詳細情報の代わりに、安定して安全な診断情報のみを公開します。
| 名前 | 型 | 説明 |
|---|---|---|
| Type | ModulationType | 違反を発生させたセキュリティモジュールです。 |
| AbortCode | OZeroAbortCode | 安定した公開中絶コードカテゴリです。 |
| AbortCodeValue | int | サーバーログに使用するのに適した数字のコード値です。 |
| AbortCodeHex | string | 0x0C のような16進数の文字列です。 |
| MessageKey | string | ローカライズや analytics のグループ化に使用できる安定した英文メッセージキーです。 |
| Message | string | 顧客に公開可能な安全な英文診断メッセージです。 |
| WillAbort | bool | コールバック返却後、または猶予時間が終わった後に現在の対応ポリシーがアプリを終了する予定であれば true です。 |
OZeroPolicyActionEvent class
RegisterUserPolicyActionCallback に渡されるサーバーポリシーのコールバックペイロードです。顧客ポータルのポリシーシステムから発行され、Pro device policy heartbeat により Unity メインスレッドで配信されます。
| 名前 | 型 | 説明 |
|---|---|---|
| Module | string | variant、injection、physics、user_policy(ユーザー UUID ブロックルール。強制終了ルールでセッションが終了する直前は ActionId = "user_blocked")などのポリシーモジュールです。 |
| ActionId | string | 配信を識別する一意の ID です。SDK はこの値で重複配信を防ぎ、サーバーへ ack を送ります。 |
| PolicyId | string | 利用可能な場合の顧客ポータル policy ID です。 |
| Reason | string | ポリシーに設定された理由、または一致した証拠から生成された理由です。 |
| Score | int | ポリシーが一致した時点のリスクスコアです。 |
| EventCount | int | ポリシー一致に反映されたイベント数です。 |
| WindowMinutes | int | ポリシー評価ウィンドウ(分)です。 |
| IssuedAtUnixMs | long | サーバーが action を発行した UTC epoch milliseconds です。 |
| ExpiresAtUnixMs | long | ack されなかった action が期限切れになる UTC epoch milliseconds です。 |
ポリシーコールバック例
Customer Portal のセキュリティポリシーを Callback に設定した場合に使用します。SDK は policy action をゲームへ配信し、ゲーム側で通知、制限、画面遷移を決定します。
using OZeroSDK.Security;
using UnityEngine;
public sealed class OZeroPolicyListener : MonoBehaviour
{
private void OnEnable()
=> OZeroSecurityManager.Instance.RegisterUserPolicyActionCallback(OnPolicyAction);
private void OnDisable()
=> OZeroSecurityManager.Instance.UnregisterUserPolicyActionCallback(OnPolicyAction);
private void OnPolicyAction(OZeroPolicyActionEvent evt)
{
Debug.LogWarning($"Policy={evt.PolicyId}, Module={evt.Module}, Score={evt.Score}, Reason={evt.Reason}");
if (evt.Score >= 80)
{
ShowSecurityNotice(evt.Reason);
DisableRankedMatchmaking();
}
}
}
OZeroUserActivationStateEvent class
RegisterUserActivationStateCallback に渡されるユーザー向け Activation / DRM 状態ペイロードです。Steam Activation / DRM は、キャッシュ済みのオフライン実行がまだ許可されるか、オンライン再検証が必要かをこのイベントでゲームへ通知します。
| 名前 | 型 | 説明 |
|---|---|---|
| Provider | string | steam などの Activation provider です。 |
| State | string | valid、revalidated、online_required、offline_degraded、rejected などの安定した状態値です。 |
| Reason | string | steam_activation_revalidate_unavailable や steam_activation_cache_expired などの reason code です。 |
| Action | string | 利用可能な場合、この状態に関連付けられたポータル policy action 値です。 |
| OfflineAllowed | bool | 署名検証済みトークンがオフライン許可期間内にある場合のみ true です。 |
| OnlineRequired | bool | 保護セッションを許可する前に、ユーザーへネットワーク接続と再検証を求める必要がある場合は true です。 |
| RefreshAtUnixMs | long | オンライン更新が必要になる UTC epoch milliseconds です。 |
| OfflineUntilUnixMs | long | 署名済みオフライン許可期間が終了する UTC epoch milliseconds です。 |
Activation 状態コールバックの使用基準
OZero-managed Steam DRM と既定の Managed Verification UI を使う場合、activation callback コードは不要です。SDK がプレイヤーにオンライン必須、再試行、timeout、ブロック状態を表示します。このコールバックは、既定 UI を置き換える場合、独自 session gate を追加する場合、または顧客ゲームサーバー検証フローを運用する場合だけ登録してください。Steam Web API Key や OZero Server API Key をクライアントに入れないでください。
OZeroUserManagedVerificationStateEvent class
RegisterUserManagedVerificationStateCallback に渡されるユーザー向け managed verification ペイロードです。Build Integrity と Steam DRM の両方がこのイベントを使いますが、既定の Managed Verification UI はこれを自動的に処理します。ゲームコードで扱うのは、カスタム UI、追加 telemetry、ゲーム固有の session gate が必要な場合だけです。
| 名前 | 型 | 説明 |
|---|---|---|
| Provider | OZeroManagedVerificationProvider | Source module: BuildIntegrity or SteamDrm. |
| State | OZeroUserManagedVerificationState | Current state such as Checking, Allowed, Warning, OnlineRequired, RetryTimedOut, Blocked, or StandardFallback. |
| Reason | string | Stable reason code suitable for custom UI text, analytics, or customer support logs. |
| Verdict | string | Server verdict when available, such as allow, warn, block, or fallback. |
| CanRetry | bool | True when a Retry action is meaningful for the current provider and state. |
| TimestampUnixMs | long | UTC epoch milliseconds when the state was raised. |
Managed verification コールバックの使用基準
Security Config > Pro - License & Online Services > Online Verification UI > Ui Policy が OZero 内蔵ダイアログを使う場合、SDK は Build Integrity と Steam DRM の managed-verification UI、再試行、timeout 状態をすでに処理します。RegisterUserManagedVerificationStateCallback は Custom UI / Callback Only、追加 telemetry、ゲーム固有の session gate の場合だけ登録してください。カスタム UI モードでは Provider に合う retry メソッドを呼び、自社ゲームサーバーモードでは SDK retry ではなく自社のログインまたはセッション要求を再送してください。
OZeroManagedVerificationUiPolicy enum
Build Integrity と Steam DRM で発生する managed verification 状態を SDK がプレイヤーへどのように表示するかを決めます。2 つの Built-In policy には TextMeshPro、TMP Essential Resources、optional OZero Built-In TMP Dialog package が必要です。これらがない状態で Built-In policy を選択すると build preflight が player build を停止します。Window > OZero Security > Check Setup で該当エラーの Import Package を押して修正してください。
| 値 | 説明 |
|---|---|
| CustomCallbackOnly | SDK は RegisterUserManagedVerificationStateCallback の状態だけを通知します。ゲーム側で独自 UI、再試行ボタン、timeout 処理、session gate をすべて実装する場合に使用します。 |
| BuiltInBlockingDialog | OZero の内蔵ブロッキングダイアログを使用します。オンライン再検証が必要な間、保護セッションを停止またはブロックすべきリリースビルドの推奨既定値です。 |
| BuiltInNonBlockingDialog | OZero の内蔵非ブロッキング通知を使用します。再試行や警告状態を案内している間もゲーム進行を安全に継続できる場合だけ使用してください。 |
OZeroSecurityTerminationNoticePolicy enum
すでに設定された致命的セキュリティ終了の直前に表示する任意のユーザー通知を制御します。Standard と Pro の両方で利用できます。
| 値 | 説明 |
|---|---|
| Disabled = 0 | 終了通知を表示しません。ネイティブの致命的終了期限は引き続き有効です。 |
| OZeroBuiltIn = 1 | ローカライズ済みの OZero 組み込みダイアログを表示し、プレイヤーの確認時に早期終了します。 |
| CustomCallbackOnly = 2 | 登録済みの終了通知コールバックを呼び出して、プロジェクト独自の UI を表示します。終了の取り消しや延長はできません。 |
OZeroManagedVerificationTimeoutAction enum
オンライン必須または再試行状態が設定された制限時間内に解決しない場合、内蔵 managed verification UI が行う動作を決めます。
| 値 | 説明 |
|---|---|
| KeepDialog | timeout 後もダイアログを表示し続けます。プレイヤーに手動再試行を続けさせる必要があるフローでのみ選択してください。 |
| BlockSession | timeout 後に保護セッションをブロックします。オンライン検証は必須だがアプリは開いたままにする場合の推奨既定値です。 |
| AbortApplication | timeout 後にアプリケーションを終了します。未解決の検証失敗で即時終了するリリースポリシーの場合だけ使用してください。 |
| InvokeCallbackOnly | 内蔵のブロックまたは終了動作を行わず、callback のみ呼び出します。完全なカスタム session control が必要な場合に使用します。 |
ModulationType enum
どのセキュリティモジュールが警告を発生させたかを示します。OZeroSecurityEvent.Type として渡されます。
| 値 | 説明 |
|---|---|
| MemoryModulation | Secure Type変数が不審な方法でアクセスされた |
| SpeedHack | スピードハックまたは時間操作を検知 |
| TimeHack | システムクロックの異常を検知(逆行、NTP不一致) |
| Injection | メモリインジェクションツール(Fridaなど)または不正DLLを検知 |
| PhysicsHack | 不可能な位置変化を検知 (OZeroPhysicsHackDetector により発生 — プレイヤーオブジェクトに直接コンポーネントをアタッチして初期化する必要があります) |
| DeviceBindingModulation | セーブデータがバインドされたデバイスと異なるデバイスで読み込まれた |
| InstallSource | アプリが公認ストア以外からインストールされた |
| BuildIntegrity | アセンブリハッシュ不一致、デバッガー接続、またはプラットフォームチェック失敗 |
| EnvironmentModulation | エミュレーターまたは非標準ランタイム環境を検知 |
| SteamAntiPiracy | Steam の所有権またはチケット検証に失敗した場合に発生します。 |
| InjectionScannerUnavailable | ネイティブのインジェクションスキャナーが動作できなかった(fail-close; abort code 0x15、攻撃検知ではない) |
OZeroBootstrapper OZeroSDK.Security
SDK初期化を担当する自動起動の起点です。プロジェクトコードから直接呼び出す必要はありません。Unityの起動フローに合わせてセキュリティ設定を読み込み、ゲーム開始前に有効なディテクターを準備します。
OZeroSecurityConfig、Project Settings で編集)のみです。
OZeroSecurityConfigRuntime OZeroSDK.Security
保護されたビルド設定をプレイヤー実行時に読み込んで検証する構成ローダーです。パッケージ化された設定を検証し、メモリ上のOZeroSecurityConfig実行設定を準備し、検証失敗時は設定された脅威対応ポリシーを適用します。
プロパティ
| 名前 | 型 | 説明 |
|---|---|---|
| Current | OZeroSecurityConfig | プレイヤービルドに含まれる保護設定を読み込んで作成した設定スナップショットです。初回アクセス時にEnsureLoaded()を呼び出します。 |
メソッド
繰り返し呼び出しても安全なローダーです。最初の呼び出しでパッケージ化された設定を検証・ロードし、以降の呼び出しでは同じ設定スナップショットを再利用します。失敗時の処理は設定された対応ポリシーに従います。
OZeroSecurityConfigとUnityエディターウィンドウと考えてください。
OZero Secure Variables OZeroSDK.Security
通常の数値、文字列、ベクター型の代わりに使用できる暗号化タイプです。値は保護されたメモリ領域に保存され、ほとんどの算術演算子と暗黙的な変換をサポートします。既存のコードで型名を変更するだけで適用できます。
対応型一覧
| クラス | 置き換え対象 |
|---|---|
| OZeroSV_Int | int |
| OZeroSV_Int64 | long |
| OZeroSV_UInt | uint |
| OZeroSV_UInt64 | ulong |
| OZeroSV_Short | short |
| OZeroSV_UShort | ushort |
| OZeroSV_Byte | byte |
| OZeroSV_Float | float |
| OZeroSV_Double | double |
| OZeroSV_Decimal | decimal |
| OZeroSV_Bool | bool |
| OZeroSV_String | string |
| OZeroSV_Vector2 | Vector2 |
| OZeroSV_Vector3 | Vector3 |
| OZeroSV_Buffer | byte[] |
対応演算子
数値型(Int、Int64、UInt、UInt64、Short、UShort、Byte、Float、Double、Decimal)はすべての算術(+ - * / %)、比較(== != < > <= >=)、複合代入(+= -= *= /=)、インクリメント/デクリメント(++ --)演算子と、対応する基本型との暗黙的変換をサポートします。Vector2・Vector3は算術および等価演算子をサポートします。Boolは等価演算子のみサポートします。Stringは==、!=、+をサポートします。Bufferはインデックス演算子を通じてバイト配列に直接アクセスできます。
OZeroSafePlayerPrefs OZeroSDK.Security
UnityのPlayerPrefsと同じように使用できる暗号化ストレージです。キー名と値が保護されているため、WindowsのレジストリやiOSの設定ファイルを直接開いても元の値を読み取るのは困難です。
メソッド
よく使う PlayerPrefs 形式のメソッドに加えて、Int64、Double、Bool、IncrementInt ヘルパーを提供します。既存の plain PlayerPrefs 値は自動移行されないため、今後保護したい key から OZeroSafePlayerPrefs で保存してください。
OZeroSafePlayerPrefsで書き込まれたデータは標準のPlayerPrefsと互換性がありません。両者を切り替えると既存データが読めなくなります。
OZeroSV_File OZeroSDK.Security
内部の暗号化ロジックでファイルの読み書きを保護します。デバイスバインディングキーを使用しないため、Steamクラウドセーブのように複数のデバイスで同じセーブファイルを読み込む必要がある場合でも使用できます。ファイルを意図的に改ざんした場合、読み込み時に整合性チェックが失敗し、InvalidDataException が発生します。
OZeroSV_File APIを通して処理してください。
メソッド
contentsを暗号化し、pathに保存します。親フォルダは自動作成されないため、必要な場合は保存前にDirectory.CreateDirectoryで作成してください。
pathのファイルを読み取り、整合性を検証したうえで復号済み文字列を返します。ファイルが改ざんされている場合はInvalidDataExceptionを発生させます。
データを暗号化し、pathに安全に保存します。
pathに指定されたファイルを読み込みます。読み込み時にファイル整合性を検証し、改ざんを検知します。
ファイルパスではなく、すでにメモリに読み込まれた暗号化バイトバッファを復号します。リモートダウンロードやカスタムストレージから受け取ったデータを ReadAllText に渡せない場合に使用してください。
サンプルコード
using OZeroSDK.Security;
string path = Application.persistentDataPath + "/save.json";
string json = JsonUtility.ToJson(saveData);
// Write (encrypts automatically)
OZeroSV_File.WriteAllText(path, json);
// Read (decrypts + integrity check)
try
{
string loaded = OZeroSV_File.ReadAllText(path);
saveData = JsonUtility.FromJson<SaveData>(loaded);
}
catch (System.IO.InvalidDataException)
{
// File was tampered — handle accordingly
Debug.LogError("Save file integrity check failed.");
}
OZeroBuildIntegrityValidator OZeroSDK.Security
ビルド改ざん、デバッガー/タイミング異常、プラットフォームネイティブ整合性チェック、任意の Pro サーバー attestation を処理する検証器です。Build Integrity が OZeroSecurityConfig で有効な場合、SDK が自動生成します。
検査内容
| 検査 | 説明 |
|---|---|
| Assembly / Manifest | 対応ビルドターゲットで、生成された整合性マニフェストと managed assembly の状態を検証します。 |
| Debugger / Timing | 接続されたデバッガー、異常なタイミングギャップ、ブレークポイントに近い遅延を検出し、一般的なフォーカス喪失の誤検知は抑制します。 |
| Platform Native | 有効な場合、Android パッケージ/署名、iOS jailbreak、デスクトップランタイム状態などのプラットフォーム別検査を実行します。 |
| Pro Attestation | Pro サーバー検証がオンになっている場合、ローカル検査の通過後にサーバーに検証トークンを要求します。自社のゲームサーバーがない場合、OZero サーバーが許可/警告/遮断の結果を一緒に返すこともできます。 |
公開プロパティ
| 名前 | 型 | 説明 |
|---|---|---|
| Instance | OZeroBuildIntegrityValidator | モジュールが作成されている場合の現在の validator インスタンスです。 |
| LastValidationResult | bool? | 直近のローカル検証結果です。初回実行前は null です。 |
| IsValidating | bool | 検証実行中は true です。 |
| IsIntegrityVerified | bool | 最近アクティブになったローカル検査を通過すると true です。 |
| OZeroOnlineAttestation.Token | OZeroBuildAttestationToken | 直近の Pro attestation トークンです。サーバー attestation が成功または失敗するまでは null であり、トークンには再利用追跡のための固有 ID が含まれます。 |
イベントとメソッド
有効なすべてのローカル検査が通過したときに呼び出されます。
有効なローカル検査または Pro attestation がビルドを拒否したときに呼び出されます。
オンラインアドオンでは OZeroOnlineAttestation.OnAttestationPassed です。サーバーアテステーションが成功し、OZeroOnlineAttestation.Token に有効なトークンがあるときに呼び出されます。
オンラインアドオンでは OZeroOnlineAttestation.RequestGameServerAttestation に移動しました。CustomerGameServer 専用で、ローカル検査が通った後、サーバーが発行した audience、hex 64 文字の challenge、session ID でアテステーションを開始します。トークンは OZeroOnlineAttestation.TokenReady で届きます。サーバー API キーでゲームサーバーと連携するを参照してください。
手動検証を開始します。通常のプロジェクトでは、ダッシュボードの起動時/定期検証設定を使用する方が良いでしょう。
OZeroBuildAttestationToken
Pro attestation の結果です。AttestToken をゲームサーバーへ渡し、ログイン、PvP、ランキング、財貨処理などのフローに使用する前に IsValid(nowMillis) で有効性を確認してください。
サーバーが発行した有効期限を過ぎている場合 true を返します。
トークン発行が成功し、まだ期限切れでない場合 true を返します。
OZeroSpeedHackDetector OZeroSDK.Security
5つの独立した検知シグナルを使用してスピードハックと時間操作を検知します。シグナルが互いに確認し合った場合のみ脅威を報告し、誤検知を減らします。
検知シグナル
| シグナル | 説明 |
|---|---|
| TimeScale | ゲームの時間の流れが異常に変化していないか確認 |
| API Clock | プラットフォーム時間とネイティブ基準時間を比較し、大きな差を検知 |
| Thread Drift | Unityランタイム時間とネイティブ基準時間の流れの差を観察 |
| Time Backward | デバイスの時間が異常に巻き戻る状況を検知 |
| NTP | 任意 — 信頼できる外部基準時間との比較(ネットワーク必要) |
検知はModulationType.SpeedHackまたはModulationType.TimeHackでOZeroSecurityManagerコールバックを通じて発生します。OZeroSecurityConfigで設定します。
OZeroWatchdog OZeroSDK.Security
信頼できる長時間ロード処理のための public helper です。リリース時の deadline より長く Unity のメインスレッドを正当にブロックする同期処理の間、native Watchdog heartbeat deadline を限定的に猶予します。
メソッド
制限付きの loading grace scope を開始します。信頼できるロード処理が終わったら、返された scope の End() を呼ぶか dispose してください。ネストされた scope に対応し、最後の scope が終わると通常の Watchdog timing に戻ります。
この loading grace scope を手動で終了します。Dispose() も同じ処理を呼ぶため、using ブロックと明示的な End() は同じように動作します。
同期ロード処理のための便利な wrapper です。loading grace scope を作成し、work を実行した後、using ブロックで安全に scope を終了します。
例
using OZeroSDK.Security;
using UnityEngine.SceneManagement;
public void LoadLargeScene()
{
using (OZeroWatchdog.BeginLoadingGrace(60000))
{
SceneManager.LoadScene("Battle", LoadSceneMode.Single);
}
}
OZeroInjectionDetector OZeroSDK.Security
実行中のゲームに疑わしいモジュールが付着していないか、またはフッキングやデバッガーの痕跡がないかを確認します。定期検査は可能な場合、検査タイミングを少しずつ変えて単純な回避試行を難しくします。
検知対象
| Runtime module | 予期しないランタイムモジュールまたはフッキングが疑われるシグナル |
| Debugger | デバッガーまたは追跡ツールの接続が疑われるシグナル |
| Memory map | 異常なランタイムメモリまたはモジュールの状態シグナル |
| Illegal DLL | プロセスにロードされた許可されていない管理アセンブリシグナル(Windows/Unity Editor) |
検知はModulationType.InjectionでOZeroSecurityManagerコールバックを通じて発生します。
OZeroPCStoreAntiPiracy OZeroSDK.Security
Steam Anti-Piracy の検知後の動作を実行中に変更する API です。ほとんどのプロジェクトは Config Dashboard での設定で十分です。ゲーム内に運営者用メニューや QA 用スイッチを直接提供する場合にのみ使用してください。 任意 DLC の所有照会(QueryDlcOwnership)も提供します。
OZeroPCStoreDetectionAction
| 値 | 説明 |
|---|---|
| Off | ローカルの Steam Anti-Piracy 応答を適用しません。制限された問題分析の状況でのみ使用してください。 |
| Observe | 診断情報だけを記録し、ゲーム実行は継続して許可します。 |
| Callback | OZeroSecurityManager コールバックを発生させ、ゲームが UI 表示、ログ記録、独自の処理を行えるようにします。このオプションだけではアプリは自動終了せず、実際の終了可否は Global Threat Response 設定とプロジェクトのコールバック処理方法に従います。 |
| Block | 違反を遮断ポリシーとして扱います。実際のアプリの終了可否は Global Threat Response 設定に従います。 |
メソッド
実行中の Steam Anti-Piracy の検知後動作を変更します。QA ビルドで一時的に Observe に下げたり、運営者用メニューで特定の動作を選択させる場合に使用します。
実行中に変更した Steam Anti-Piracy の動作を解除し、Config Dashboard または Pro ポータルに設定された基本動作に戻します。
例
using OZeroSDK.Security;
// QA session: observe Steam violations without blocking gameplay.
OZeroPCStoreAntiPiracy.SetDetectionActionOverride(
OZeroPCStoreDetectionAction.Observe);
// Restore the dashboard/server policy.
OZeroPCStoreAntiPiracy.ClearDetectionActionOverride();
最後の検証結果
直近の Steam 検証スナップショットを返します。reported AppID、BuildID、SteamID、サーバー検証状態、soft signal、native score フィールドを含みます。デバッグ UI や QA レポートには使用できますが、ゲームプレイ権限の判断の唯一の基準として使用しないでください。
QueryDlcOwnership
現在の Steam ユーザーが任意 DLC を所有しているかを照会します。ゲームを終了しない照会専用の API で、DLC コンテンツの解放などの判断に使います。requiredDlcAppIds に入れた必須 DLC は起動時に自動で確認されるため、この呼び出しは不要です。Steamworks.NET を直接呼び出す代わりにこの API を使ってください。
| フィールド | 説明 |
|---|---|
| bool Owned | 現在のアカウントが DLC を所有しているか(Steam BIsSubscribedApp 基準)。解放の判断にはこの値を使ってください。 |
| bool Installed | DLC がこの PC にインストールされているか。所有しているがインストールされていない状態(未発売またはダウンロードしていない DLC)は正常で、違反ではありません。 |
| bool CrossCheckConsistent | Steam API の応答どうしが整合しているかの確認結果です。応答間の整合性の確認にすぎず改ざんの証拠ではないため、この値だけでブロックを判断しないでください。 |
| string Reason | owned、not_owned、verification_unavailable(Steam の準備ができておらず応答できない)、bridge_unavailable(このビルドに Steam ブリッジがない。エディターと Steam Anti-Piracy をオフにしたビルドを含む)。失敗時は例外ではなく Owned=false とこの値を返します。 |
using OZeroSDK.Security;
// Optional DLC: unlock the content only when the account owns it.
OZeroDlcOwnership dlc = OZeroPCStoreAntiPiracy.QueryDlcOwnership(1234560);
if (dlc.Owned)
{
UnlockExpansion();
}
OZeroInstallSourceValidator OZeroSDK.Security
Android のインストール元検証器です。Install Source が有効な場合、コンポーネントは自動生成されます。顧客コードでは主にサポート UI、診断ログ、ストア別分岐処理のために最後の結果を読みます。
メソッドとイベント
インストール元の確認が終わったときに呼び出されます。
直近の検査結果を返します。
必要に応じて初期化を行い、Android のインストール元結果を返します。
InstallSourceResult
| DetectedSource | 解決されたインストール元です。型は AndroidInstallSource enum です。 |
| RawInstallerPackage | Android PackageManager が返した元の installer package name です。 |
| IsAuthorized | ローカル設定と、有効な場合は Pro サーバーポリシーがこのインストール元を許可しているなら true です。 |
| ServerVerified | Pro 専用です。サーバー検証呼び出しが完了したなら true です。 |
| ServerAuthorized | Pro 専用です。ServerVerified が true の時のサーバー側の許可状態です。 |
AndroidInstallSource
InstallSourceResult.DetectedSource が返す正確な enum 値です。manual のストア名は読みやすい表示名であり、コードでは下記の enum 名で比較してください。
| 値 | 説明 |
|---|---|
| GooglePlayStore | Google Play Store からインストールされた場合です。 |
| SamsungGalaxyStore | Samsung Galaxy Store からインストールされた場合です。 |
| AmazonAppstore | Amazon Appstore からインストールされた場合です。 |
| HuaweiAppGallery | Huawei AppGallery からインストールされた場合です。 |
| OneStore | ONE Store からインストールされた場合です。 |
| XiaomiGetApps | Xiaomi GetApps からインストールされた場合です。 |
| OppoAppMarket | OPPO App Market からインストールされた場合です。 |
| VivoAppStore | Vivo App Store からインストールされた場合です。 |
| Custom | 元の installer package が customAuthorizedPackages と一致した場合です。 |
| ADB | Android が空の installer package を返した場合です。通常は ADB または sideload 形式のインストールで発生します。 |
| DetectionFailed | JNI またはプラットフォーム API を利用できず、installer 照会そのものが失敗した場合です。ADB とは別です。 |
| Unknown | Android が package name を返したものの、標準リストにもカスタム許可リストにもない場合です。 |
| Editor | Unity Editor 内で実行中に返されます。 |
| NotApplicable | Android のインストール元という概念が適用されないプラットフォームで返されます。 |
OZeroDeviceBindingDetector OZeroSDK.Security
端末に紐づくセーブスロットとサポート用リセットフローのためのヘルパー API です。Device Binding が有効な場合、検証器は自動で開始されます。クラウドセーブ、アカウントセーブスロットなどを現在の端末に紐づけたい場合だけ、以下のトークンメソッドを直接呼び出してください。
メソッド
ローカル端末フィンガープリントを準備し、登録または検証します。通常のプロジェクトでは SDK 起動時に自動で呼び出されます。
セーブスロットキーを現在の端末に紐づけるトークンを作成します。トークンはセーブメタデータまたはサーバー記録に保存し、プレイヤーが直接編集できるセーブ本文には入れないでください。
保存済みのセーブスロットトークンが現在の端末と一致するか確認します。一致しない場合、設定された Device Binding 違反応答が発生します。
現在の端末フィンガープリント hash を返すデバッグ/デモ用ヘルパーです。本番ゲームコードで表示、アップロード、保存しないでください。
この端末に保存された指紋を消去し、現在の端末を再登録します。プレイヤービルドで Pro ライセンスとサーバー設定がある場合、空でないトークンはバックグラウンドでサーバーが発行した Reset Token として検証されるため、解除はこの呼び出しが戻った後に行われます。結果を待つには ClearStoredFingerprintAsync を使ってください。それ以外の場合は認可コールバックが判断し、トークンを AuthorizationToken で受け取ります。サーバー Reset Token を使わない場合は RequestReset を使ってください。
ClearStoredFingerprint の await 可能な形式です。メインスレッドから呼び出して await し、指紋を消去した場合は true を返します。サーバー検査はメインスレッドをブロックしないため、HTTP がメインスレッドで動く環境(Windows・macOS の IL2CPP、WebGL)でもゲームが止まりません。
保存済みの指紋の消去を要求します。プレイヤービルドでは認可コールバックが判断し、reason はログ用にコールバックへ渡されます。承認されると指紋を消去し、同じセッションで現在の端末を再登録して true を返します。エディターでは常に解除されます。
プレイヤービルドで解除を承認(true)または拒否するゲームのコールバックを登録します。null を渡すと登録を解除します。コールバックは解除を要求したスレッドで同期的に呼ばれます。認可コールバックがない場合、プレイヤービルドは有効なサーバー Reset Token がない限り解除を拒否します。
true を返してください。常に true を返すコールバックは、プロセス内の任意のコードにバインディングの消去を許してしまいます。同梱のデモシーンは、デモのためにすべての要求を承認します。
OZeroDeviceBindingResetRequest
認可コールバックに渡される読み取り専用の構造体です。
RequestReset に渡した理由です。ない場合は空文字列です。
ClearStoredFingerprint に渡したトークンです。ない場合は空文字列です。
例
using OZeroSDK.Security;
var detector = OZeroDeviceBindingDetector.Instance;
string slotKey = "account:1234:slot:main";
string token = detector.BindToSaveSlot(slotKey);
// Store token next to your save metadata.
bool ok = detector.ValidateSaveSlot(slotKey, token);
// Reset flow: register an authorizer that performs your own check.
OZeroDeviceBindingDetector.SetResetAuthorizer(request =>
MyAccountService.HasVerifiedResetFor(request.Reason));
bool cleared = detector.RequestReset("ticket-1234");
OZeroSecurityConfig ScriptableObject
OZero モジュールのセキュリティ設定です。エディターでは Edit > Project Settings > OZero Security で編集し、ProjectSettings/OZeroSecuritySettings.asset(共有)と UserSettings/OZeroSecuritySecrets.json(ユーザーごとの秘密値)に保存され、ビルド時にプレイヤーへ焼き込まれます。コードで現在の設定を読むには OZeroSecurityConfig.Instance を使い、エディタースクリプトから変更するには OZeroSecurityProjectSettings(Load、LoadOrCreate、Save)を使います。
フィールド
フィールドは Response や Integrity などのネストされた設定クラスにグループ化されます。この表は、コードから確認することが多い項目や統合時によく調整する項目の要約です。Inspector の全設定表は manual を参照してください。デフォルト値はコードに保存されたシリアライズ初期値を基準にしています。リリースビルドで実行時の値が強制的に変わる項目は * で示します。
| フィールド | 型 | デフォルト | 説明 |
|---|---|---|---|
| — トップレベル — | |||
| developerSecret | string | "" | OZeroSV_File と OZeroSafePlayerPrefs のデータを保護するプロジェクト別 secret です。最初のリリース前に Config Dashboard の Generate Secure Secret で生成し、リリース後は変更しないでください。変更すると既存の保護データを新しいビルドで復号できません。 |
| enableLog | bool | true | SDK のデバッグログを有効にします。開発/QA の原因調査には役立ちますが、リリース時のログ公開ポリシーは別途確認してください。 |
| enableFailureDiagnostics | bool | false | セキュリティ失敗の診断ファイルを Application.persistentDataPath に保存します。QA またはカスタマーサポート調査中だけ有効にし、確認後は再度オフにしてください。 |
| — Response — | |||
| response.forceQuitOnDetection | bool | true | 脅威が確認されたときにアプリを自動終了するかどうかを決定します。QA中はオフにしてイベントだけを観察できますが、リリースビルドではプロジェクトポリシーに合わせて明確に選択してください。 |
| response.fatalCallbackGraceSeconds | float | 10 | 脅威検知後、ゲーム側のセキュリティコールバック UI がプレイヤーへ案内できる最大時間(秒)です。即時終了したい場合だけ 0 に設定してください。 |
| securityTerminationNoticePolicy | OZeroSecurityTerminationNoticePolicy | Disabled | すでに保留中の致命的セキュリティ終了に対する、全ライセンス共通の任意通知ポリシーです。 |
| securityUiDialogPrefabResourcePath | string | "" | 共有セキュリティ UI prefab の任意の Resources パスです。空欄の場合は SDK の既定値を使用します。 |
| securityUiLanguageCode | string | auto | 共有セキュリティ UI 文字列の言語コードです。auto は Application.systemLanguage に従います。 |
| securityUiFallbackLanguageCode | string | en | 選択した共有ローカライズ JSON がない場合のフォールバック言語です。 |
| — Managed Verification UI — | |||
| Security Config > Pro - License & Online Services > Online Verification UI > Ui Policy (Pro) | OZeroManagedVerificationUiPolicy | BuiltInBlockingDialog | OZero Managed Build Integrity と Steam DRM の状態をユーザーへ表示する UI フローを選択します。内蔵モードでは SDK が nonce/attest、managed session verification、オンライン必須通知、再試行、timeout UI を処理します。SDK ダイアログを置き換える場合だけ Custom UI / Callback Only を使ってください。 |
| Security Config > Pro - License & Online Services > Online Verification UI > Retry Timeout Seconds (Pro) | int | 15 | プレイヤーが Retry を押してから retry-timeout として報告するまで待機する最大秒数です。 |
| Security Config > Pro - License & Online Services > Online Verification UI > Online Required Timeout Seconds (Pro) | int | 120 | オンライン必須状態を維持し、設定された timeout action を適用するまで待機する最大秒数です。 |
| Security Config > Pro - License & Online Services > Online Verification UI > Timeout Action (Pro) | OZeroManagedVerificationTimeoutAction | BlockSession | timeout 時にダイアログを維持する、保護セッションをブロックする、アプリを終了する、または callback のみ呼び出すかを選択します。 |
| Security Config > Pro - License & Online Services > Online Verification UI > Auto Retry When Network Restored (Pro) | bool | false | ネットワーク接続が復旧したときに managed verification を自動的に再試行します。明示的なプレイヤー操作なしで再試行しても安全なゲームフローでのみ有効にしてください。 |
| — Integrity — | |||
| integrity.useIntegrity | bool | true | Build Integrity モジュールを有効にするマスタースイッチです。 |
| integrity.validateOnStartup | bool | true | Start()で完全な整合性チェックを実行します。 |
| integrity.periodicCheckInterval | float | 300 | 定期的な再検証実行間隔(秒)です。コードのデフォルトは 300 で、0 以下にすると定期チェックを無効化します。 |
| integrity.checkAssemblyHash | bool | true | OZeroAssemblyManifestに対するコンパイル済みアセンブリのSHA-256 / 公開鍵トークン検証。 |
| integrity.checkDebugger | bool | true | アタッチされたマネージドデバッガー、Unityデバッグビルドフラグ、CPUタイミング異常を検知。 |
| integrity.checkPlatformNative | bool | true | プラットフォーム固有のネイティブチェック(Root、Jailbreak、APK署名、Windowsコード署名など)を実行。 |
| integrity.failIfManifestMissing | bool | false* | manifest の欠落または読み込み失敗を違反として処理します。*development 以外のプレイヤービルドでは、シリアライズ値に関係なく true に強制されます。 |
| integrity.failIfAssemblyHashBlobMissing | bool | false* | 生成されたアセンブリハッシュブロブの欠落を違反として処理します。*development 以外のプレイヤービルドでは true に強制されます。 |
| integrity.requireManifestSignature | bool | false* | manifest に有効な署名を要求します。キーは Window → OZero Security → Config & Dashboard の Generate Key Pair で生成してください。*リリースプレイヤービルドでは true に強制されます。 |
| integrity.il2cppHashGlobalGameManagers | bool | false | IL2CPP デスクトップ(Windows・macOS・Linux)のファイルハッシュに globalgamemanagers を含めます。Standard と Strict プリセットではオンになります。 |
| integrity.il2cppHashSharedAssets | bool | false | IL2CPP デスクトップのファイルハッシュに sharedassets* ファイルを含めます。Standard と Strict プリセットではオンになります。 |
| integrity.il2cppHashSceneFiles | bool | false | IL2CPP デスクトップのファイルハッシュに level* などの Unity シーンファイルを含めます。Standard と Strict プリセットではオンになります。 |
| integrity.blockEmulator | bool | true | (Android) エミュレーター検知を整合性違反として処理。 |
| Security Config > Build Integrity > Pro - Online > Check Integrity With Server (Pro) | bool | false | Pro の nonce → attest フローを有効にします。OZero Managed と既定の Managed Verification UI を使う場合、SDK がビルド整合性証拠の送信、managed session 判定要求、ユーザー向け retry/timeout 状態表示をゲームコードなしで処理します。 |
| Security Config > Build Integrity > Pro - Online > Attestation Verification Mode (Pro) | enum | OZeroManaged | 最終検証を行う側を選びます。既定値と Low・Standard セキュリティプリセットは OZeroManaged で、SDK が OZero に検証を依頼します。Strict は CustomerGameServer です。ゲームサーバーが発行した情報でクライアントが RequestGameServerAttestation(...) を呼び、ゲームサーバーが OZA トークンを検証します。サーバー検証には checkIntegrityWithServer を別途有効にしてください。ライセンスのティアが Pro でない場合、どのプリセットを適用しても OZeroManaged のままです。 |
| Security Config > Build Integrity > Pro - Online > Attestation Network Policy (Pro) | enum | RequireOnlineRevalidation | 対応するポリシーは RequireOnlineRevalidation のみです。サーバー検証を完了または更新できない場合は OnlineRequired 状態となり、オンラインでの再検証が必要です。未検証のオフラインセッションを許可する選択肢はありません。 |
| — InstallSource (Android) — | |||
| installSource.useInstallSource | bool | true | インストール元検証のマスタースイッチ。 |
| installSource.allowGooglePlayStore | bool | true | Google Playからのインストール許可(Galaxy Store、Amazon Appstore、AppGallery、OneStoreなど個別ストアフラグもトグル可能)。 |
| Security Config > Install Source (Android Only) > Pro - Online > Check Installer With Server (Pro) | bool | false | ローカル検出後に /v1/install-source/verify を呼び出し、Pro サーバーの allowlist と監査ログを適用します。 |
| installSource.allowDetectionFailed | bool | false | Android installer 照会そのものが失敗しても起動を許可します。検証済みの端末別理由がない限り、リリースではオフにしてください。 |
| installSource.allowUnknownSources | bool | false | 標準リストにも customAuthorizedPackages にもない installer package を許可します。 |
| — Steam Anti-Piracy — | |||
| steamAntiPiracy.useSteamAntiPiracy | bool | false | Steam 起動、権限、必須 DLC の所有、リリース hygiene 検査の master switch です。 |
| steamAntiPiracy.detectionAction | OZeroPCStoreDetectionAction | Callback | Steam 検証失敗時に使うローカル応答です。Observe は記録のみでコールバックを発生させません。Callback はセキュリティコールバックを発生させ、reportViolationToCallback がオフの場合は Observe に下がります。Block はその設定に関係なくコールバックを必ず発生させます。どちらの場合もアプリを終了するかどうかは forceQuitOnDetection が決めるため、Block だけではアプリは終了しません。Pro ポリシーがこの値を override する場合があります。 |
| Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Check Steam DRM With Server (Pro) | bool | false | サーバー根拠を使った Steam DRM 検証を有効にします。OZero Managed と既定の Managed Verification UI を使う場合、SDK が Steam ticket 送信、activation token cache、オンライン必須 UI、再試行、timeout 状態を処理します。 |
| Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Steam Drm Verification Mode (Pro) | enum | OZero Managed | OZero 管理検証または顧客ゲームサーバー検証のどちらで Steam DRM を運用するか選択します。OZero Managed は既定の Managed Verification UI と組み合わせると、自社バックエンドなしの統合経路になります。 |
| Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Steam Drm Network Policy (Pro) | enum | RequireOnlineRevalidation | Steam DRM 再検証が必要な時点でサーバーへ接続できない場合は、オンライン必須状態として処理します。既定の Managed Verification UI モードでは再試行フローが自動表示されます。 |
| — DeviceBinding — | |||
| deviceBinding.useDeviceBinding | bool | true | SDK 起動時の Device Binding 検証を有効にします。 |
| deviceBinding.hardwareChangeTolerance | int (0–3) | 1 | 端末フィンガープリントの構成項目がいくつ変わっても同じ端末とみなすかを決めます。 |
| Security Config > Device Binding > Pro - Online > Verify Device With Server (Pro) | bool | false | Pro 専用です。/v1/device/register と /v1/device/verify で端末フィンガープリントを登録・検証します。ネットワーク失敗だけではゲームを止めませんが、サーバーの明確な拒否は Device Binding 違反になります。 |
| — SpeedHack — | |||
| speedHack.useSpeedHack | bool | true | スピードハックディテクターのマスタースイッチ。 |
| speedHack.checkInterval | float | 1.0 | 検査間隔(秒)です。小さすぎる、または大きすぎる値は安全な範囲内に自動補正されます。 |
| speedHack.requiredDetections | int | 3 | 違反と判断する前に必要な連続した疑わしいサンプルの数です。小さすぎる、または大きすぎる値は安全な範囲内に自動補正されます。 |
| speedHack.detectSlowHack | bool | false | 低速化による時間操作も検出します。意図的なスローモーションでの誤検知を減らすため、既定値はオフです。 |
| speedHack.useWebTimeValidation | bool | true | 外部エンドポイントとのHTTPS HEADベースのゲーム時間クロス検証を有効化。 |
| speedHack.webTimeUrls[] | string[] | [] | Web 時刻のクロスチェックに使う HTTPS アドレス一覧です。自分で管理する、または信頼できるアドレスを 2 つ以上設定してください。リストが空の場合、Web 時刻検証に使えるアドレスはありません。 |
| speedHack.minSuccessfulEndpoints | int | 2 | 1 回の web-time 検査ラウンドを成功として扱うために、webTimeUrls のうち最低いくつが有効な Date ヘッダーを返す必要があるかを決めます。 |
| speedHack.maxConsecutiveFailures | int | 6 | web-time 検査ラウンドがこの回数だけ連続で失敗すると、onWebTimeUnavailable ポリシーを実行します。 |
| speedHack.onWebTimeUnavailable | WebTimeUnavailablePolicy | WarnOnly | Web/サーバー時刻を何度も確認できない場合のポリシーです。WarnOnly はログだけ残して継続します。Strict は繰り返し失敗後に SpeedHack イベントを発生させます。Silent はログも残さないため、特殊テスト用に留めてください。WebGL ではブラウザーのセキュリティポリシーにより他ドメインの Date ヘッダーを読めず、Web 時刻の確認が常に失敗するため、Strict も WarnOnly として適用されます。 |
| speedHack.enableRemoteSpeedHackConfig | bool | true | Pro サーバーから Speed & Time Hack の設定を取得します。新規設定の既定値は true で、セキュリティプリセットはライセンスのティアが Pro のときだけ有効にし、Standard では無効にします。ポータルで値を指定した項目だけがローカル値を上書きし、しきい値だけでなく detectSlowHack・enableTimeScaleDetection・enableThreadTimerCheck・enableSignedServerTime などの検知オン/オフも含まれます。ポータルで空のままの項目はローカル値を維持します。無効にした場合やサーバーに接続できない場合はローカル設定のみ使います。 |
| speedHack.remoteSpeedHackConfigInterval | float | 300 | /v1/speedhack-config を再取得する間隔です。0 は起動時に一度だけ取得します。 |
| speedHack.remoteSpeedHackConfigJitterPercent | float | 20 | Pro リモート設定要求が多くの端末で重ならないよう、設定した更新周期の前後に呼び出し時刻を分散する比率です。0〜75 に制限されます。 |
| speedHack.enableSignedServerTime | bool | false | Pro サーバー機能です。Pro アクティベーションが使えるとき、署名付き /v1/time を優先信頼時刻として使います。連続失敗後は設定済みの web-time endpoint に戻ります。 |
| — PhysicsHack — | |||
| physicsHack.useGlobalPhysicsHack | bool | true | すべての OZeroPhysicsHackDetector コンポーネントをまとめてオン/オフするグローバルスイッチです。オブジェクトごとの移動基準値は各コンポーネントの Inspector に残ります。 |
| Security Config > Pro - License & Online Services > Telemetry > Enable Remote Physics Config (Pro) | bool | true | Pro でアプリ起動時に全体の PhysicsHack 設定を取得します。新規設定の既定値は true で、セキュリティプリセットはライセンスのティアが Pro のときだけ有効にし、Standard では無効にします。ポータルで指定した項目(useGlobalPhysicsHack、telemetryThrottlePerMinute)だけがローカル値を上書きし、テレメトリーはサーバーから無効化のみ可能で有効化はできません。無効時やサーバー接続に失敗した場合はローカル設定を使います。キャラクターごとの移動しきい値は別の設定です。コードからは読み取り専用の EnableRemotePhysicsConfig で確認できます。 |
| Security Config > Pro - License & Online Services > Telemetry > Enable Server Telemetry (Pro) | bool | true | Pro 専用で、既定で有効です。有効なライセンスに権限がある限り、プロジェクトが無効化しなければ一般セキュリティイベントと詳細な PhysicsHack telemetry を送信します。無効化すると新しい telemetry 送信を停止し、サーバーポリシーはローカルで無効化された送信を有効化できません。 |
| Security Config > Pro - License & Online Services > Telemetry > Telemetry Throttle Per Minute (Pro) | int | 30 | このクライアントが 1 分間に送信できる PhysicsHack telemetry 数です。0 は無制限のため、調整ミスのある detector がサーバーを呼びすぎる可能性があり推奨しません。 |
| — Injection — | |||
| injection.useInjection | bool | true | Injection & Hooking 全体のスイッチです。プレイヤービルドでは設定された応答ポリシーに従って処理し、開発ビルドでは原因確認のため警告中心の診断フローで動作します。エディター Play Mode では Editor Play Mode Protection が Test のときだけ検査します。 |
| Security Config > Injection & Hooking > Pro - Online > Enable Remote Injection Config (Pro) | bool | true | Pro サーバーから Injection 設定を取得します。新規設定の既定値は true で、セキュリティプリセットはライセンスのティアが Pro のときだけ有効にし、Standard では無効にします。サーバーで指定した項目だけを上書きし、無効時はローカル設定のみ使います。サーバーホワイトリストの取得は enableServerWhitelist で別途設定します。 |
| Security Config > Injection & Hooking > Pro - Online > Enable Server Whitelist (Pro) | bool | false | Pro で署名付きのモジュール許可・拒否リストを取得します。enableServerWhitelist 一つで取得を切り替え、許可リストと拒否リストの有効期限はそれぞれ管理します。許可したモジュールはサイレント処理し、明示的に拒否したモジュールにはブロック方針を適用します。検証済みリストだけをキャッシュし、オフラインでは有効期限内の判定のみ使います。既定値は false です。 |
| Security Config > Injection & Hooking > Pro - Online > Enable Injection Scoped Exact (Pro) | bool | false | 観測したモジュールを、リスト全体を取得する代わりにハッシュ単位でサーバーに照会します。まだ提供されていません: OZero サーバーでこの照会機能が有効になっていないため、有効にしても効果はなく、SDK は通常のサーバーホワイトリストを使い続けます。無効のままにしてください。 |
| Security Config > Injection & Hooking > Pro - Online > Enable Injection Bloom Advisory (Pro) | bool | false | 署名付きのグローバルモジュールインデックスで照会の優先順位を決めます(参考のみ)。まだ提供されていません: グローバルインデックスのルートキーが公開されていないため、enableInjectionScopedExact と一緒に有効にすると root_unconfigured でビルドが失敗します。無効のままにしてください。 |
| injection.serverWhitelistRefreshInterval | float | 0 s | 実行中にサーバーリストを定期取得する間隔です。0 では定期取得だけを停止し、起動時の取得と期限切れ前の自動更新は継続します。 |
| injection.enableWindowsSystemModuleScan | bool | true | Windows の System32、SysWOW64、WinSxS からゲームにロードされたモジュールを検査します。有効にすると、システムフォルダー内にあるという理由だけでモジュールやフック先を信頼しなくなります。Low: false、Standard/Strict: true。モジュールの識別には enableWindowsModuleIdentityScan も有効にしてください。インラインフックの検査は別のオプションで設定します。 |
| injection.injectionWhitelistEntries | OZeroInjectionWhitelistEntry[] | empty | Injection Detector に登録するローカル信頼モジュールリストです。Pro 専用ではなく、すべてのティアで使用できます。ゲームと一緒に配布する、または QA、診断ファイル、Pro telemetry、OZero サポート案内で確認したモジュールだけ追加してください。 |
WebTimeUnavailablePolicy
設定済みの web-time アドレスが maxConsecutiveFailures ラウンド連続で失敗した後に適用されるポリシーです。一時的なネットワーク問題をログだけにするか、セキュリティコールバックへ上げるかを決めます。
| 値 | 説明 |
|---|---|
| WarnOnly | 既定値です。警告ログだけを残し、ゲームは続行します。オフラインでも動作する必要があるゲームには最も安全な選択です。 |
| Strict | 繰り返し失敗した後に SpeedHack コールバックを発生させます。ネットワーク品質が低い場合も web-time 失敗は起こり得るため、実際のサービス地域のネットワークでテストしてから使ってください。 |
| Silent | ログもコールバックも発生させません。短い互換性テスト用に留め、リリースビルドでは推奨しません。 |
OZeroLicenseConfig OZeroSDK.Security.License
オンラインライセンス設定です。エディターでは ProjectSettings/OZeroLicenseSettings.asset に保存され、Window > OZero Security > Online Services ウィンドウまたは Edit > Project Settings > OZero Security の Pro カードで編集し、ビルド時に暗号化リソースとしてプレイヤーに含まれます。ライセンスティアを選択し、Pro ライセンスキーと Pro ランタイムサーバー設定を保持します。設定ファイルがないかキーが空の場合、Standard と同じ方法で動作します。エディタースクリプトから変更するには OZeroSDK.Security.License.OZeroLicenseProjectSettings(Load、LoadOrCreate、Save、Reload)を使います。
フィールド
| フィールド | 型 | 説明 |
|---|---|---|
| tier | OZeroLicenseTier | Standard は完全オフラインで動作します。Pro はプロジェクトバインディング native variant とサーバーベースのランタイム機能を有効化します。 |
| licenseKey | string | プロジェクトに発行された Pro ライセンスキーです。Pro はプロジェクト専用 Native Variant の確認、ランタイム有効化、サーバー機能に使います。空の場合は Standard と同じ方法で動作します。 |
| serverBaseUrl | string | Pro ランタイムサーバーの Base URL です。有効化、テレメトリー、signed time、attestation、サーバーポリシー呼び出しに使います。Standard ランタイムはこの URL を呼び出しません。 |
| serverPublicKeyHex | string | Pro 専用のサーバー署名公開キーです。Customer Portal > Server Key の Active publicKey をコピーして入力します。Pro ランタイムの signed activation、time、attestation、offline policy トークン検証に使います。Pro Variant manifest はこのフィールドではなく、SDK 内蔵の OZero Variant signing key で検証されます。 |
| previousServerPublicKeyHex | string | Pro サーバーキーのローテーション猶予期間だけ使う以前の署名用公開キーです。通常は空のままにしてください。 |
| offlineProPolicyMode | OZeroOfflineProPolicyMode | デバイスがオフラインのとき、ローカル Build Integrity が署名済み Pro ポータルブロックポリシーをどう適用するかを決めます。 |
| activationTimeoutSeconds | float | SDK が Pro アクティベーションの応答を待つ時間です。ポータルポリシーで許可されていれば、保存済みの Pro 情報を使用します。新しいオンライン確認が必要な場合、サーバー応答なしでは起動を続行できません。 |
| enableLog | bool | インスペクターに表示されなくなった旧フィールドです。ライセンスフローのログは OZeroSecurityConfig の Enable Debug Logs に従います。 |
| enableDevicePolicyHeartbeat | bool | Pro 専用。現在のデバイスが引き続き許可されている状態か定期的に確認します。 |
| devicePolicyHeartbeatInterval | float | Pro デバイスポリシー確認の基本間隔です。既定値は 300 秒で、0 にすると定期確認を無効にします。 |
| devicePolicyHeartbeatJitterPercent | float | 多くの端末が同時にデバイスポリシーを確認しないよう、設定周期の前後に確認時刻を少し分散する比率です。0〜75 に制限されます。 |
| enableSecurityLevelCheck | bool | Pro 専用。ビルドが期待されるセキュリティレベルを宣言しているかサーバーで確認できるようにします。 |
| declaredSecurityLevel | OZeroDeclaredSecurityLevel | このビルドがサーバーに宣言するセキュリティレベルです。 |
| failOnSecurityLevelReject | bool | true の場合、サーバーが宣言されたセキュリティレベルまたは設定ハッシュを明示的に拒否すると、設定された強い対応を実行します。 |
| securityLevelCheckInterval | float | サーバー側セキュリティレベル再確認の間隔です。0 は起動時に一度だけ確認します。 |
| securityLevelCheckJitterPercent | float | 多くの端末が同時にセキュリティレベルを再確認しないよう、設定周期の前後に確認時刻を少し分散する比率です。0〜75 に制限されます。 |
ローカル時刻ポリシー
このポリシーは Customer Portal > License > Local clock で管理するため、Unity 側で同期する設定項目はありません。オンラインの Pro アクティベーションにはサーバー時刻が使われます。許容差は、保存済みの Pro アクティベーション情報をオフライン起動に使用できるか判断するときだけ適用されます。
OZeroDeclaredSecurityLevel
セキュリティレベル検証を有効にしたとき Pro サーバーへ送信する enum です。サーバーはこの値がライセンスに設定された最小レベルを満たすか確認します。
| 値 | 説明 |
|---|---|
| Low | プロトタイプまたは開発ビルド向けのレベルです。サーバーポリシーが低い保護宣言を明示的に許可する場合だけ使ってください。 |
| Standard | 既定値で、通常の保護済みライブゲームビルドに推奨する宣言です。 |
| Strict | 最大保護の宣言です。strict ポリシーでプロジェクトが正常動作することを QA で確認したあとに使ってください。 |
OZeroOfflineProPolicyMode
デバイスがサーバーに接続できないとき、ローカル Build Integrity が署名済み Pro ポータルブロックポリシーをどう扱うかを決める enum です。値は fail-open 運用、fail-closed ポリシー強制、テスト bypass を明確に分けます。
| 値 | 説明 |
|---|---|
| ApplyCachedBlockPolicies | 推奨される fail-open 既定値です。オフライン中に有効な署名済みポリシーキャッシュがあれば適用し、使用可能なキャッシュがなければこのポリシーゲートは通過します。 |
| RequireFreshPolicy | 厳格な fail-closed モードです。使用可能な署名済みポリシーがない場合、サーバー Integrity が有効なら Build Integrity を失敗させます。 |
| IgnoreCachedBlockPolicies | Bypass モードです。オフライン中はキャッシュ済み Pro ブロックポリシーを読みません。テストや移行以外ではライブビルドに使わないでください。 |
プロパティ
Resources からランタイム設定をロードします。null の場合は Standard と同じ方法で処理してください。
Standard または空のライセンスキーであれば true です。Pro アクティベーションが必要な場合は false です。
Pro の場合 true です。Native Variant 検証対象の tier を識別し、private Variant の有無はインポート済みの署名 manifest から自動判定します。
現在のプロジェクトで明示的な native variant 互換性オーバーライドが有効な場合のみ true です。
OZeroLicenseRuntime OZeroSDK.Security.License
現在のライセンス状態を読み取る実行中 API です。アプリ起動時に自動初期化されるため、ほとんどのプロジェクトは状態を読み取るか HasCapability を呼び出すだけで十分です。
プロパティ
| 名前 | 型 | 説明 |
|---|---|---|
| Entitlement | OZeroLicenseEntitlement | 現在アクティベートされている Pro 権限情報です。Standard モードでは null です。 |
| HasEntitlement | bool | 現在の Pro アクティベーション情報が存在すれば true です。 |
| IsServerless | bool | SDK が Pro サーバー機能なしで実行中であれば true です。 |
| Initialized | bool | ライセンスランタイムの初回の起動処理が終わると true になります。 |
| IsProDowngraded | bool | SDK が明示的に Standard の動作へ切り替わった場合は true です。オンライン確認を必須とするポータルポリシーでは、この切り替えを使わずアプリの起動を停止します。 |
| DowngradeReason | string | 直近の自動ダウングレード(基本保護の切り替え)の診断理由です。 |
| CurrentDeviceId | string | 有効化とデバイスポリシーに使うデバイス ID です。既定の設定では Customer Portal とテレメトリに表示される値と同じです。必要に応じてゲームサーバーに保存してください。 |
| HasExternalUserId | bool | SetExternalUserId でユーザー UUID が紐付いている間は true です。 |
| LastServerBlockCode | string | セッションを終了させた最後のサーバー側ブロックのコードです。"USER_BLOCKED"(ユーザー UUID ブロック)または "DEVICE_BLOCKED"(デバイスブロック)で、ない場合は null です。セキュリティコールバックで読み取り、両者を区別してください。 |
| DeviceIdProvider | Func<string> | 有効化に使うデバイス ID を置き換えます。変更は推奨しません。サーバーがデバイスを別の ID で認識するようになり、テレメトリは元の ID のままになります。ゲームのアカウント ID を紐付けるには、デバイス ID を保つ SetExternalUserId を使ってください。 |
メソッド
複数回呼び出しても安全な開始メソッドです。通常は SDK が自動で呼び出し、カスタムブートストラップではライセンス状態を読み取る前に await することができます。
現在のアクティベーション情報に telemetry、signed_time、attestation などの機能権限があるか返します。Standard では false です。
[Pro] ユーザー UUID(アカウント ID)をこのセッションに紐付けます。サインイン後に呼び出してください。値はデバイス内でハッシュ化され、ハッシュだけが送信されます。前後の空白を除いて空の場合、128 文字を超える場合、制御文字を含む場合は false を返します。有効化前に呼び出しても構わず、ハッシュは有効化後に送信されます。およそ 5 秒後にデバイスポリシー確認が実行されます(最短 30 秒間隔)。
[Pro] ユーザー UUID の紐付けを解除します。サインアウト時に呼び出してください。
SHA-256("OZERO-EXTUID-V1|" + salt + "|" + uuid) の小文字 hex です。salt はライセンスの 64 文字 hex salt、uuid は前後の ASCII 空白を除いた値(大文字小文字は保持)です。テストベクター:salt 000102…1e1f、player-123 → 2d5a23607064e6dbc02dc32dc96af8cf54862d2f51d778596cfbe9d6ba8f8a3a。
ライセンスサーバーのランタイム呼び出し
Pro 機能は /v1 以下の HTTPS JSON API を使用します。OZA v2 は audience、256-bit challenge、session ID、build evidence に結合された1回限りの token です。/v1/validate は License Server API Key と同じ binding 値を必須とし、成功した token は常に原子的に consume されます。独自 backend がないチームは root-signed keyset と OZMS receipt を使用する OZero Managed を利用します。
| エンドポイント | 用途 |
|---|---|
| POST /v1/activate | 現在のデバイスで Pro ライセンスをアクティベートし、ローカルのアクティベーション情報を更新します。 |
| GET /v1/time | 有効になっている場合、Speed & Time Hack 検証に使用する signed server time を提供します。 |
| POST /v1/attest | 有効になっている無결성(無欠性)検査の通過後、固有のトークン ID が含まれた Pro build attestation トークンを発行します。nonce は送信されたビルド証拠とアプリの識別情報に紐づけられます。 |
| POST /v1/validate | ゲームサーバーで OZA v2 を検証します。Server API Key と audience/challenge/sessionId は必須で、成功した token は常に1回だけ consume されます。 |
| POST /v1/managed-session | 自社のバックエンドがないチームのために、OZero が Pro OZA トークンを検証し、許可/警告/遮断の結果と短いセッションを返します。SDK はセッション満了前に自動再検証を試み、同じトークンを再利用する流れは遮断されます。 |
| POST /v1/telemetry | Pro テレメトリ権限がアクティブになっている場合、セキュリティイベントをサーバーに送信します。 |
| /v1/customer-api/* | お客様サーバー API:read / block スコープを持つサーバー API キーで、ユーザー・デバイスの照会とユーザーブロックルールの管理を行います。お客様サーバー API を参照してください。 |
POST /v1/activate contract
Unity SDK が Pro ライセンスを有効化するときに送信する基本リクエスト契約です。サーバーの schema はネイティブ検証用フィールドを追加で受け取れますが、現在の SDK の基本アクティベーション要求は以下のフィールドを送信します。
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
| licenseKey | string | yes | /v1/activate で使う Pro ライセンスキーです。サーバーはキー形式、状態、ティア、有効期限を確認します(アクティベーション数と端末数は無制限)。 |
| deviceId | string | yes | デバイス識別子です。必要な場合は OZeroLicenseRuntime.DeviceIdProvider で独自に指定できます。 |
| sdkVersion | string | yes | クライアント SDK のバージョンです。 |
| platform | enum string | yes | Unity の実行プラットフォームです。サーバーは許可された platform 値だけを受け付けます。 |
| appIdentifier | string | optional | Unity アプリケーション識別子です。空でない場合だけ送信されます。 |
| companyName | string | optional | Unity PlayerSettings の会社名です。空でない場合だけ送信されます。 |
| productName | string | optional | Unity PlayerSettings の製品名です。空でない場合だけ送信されます。 |
| webglOrigin | string | optional | WebGL ビルドで検出した origin です。値がある場合だけ送信されます。 |
| フィールド | 型 | 説明 |
|---|---|---|
| activated | bool | アクティベーションが成功したかどうかです。正常応答では true です。 |
| tier | string | サーバーが認めたライセンス tier です。 |
| capabilities | string[] | このライセンスで使用できる機能一覧です。 |
| serverFeaturesEnabled | bool | Pro サーバー機能を使用できるかどうかです。 |
| signedToken | string | サーバーが署名したアクティベーショントークンです。以降の Pro サーバー機能検証に使われます。 |
| keyId | string | 署名キーの識別子です。トークン検証キーのローテーションに使われます。 |
| expiresAt | number | アクティベーション情報の有効期限です。 |
code と message を持つ JSON です。主なコードは BAD_JSON, BAD_REQUEST, LICENSE_NOT_FOUND, LICENSE_PENDING, LICENSE_SUSPENDED, LICENSE_REVOKED, LICENSE_EXPIRED, DEVICE_BLOCKED, SERVER_NOT_CONFIGURED, SIGN_FAILED です。SDK は、ライセンス・デバイス・identity の明示的な拒否と一時的なネットワーク失敗を分けて扱います。
サーバー API キーでゲームサーバーと連携する
[Pro] サーバー API キーを使うと、ゲームサーバーは OZero と 3 つのことを行えます。サインイン時にプレイヤーのビルドが正規品か確認する(validate)、サポートや運用のためにユーザーとデバイスを照会する(read)、ユーザーをブロック・解除する(block)です。キーはゲームサーバーだけに置きます。ゲームクライアントはキーを持たず、OZero SDK を実行してトークンをお客様のサーバーに送るだけです。
概要
Game client (OZero SDK) Your game server OZero API
----------------------- ---------------- ---------
sign-in request -------> issue audience, challenge, sessionId
RequestGameServerAttestation()
TokenReady: attestToken ------> POST /v1/validate [validate] ----> valid / reason
allow or refuse the sign-in
SetExternalUserId(uuid) (the SDK sends only the hash h1 with policy checks)
support tool: GET /customer-api/... [read] ---->
sanctions: POST/DELETE user-blocks [block] ---->
policy check: callback or end <--------------------------------------------- rule applied
The server API key lives only on your game server. Never put it in the game client.
| やりたいこと | スコープ | エンドポイント |
|---|---|---|
| サインイン時にビルドが正規品か確認する | validate | POST /v1/validate |
| サポート:通報されたユーザーが使ったデバイス、最近の検知、特定のデバイスにそのユーザーが紐付いているか | read | GET /v1/customer-api/... |
| 不正ユーザーのブロック、期間付きの制裁、解除 | block(照会も行うなら + read) | POST / DELETE /v1/customer-api/user-blocks |
| CI からリリースビルドのセキュリティ設定ハッシュを登録する | config | GET / POST / DELETE /v1/customer-api/config-hashes |
サーバー API キーのスコープ
この API は既存のサーバー API キー(Customer Portal > サーバー鍵 > サーバー API キー)にスコープを付けて使います。キーの発行時にスコープを選び、キー一覧に各キーのスコープが表示されます。以前に発行したキーは validate のみです。
validate:/v1/validateによるトークン検証(従来の用途)。read:ユーザー、デバイス、イベント、ブロックルールの照会(すべてのGETリクエスト)。block:ユーザーブロックルールの作成・更新・解除(ユーザーのゲームを終了させる強制終了ルールを含む)。config:セキュリティ設定ハッシュの照会・登録・解除(/v1/customer-api/config-hashes、すべてのメソッド)。CI 用です。CI のキーにはconfigだけを与え、blockは与えないでください。
発行画面で block にチェックを入れると read も一緒にチェックされます(外すこともできます)。block だけの場合、ブロックルールの作成・解除はできますが、ユーザー・デバイス・ルール一覧は照会できません。照会も行うには read + block を使ってください。
block スコープを持つキーは、強制終了を含めてユーザーをブロックできます。サーバー API キーはお客様のサーバーだけに置き(環境変数など)、ゲームクライアントには決して入れないでください。
認証と制限
すべてのリクエストは https://api.ozerosecurity.com/v1 に Authorization: Bearer <サーバー API キー> を付けて送り、有効な Pro ライセンスだけが利用できます。POST /v1/validate には validate スコープが必要で、IP ごとの制限があります。/v1/customer-api/* は GET に read、ルール変更に block が必要で(例外:/v1/customer-api/config-hashes は GET を含むすべてのメソッドに config が必要です)、キーごとに毎分 120 回と IP ごとの制限が適用されます。一覧は limit と before(前のページの nextBefore)でページ分割します。
1. 検証(validate):正規のビルドか?
SDK がビルドの整合性の証拠を OZero に送り、お客様のサーバーが決めた値に結び付いた使い切りの OZA アテステーショントークンを受け取ります。POST /v1/validate は、そのトークンがこのライセンスの正規ビルドに対して OZero がこのサインイン用に発行し、まだ使われていないことをお客様のサーバーに伝えます。サインイン、ランク戦、購入の前に使ってください。Online Services > Build Integrity > Check Integrity With Server をオンにし、Attestation Verification Mode を CustomerGameServer に設定する必要があります。
- ゲームサーバーがこのサインイン用のバインディングを発行します。
audience(ゲーム固有の名前、英数字と._:-で 3~128 文字)、challenge(毎回新しく作る 32 バイトの乱数を hex 64 文字で)、sessionId(同じ文字で 16~128 文字)です。 - クライアントが
OZeroOnlineAttestation.RequestGameServerAttestation(audience, challenge, sessionId)を呼び出します。上の設定がオフの場合、ローカル検査がまだ通っていない場合、実行中の場合、値が不正な場合は false を返します。 - トークンは
OZeroOnlineAttestation.TokenReady(またはRegisterTokenCallback)で届きます。クライアントはcontext.AttestTokenをゲームサーバーに送ります。 - ゲームサーバーが
validateスコープを持つキーでPOST /v1/validateを呼び出します。本文は{ attestToken, licenseKey, audience, challenge, sessionId, enforceVersionWhitelist }で、自分が発行した値をそのまま使います。 valid: trueならサインインを許可します。payloadからプラットフォーム、SDK・アプリのバージョン、マニフェストハッシュなどが分かります。valid: falseなら拒否してreasonを記録します。トークンは一度しか検証できず、2 回目の呼び出しはTOKEN_REPLAYEDを返します。
ゲームクライアント(Unity):
using OZeroSDK.Online;
using OZeroSDK.Security;
using UnityEngine;
public sealed class OZeroSignInAttestation : MonoBehaviour
{
private void OnEnable() => OZeroOnlineAttestation.RegisterTokenCallback(OnToken);
private void OnDisable() => OZeroOnlineAttestation.UnregisterTokenCallback(OnToken);
// 1) Your game server answered the sign-in request with a binding.
public void OnBindingReceived(string audience, string challenge, string sessionId)
{
if (!OZeroOnlineAttestation.RequestGameServerAttestation(audience, challenge, sessionId))
Debug.LogWarning("OZero attestation did not start (check Security Config > Build Integrity > Pro - Online).");
}
// 2) The SDK issued a token bound to that binding: send it to your game server.
private void OnToken(OZeroAttestationTokenContext context)
{
MyGameServer.SendAttestToken(context.AttestToken); // your networking code
}
}
リクエストとレスポンス:
POST https://api.ozerosecurity.com/v1/validate
Authorization: Bearer <server API key with the validate scope>
Content-Type: application/json
{
"attestToken": "<header>.<payload>.<signature>",
"licenseKey": "<your license key>",
"audience": "my-game-login",
"challenge": "<64 hex characters, new for every sign-in>",
"sessionId": "4b1d6c2e-8f3a-4c55-9a0e-2f1b7d9c3e11",
"enforceVersionWhitelist": false
}
200 OK (genuine build)
{
"valid": true,
"reason": null,
"payload": {
"licenseSubject": "...", "platform": "...", "sdkVersion": "1.2.0", "appVersion": "...",
"manifestHash": "...", "integrityFlags": "...", "deviceBindingHash": "...",
"deviceBinding": null, "installSource": null, "countryCode": null,
"issuedAt": "...", "expiresAt": "...", "tokenId": "..."
}
}
403 Forbidden (rejected)
{ "valid": false, "reason": "TOKEN_REPLAYED", "payload": null }
/v1/validate の主な結果:
4xx・5xx のレスポンスにも JSON の本文があります。エラーを投げる前に本文を読んでください。キーやリクエストのエラーは { "code", "message" }、拒否されたトークンは { "valid": false, "reason" } を返します。
| HTTP | code / reason | 説明 |
|---|---|---|
| 401 | SERVER_API_KEY_REQUIRED | キーがない、不明または失効したキー、または validate スコープのないキーです。 |
| 503 | SERVER_NOT_CONFIGURED | ライセンスに validate スコープを持つ有効なサーバー API キーがありません(read、block、config のみのキーは対象外)。Customer Portal で validate スコープ付きのキーを発行してください。 |
| 400 | BAD_JSON, BAD_REQUEST | 本文が正しい JSON ではない、またはフィールドの形式が誤っています。 |
| 403 | TOKEN_REPLAYED | すでに検証済みのトークンです。新しいバインディングを発行し、クライアントに新しいトークンを要求してください。 |
| 403 | AUDIENCE_MISMATCH, CHALLENGE_MISMATCH, SESSION_MISMATCH | 送った値で発行されたトークンではありません。発行した audience、challenge、sessionId をそのまま渡しているか確認してください。 |
| 400 / 401 | MALFORMED_TOKEN, INVALID_SIGNATURE, TOKEN_EXPIRED | トークンが破損、偽造、または期限切れです。サインインを拒否してください。 |
| 403 | MANIFEST_HASH_NOT_WHITELISTED, BUILD_BLOCKED, COUNTRY_BLOCKED, DEVICE_MISMATCH | ライセンスポリシーがビルドまたはデバイスを拒否しました(enforceVersionWhitelist を有効にしたバージョンホワイトリスト、ブロックされたビルド、ブロックされた国、デバイスの不一致)。 |
| 403 | LICENSE_*, TIER_REQUIRED | ライセンスが利用できない状態です(停止、期限切れ、失効、保留中、または Pro ではない)。 |
2. 照会(read):ユーザーとデバイスを調べる
サポートでよくある質問:通報されたユーザーがどのデバイスでプレイしたか、OZero が最近何を検知したか、通報のデバイスにこのユーザーが紐付いているか。ユーザーの UUID とライセンスの salt で h1 を計算し、下の GET エンドポイントを呼び出してください。サーバーはキーのハッシュだけを保存するため、ユーザーの h1 の一覧は返せません。デバイス上の特定ユーザーを確認するには ?h1= を渡してください。
| エンドポイント | スコープ | 用途 |
|---|---|---|
| GET /customer-api/users/{h1}/devices | read | そのユーザーが観測されたデバイス(初回・最終観測、イベント数)と、適用中のルール(block、ない場合は null)。 |
| GET /customer-api/users/{h1}/events | read | そのユーザーの最近のイベント(新しい順)。source は telemetry、physics、policy のいずれかです。from / to(Unix ms)で期間を絞り込みます。 |
| GET /customer-api/devices/{deviceId}/users | read | デバイスに紐付いたユーザー:count と各ユーザーのキーの先頭 8 文字。サーバーは h1 を保存しないため、ユーザーのハッシュ一覧は返せません。?h1= を渡すと、そのユーザーがこのデバイスに紐付いているかを linked(true/false)で返します。 |
| GET /customer-api/user-blocks | read | ブロックルールの一覧(新しい順)。?active=true で適用中のルール(解除・期限切れでないもの)だけを返します。 |
GET /users/{h1}/devices のレスポンス例:
{
"userKeyPrefix": "a1b2c3d4",
"devices": [
{ "deviceId": "...", "firstSeenAt": 1789900000000, "lastSeenAt": 1790000000000, "eventCount": 12 }
],
"block": null
}
3. ブロック(block):制裁と解除
主な使い方:不正が確定したら自動でブロックする、運用ツールから期間付きの制裁をかける、異議申し立てが認められたら解除する。callback は対応をゲームコードに任せ、terminate はゲームを終了させます。同じユーザーに再度送ると、適用中のルールが更新されます。
| エンドポイント | スコープ | 用途 |
|---|---|---|
| POST /customer-api/user-blocks | block | ルールの作成:{ "h1", "action": "callback" | "terminate", "reason", "expiresInDays" または "expiresAt" }。期限を指定しない場合は解除するまで有効です。ユーザーごとに適用中のルールは 1 つなので、同じユーザーに再度送ると更新されます(201 ではなく 200)。 |
| DELETE /customer-api/user-blocks/{id} | block | ルールを解除します。繰り返し呼び出しても問題ありません。 |
ゲームで起きること:ゲームが SetExternalUserId を呼び出してからおよそ 5 秒後(最短 30 秒間隔)のデバイスポリシー確認でルールが適用されます。callback は Module = "user_policy" の policy action をゲームに送り、terminate は policy action を先に送ってから OZ-SEC-0E の案内を表示してセッションを終了し、OZeroLicenseRuntime.LastServerBlockCode が "USER_BLOCKED" になります。マニュアルのデバイス ID とユーザー UUIDを参照してください。
4. 設定(config):セキュリティ設定ハッシュ(CI)
リリースビルドごとにハッシュを登録すると、セキュリティレベル確認はお客様のビルドだけを通過させます(マニュアルのセキュリティ設定ハッシュの許可リストを参照)。すべてのメソッドに config スコープが必要です。ハッシュはビルドログの行 [OZero] Security config digest for this <Target> build: … の小文字 hex 64 文字です。
| エンドポイント | スコープ | 用途 |
|---|---|---|
| GET /customer-api/config-hashes | config | ポリシー(mode:enforced または compatibility、activeCount)、承認済みハッシュ(status:active、expired、revoked)、サーバーが受信したハッシュ(observed:初回・最終観測、観測回数、宣言レベル、SDK バージョン、プラットフォーム、approval)。 |
| POST /customer-api/config-hashes | config | ハッシュの承認:{ "configHash", "label"?, "expiresInDays"? または "expiresAt"?, "acknowledgeEnforcement"? }。{ hash, created, enforcementStarted } を返し、新しいハッシュは 201、既存のハッシュを再承認すると 200 です。期限を指定しない場合は解除するまで有効です。 |
| DELETE /customer-api/config-hashes/{configHash} | config | ハッシュを解除します。{ hash, policy } を返し、最後の有効なハッシュを解除すると policy.mode が compatibility になります。再度解除しても問題ありません。 |
"acknowledgeEnforcement": true が必要で、ない場合サーバーは 409 ENFORCEMENT_ACK_REQUIRED を返します。配布中のビルドをすべて確認したうえで最初の承認は Customer Portal で行い、以降のビルドは CI に登録させることをお勧めします。
リクエストとレスポンス:
POST https://api.ozerosecurity.com/v1/customer-api/config-hashes
Authorization: Bearer <server API key with the config scope>
Content-Type: application/json
{
"configHash": "<64 hex characters from the build log>",
"label": "1.2.0 release (Windows)",
"expiresInDays": 365,
"acknowledgeEnforcement": true
}
201 Created
{
"hash": {
"configHash": "...", "label": "1.2.0 release (Windows)", "status": "active",
"createdAt": 1790000000000, "createdBy": "api-key:...", "expiresAt": 1821536000000,
"revokedAt": null, "revokedBy": null
},
"created": true,
"enforcementStarted": true
}
409 Conflict (first approval without acknowledgeEnforcement)
{ "code": "ENFORCEMENT_ACK_REQUIRED", "message": "the first approved hash turns on enforcement: ..." }
GET https://api.ozerosecurity.com/v1/customer-api/config-hashes
{
"policy": { "activeCount": 1, "mode": "enforced" },
"approved": [ { "configHash": "...", "label": "...", "status": "active", "expiresAt": null, "...": "..." } ],
"observed": [ { "configHash": "...", "firstSeenAt": 1789900000000, "lastSeenAt": 1790000000000, "seenCount": 3,
"lastDeclaredLevel": 2, "lastSdkVersion": "1.2.0", "lastPlatform": "...", "approval": "active" } ],
"schemaReady": true
}
DELETE https://api.ozerosecurity.com/v1/customer-api/config-hashes/<configHash>
{ "hash": { "configHash": "...", "status": "revoked", "...": "..." },
"policy": { "activeCount": 0, "mode": "compatibility" } }
CI の例(bash、Unity ビルドの次のステップ):ビルドログ(-logFile で指定したファイル)からハッシュを読み取って登録します。キーは CI のシークレットに保存してください。
#!/usr/bin/env bash
# CI step after the Unity build. BUILD_LOG is the file passed to Unity with -logFile.
# OZERO_CONFIG_API_KEY: a server API key with only the config scope, stored as a CI secret.
set -euo pipefail
BUILD_LOG="${BUILD_LOG:-build.log}"
LABEL="${BUILD_LABEL:-ci build}" # letters, digits and spaces only (it goes into JSON as is)
HASH=$(grep -oE 'Security config digest for this [A-Za-z0-9]+ build: [0-9a-f]{64}' "$BUILD_LOG" \
| tail -n 1 | awk '{print $NF}')
if [ -z "$HASH" ]; then
echo "No security config digest in $BUILD_LOG" >&2
exit 1
fi
STATUS=$(curl -sS -o ozero-response.json -w '%{http_code}' -X POST \
"https://api.ozerosecurity.com/v1/customer-api/config-hashes" \
-H "Authorization: Bearer ${OZERO_CONFIG_API_KEY}" \
-H "Content-Type: application/json" \
-d "{\"configHash\":\"${HASH}\",\"label\":\"${LABEL}\",\"expiresInDays\":365}")
case "$STATUS" in
200|201) echo "Approved security config hash ${HASH}" ;;
409) echo "First approval turns on enforcement: approve once in the Customer Portal," \
"or send \"acknowledgeEnforcement\": true on purpose." >&2
cat ozero-response.json >&2; exit 1 ;;
*) echo "OZero API returned HTTP ${STATUS}:" >&2 # the body has { "code", "message" }
cat ozero-response.json >&2; exit 1 ;;
esac
| HTTP | code | 説明 |
|---|---|---|
| 400 | BAD_CONFIG_HASH, BAD_EXPIRY, BAD_JSON | configHash が hex 64 文字ではない、または期限の値が不正です。 |
| 403 | SCOPE_REQUIRED | キーに config スコープがありません。 |
| 404 | CONFIG_HASH_NOT_FOUND | このライセンスの一覧にないハッシュです(DELETE)。 |
| 409 | ENFORCEMENT_ACK_REQUIRED | acknowledgeEnforcement: true なしで最初の承認を要求しました。 |
| 503 | CONFIG_HASH_SCHEMA_NOT_READY | サーバーはまだセキュリティ設定ハッシュを処理する準備ができていません。しばらくしてから再試行してください。 |
お客様 API のエラー
4xx・5xx のレスポンスも本文に { "code", "message" } を返します。本文を読み、code で分岐してください。
| HTTP | code | 説明 |
|---|---|---|
| 401 | API_KEY_REQUIRED, API_KEY_INVALID | Authorization ヘッダーがない、またはキーが不明か失効しています。 |
| 403 | SCOPE_REQUIRED | キーにリクエストに必要なスコープ(read、block または config)がありません。 |
| 403 | PRO_REQUIRED | ライセンスが Pro ではありません。 |
| 403 | LICENSE_SUSPENDED, LICENSE_EXPIRED, LICENSE_REVOKED, LICENSE_PENDING | ライセンスが停止、期限切れ、失効、または保留中です。 |
| 400 | BAD_* | 不正なリクエストです。例:BAD_USER_HASH(h1 が小文字 hex 64 文字ではない)、BAD_ACTION、BAD_EXPIRY、BAD_DEVICE_ID、BAD_JSON。 |
| 404 | NOT_FOUND | このライセンスにそのルールはありません。 |
| 429 | RATE_LIMITED | リクエストが多すぎます(キーごとまたは IP ごと)。しばらくしてから再試行してください。 |
| 503 | EXTERNAL_USER_DISABLED | 現在、サーバーでユーザー UUID 連携を利用できません。 |
API で作成したルールは Customer Portal のルール一覧にサーバー APIと表示され、操作履歴には server-api-key … とキーの末尾 4 文字で記録されます。
5. 例: ゲームサーバーの 1 ファイルで試す
validate、read、block をすべて扱う Node.js モジュール全体(18 以上、追加パッケージなし)と、同じ流れの C#・Python の例です。キー、ライセンスキー、salt は環境変数から読み込みます。ハッシュ関数は使用前にテストベクターで自己確認します。429 と 503 は指数バックオフで再試行します。
// ozero-server.js - Node.js 18+ (built-in fetch and crypto, no extra packages)
const crypto = require("crypto");
const API = "https://api.ozerosecurity.com/v1";
const VALIDATE_KEY = process.env.OZERO_VALIDATE_API_KEY; // scope: validate (sign-in servers)
const OPS_KEY = process.env.OZERO_SERVER_API_KEY; // scopes: read + block (operations tools)
const LICENSE_KEY = process.env.OZERO_LICENSE_KEY;
const SALT = process.env.OZERO_EXTUID_SALT; // Customer Portal > User UUID > Settings
const AUDIENCE = "my-game-login";
// h1 = SHA-256("OZERO-EXTUID-V1|" + salt + "|" + uuid) as lowercase hex.
function extUserHash(salt, uuid) {
const id = uuid.replace(/^[ \t\n\v\f\r]+|[ \t\n\v\f\r]+$/g, "");
return crypto.createHash("sha256").update("OZERO-EXTUID-V1|" + salt + "|" + id, "utf8").digest("hex");
}
// Self-check with the published test vector.
const TEST_SALT = Array.from({ length: 32 }, (_, i) => i.toString(16).padStart(2, "0")).join("");
if (extUserHash(TEST_SALT, "player-123") !== "2d5a23607064e6dbc02dc32dc96af8cf54862d2f51d778596cfbe9d6ba8f8a3a") {
throw new Error("extUserHash does not match the test vector");
}
// 4xx and 5xx responses carry a JSON body with "code" (for example SCOPE_REQUIRED).
class OZeroApiError extends Error {
constructor(status, data) {
super(`OZero API ${status}: ${data.code ?? "HTTP_" + status} ${data.message ?? ""}`.trim());
this.status = status;
this.code = data.code ?? `HTTP_${status}`;
}
}
// One request helper: reads the JSON body for every status and retries 429 and 503
// with exponential backoff (0.5, 1, 2, 4 s).
async function call(key, method, path, body, attempt = 0) {
const res = await fetch(API + path, {
method,
headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
body: body ? JSON.stringify(body) : undefined,
});
if ((res.status === 429 || res.status === 503) && attempt < 4) {
await new Promise((resolve) => setTimeout(resolve, 500 * 2 ** attempt));
return call(key, method, path, body, attempt + 1);
}
return { status: res.status, data: await res.json().catch(() => ({})) };
}
async function callOk(key, method, path, body) {
const { status, data } = await call(key, method, path, body);
if (status >= 400) throw new OZeroApiError(status, data);
return { status, data };
}
// 1) validate --------------------------------------------------------------
function newSignInBinding() {
return {
audience: AUDIENCE,
challenge: crypto.randomBytes(32).toString("hex"),
sessionId: crypto.randomUUID(),
};
}
async function validateSignIn(binding, attestToken) {
const { status, data } = await call(VALIDATE_KEY, "POST", "/validate", {
attestToken,
licenseKey: LICENSE_KEY,
...binding,
enforceVersionWhitelist: false,
});
if (data.valid === true) return { ok: true, payload: data.payload };
if (data.valid === false) return { ok: false, reason: data.reason }; // e.g. TOKEN_REPLAYED: refuse
throw new OZeroApiError(status, data); // e.g. SERVER_API_KEY_REQUIRED: fix the key or setup
}
// 2) read ------------------------------------------------------------------
async function lookUpUser(uuid) {
const h1 = extUserHash(SALT, uuid);
const devices = await callOk(OPS_KEY, "GET", `/customer-api/users/${h1}/devices`);
const events = await callOk(OPS_KEY, "GET", `/customer-api/users/${h1}/events?limit=20`);
return { devices: devices.data.devices, block: devices.data.block, events: events.data.events };
}
async function isUserOnDevice(uuid, deviceId) {
const h1 = extUserHash(SALT, uuid);
const { data } = await callOk(OPS_KEY, "GET",
`/customer-api/devices/${encodeURIComponent(deviceId)}/users?h1=${h1}`);
return data.linked === true;
}
// 3) block -----------------------------------------------------------------
async function blockUser(uuid, action, reason, days) {
const body = { h1: extUserHash(SALT, uuid), action, reason }; // action: "callback" | "terminate"
if (days) body.expiresInDays = days; // omit to keep it until lifted
const { data } = await callOk(OPS_KEY, "POST", "/customer-api/user-blocks", body);
return data.block.id; // 201 created, 200 updated the user's rule in effect
}
async function liftBlock(ruleId) {
await callOk(OPS_KEY, "DELETE", `/customer-api/user-blocks/${ruleId}`);
}
// Branch on the error code, for example in your operations tool:
async function blockFromTool(uuid) {
try {
return await blockUser(uuid, "terminate", "Cheating", 30);
} catch (e) {
if (e.code === "SCOPE_REQUIRED") throw new Error("This key has no block scope: issue a read + block key.");
if (e.code === "API_KEY_INVALID") throw new Error("The key was revoked: deploy the new key.");
throw e;
}
}
module.exports = {
newSignInBinding, validateSignIn, lookUpUser, isUserOnDevice, blockUser, liftBlock, blockFromTool, OZeroApiError,
};
// C# (.NET 6+): the same flow
using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
// 4xx and 5xx responses carry a JSON body with "code" (for example SCOPE_REQUIRED).
public sealed class OZeroApiException : Exception
{
public OZeroApiException(int status, string code, string message)
: base($"OZero API {status}: {code} {message}".Trim())
{
Status = status;
Code = code;
}
public int Status { get; }
public string Code { get; }
}
public sealed class OZeroServerClient
{
// Leave out null fields such as expiresInDays when no expiry is given.
private static readonly JsonSerializerOptions Json = new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };
private static readonly Uri Api = new("https://api.ozerosecurity.com/v1/");
private readonly HttpClient _validate = Create(Env("OZERO_VALIDATE_API_KEY"));
private readonly HttpClient _ops = Create(Env("OZERO_SERVER_API_KEY"));
private readonly string _licenseKey = Env("OZERO_LICENSE_KEY");
private readonly string _salt = Env("OZERO_EXTUID_SALT");
private static string Env(string name) =>
Environment.GetEnvironmentVariable(name) ?? throw new InvalidOperationException(name + " is not set");
private static HttpClient Create(string key)
{
var http = new HttpClient { BaseAddress = Api };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", key);
return http;
}
public static string ExtUserHash(string salt, string uuid)
{
var id = uuid.Trim(' ', '\t', '\n', '\v', '\f', '\r');
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes("OZERO-EXTUID-V1|" + salt + "|" + id));
return Convert.ToHexString(bytes).ToLowerInvariant();
}
// Sends the request (retrying 429 and 503 with exponential backoff) and always reads the JSON body.
private static async Task<(int Status, JsonElement Body)> Send(Func<Task<HttpResponseMessage>> request)
{
for (int attempt = 0; ; attempt++)
{
using var res = await request();
bool retry = res.StatusCode is HttpStatusCode.TooManyRequests or HttpStatusCode.ServiceUnavailable;
if (retry && attempt < 4)
{
await Task.Delay(500 * (1 << attempt));
continue;
}
JsonElement body;
try { body = await res.Content.ReadFromJsonAsync<JsonElement>(); }
catch (JsonException) { body = default; } // not JSON (for example a proxy error page)
return ((int)res.StatusCode, body);
}
}
// Throws OZeroApiException with the server's code for 4xx and 5xx.
private static JsonElement Ok((int Status, JsonElement Body) r)
{
if (r.Status < 400) return r.Body;
string code = $"HTTP_{r.Status}", message = "";
if (r.Body.ValueKind == JsonValueKind.Object)
{
if (r.Body.TryGetProperty("code", out var c)) code = c.GetString() ?? code;
if (r.Body.TryGetProperty("message", out var m)) message = m.GetString() ?? "";
}
throw new OZeroApiException(r.Status, code, message);
}
// 1) validate: (true, payload) allows the sign-in, (false, reason) refuses it.
public async Task<(bool Ok, JsonElement Result)> ValidateSignIn(
string audience, string challenge, string sessionId, string attestToken)
{
var r = await Send(() => _validate.PostAsJsonAsync("validate", new
{
attestToken, licenseKey = _licenseKey, audience, challenge, sessionId, enforceVersionWhitelist = false,
}));
if (r.Body.ValueKind == JsonValueKind.Object && r.Body.TryGetProperty("valid", out var valid))
return valid.GetBoolean() ? (true, r.Body.GetProperty("payload")) : (false, r.Body.GetProperty("reason"));
Ok(r); // no "valid": a key or setup error such as SERVER_API_KEY_REQUIRED
throw new OZeroApiException(r.Status, $"HTTP_{r.Status}", "unexpected response");
}
// 2) read
public async Task<JsonElement> LookUpUser(string uuid) =>
Ok(await Send(() => _ops.GetAsync($"customer-api/users/{ExtUserHash(_salt, uuid)}/devices")));
// 3) block and lift
public async Task<string> BlockUser(string uuid, string action, string reason, int? days = null)
{
var body = Ok(await Send(() => _ops.PostAsJsonAsync("customer-api/user-blocks", new
{
h1 = ExtUserHash(_salt, uuid), action, reason, expiresInDays = days,
}, Json)));
return body.GetProperty("block").GetProperty("id").GetString()!;
}
public async Task LiftBlock(string ruleId) =>
Ok(await Send(() => _ops.DeleteAsync($"customer-api/user-blocks/{ruleId}")));
// Branch on the error code, for example in your operations tool.
public async Task<string?> BlockFromTool(string uuid)
{
try
{
return await BlockUser(uuid, "terminate", "Cheating", 30);
}
catch (OZeroApiException e) when (e.Code == "SCOPE_REQUIRED")
{
Console.Error.WriteLine("This key has no block scope: issue a read + block key.");
return null;
}
}
}
# Python 3 + requests: the same flow
import hashlib
import os
import secrets
import time
import uuid as uuidlib
import requests
API = "https://api.ozerosecurity.com/v1"
VALIDATE_KEY = os.environ["OZERO_VALIDATE_API_KEY"] # scope: validate
OPS_KEY = os.environ["OZERO_SERVER_API_KEY"] # scopes: read + block
LICENSE_KEY = os.environ["OZERO_LICENSE_KEY"]
SALT = os.environ["OZERO_EXTUID_SALT"]
def ext_user_hash(salt: str, uuid: str) -> str:
uid = uuid.strip(" \t\n\v\f\r")
return hashlib.sha256(f"OZERO-EXTUID-V1|{salt}|{uid}".encode("utf-8")).hexdigest()
assert ext_user_hash(bytes(range(32)).hex(), "player-123") == \
"2d5a23607064e6dbc02dc32dc96af8cf54862d2f51d778596cfbe9d6ba8f8a3a"
class OZeroApiError(Exception):
"""4xx and 5xx responses carry a JSON body with "code" (for example SCOPE_REQUIRED)."""
def __init__(self, status, data):
self.status = status
self.code = data.get("code") or f"HTTP_{status}"
super().__init__(f"OZero API {status}: {self.code} {data.get('message', '')}".strip())
def call(key, method, path, body=None):
"""Reads the JSON body for every status; retries 429 and 503 with exponential backoff."""
for attempt in range(5):
res = requests.request(method, API + path, json=body, timeout=10,
headers={"Authorization": f"Bearer {key}"})
if res.status_code not in (429, 503) or attempt == 4:
try:
data = res.json()
except ValueError:
data = {}
return res.status_code, data
time.sleep(0.5 * 2 ** attempt)
def call_ok(key, method, path, body=None):
status, data = call(key, method, path, body)
if status >= 400:
raise OZeroApiError(status, data)
return data
# 1) validate
def new_sign_in_binding():
return {"audience": "my-game-login", "challenge": secrets.token_hex(32), "sessionId": str(uuidlib.uuid4())}
def validate_sign_in(binding, attest_token):
status, data = call(VALIDATE_KEY, "POST", "/validate", {
"attestToken": attest_token, "licenseKey": LICENSE_KEY, **binding, "enforceVersionWhitelist": False})
if "valid" not in data:
raise OZeroApiError(status, data) # e.g. SERVER_API_KEY_REQUIRED
return data["valid"], data.get("payload") if data["valid"] else data.get("reason")
# 2) read
def look_up_user(uuid):
return call_ok(OPS_KEY, "GET", f"/customer-api/users/{ext_user_hash(SALT, uuid)}/devices")
# 3) block and lift
def block_user(uuid, action, reason, days=None):
body = {"h1": ext_user_hash(SALT, uuid), "action": action, "reason": reason}
if days:
body["expiresInDays"] = days
return call_ok(OPS_KEY, "POST", "/customer-api/user-blocks", body)["block"]["id"]
def lift_block(rule_id):
call_ok(OPS_KEY, "DELETE", f"/customer-api/user-blocks/{rule_id}")
# Branch on the error code, for example in your operations tool:
def block_from_tool(uuid):
try:
return block_user(uuid, "terminate", "Cheating", 30)
except OZeroApiError as e:
if e.code == "SCOPE_REQUIRED":
raise RuntimeError("This key has no block scope: issue a read + block key.") from e
raise
6. 運用のヒント
- 用途ごとにキーを分けてください。サインインサーバーには
validateキー、運用ツールだけにread+blockキーを置きます。そうすればサインイン用キーが漏れてもユーザーをブロックできません。 - CI には
configスコープだけを持つ専用のキーを与えてください。そうすれば CI のキーが漏れてもユーザーの照会やブロックはできません。 - 停止なしでキーを交換:新しいキーを発行してサーバーに配備し、その後 Customer Portal で古いキーを失効させます。
- 制限に注意してください。お客様 API はキーごとに毎分 120 回です。サポートの照会はまとめて行い、429 ではバックオフしてください。
- サーバー API キー、salt、
h1、元の UUID をログや分析データに残さないでください。h1は仮名の個人識別子として扱ってください。
OZeroAbortCode とイベントメッセージ
OZero がセキュリティ脅威を確定すると、OZeroSecurityEvent を作成し、SDK の基本対応フローとプロジェクトで登録したコールバックへ渡します。イベントには ModulationType、安定した公開 OZeroAbortCode、MessageKey、安全な英語 Message、WillAbort が含まれます。
Abort code とメッセージ表
| コード | OZeroAbortCode | ModulationType | MessageKey | メッセージ |
|---|---|---|---|---|
| 0x01 | MemoryModulation | MemoryModulation | memory_modulation | Protected memory value changed unexpectedly. |
| 0x02 | Injection | Injection | injection | Unexpected module, hook, or runtime injection signal detected. |
| 0x0A | BuildIntegrity | BuildIntegrity | build_integrity | Build integrity validation failed. |
| 0x0C | SpeedOrTimeHack | SpeedHack | speed_hack | Suspicious time scale or execution speed change detected. |
| 0x0C | SpeedOrTimeHack | TimeHack | time_hack | System clock or trusted time anomaly detected. |
| 0x0E | DeviceOrInstallPolicy | DeviceBindingModulation | device_binding | Device binding policy rejected the current device. |
| 0x0E | DeviceOrInstallPolicy | InstallSource | install_source | Application install source is not trusted. |
| 0x0F | PhysicsHack | PhysicsHack | physics_hack | Abnormal physics behavior exceeded the configured policy. |
| 0x10 | EnvironmentModulation | EnvironmentModulation | environment_modulation | Unsupported or unsafe runtime environment detected. |
| 0x13 | SteamAntiPiracy | SteamAntiPiracy | steam_antipiracy | Steam ownership or ticket validation failed. |
| 0x15 | InjectionScannerUnavailable | InjectionScannerUnavailable | injection_scanner_unavailable | インジェクションスキャナーが動作できず、SDK は無防備で続行する代わりにゲームを終了しました。ForceQuitOnDetection が無効でも発生します。 |
ログや多言語 UI を作成する際は、OZeroAbortCode と MessageKey を基準値として使用してください。Message は開発者が状況を理解しやすい安全な表現にしているため、開発者向け画面や QA ログにそのまま表示しても問題ありません。
ランタイムでのセキュリティイベント処理
OZero は基本的に、Config Dashboard の Response 設定に従ってアプリを終了するか、ログだけを残します。独自の警告画面を表示する、サーバーログを送る、終了直前に短い保存処理を行う必要がある場合は OZeroSecurityManager.RegisterUserCallback でハンドラを登録してください。
evt.WillAbort が true の場合、現在の応答ポリシーではコールバック処理後にアプリが終了します。この時間は analytics flush や最後の保存など、短時間で終わる処理だけに使ってください。コールバックはイベント報告と終了前処理の接続点であり、OZero のセキュリティ対応を取り消すためのものではありません。
using OZeroSDK.Security;
void OnEnable()
{
OZeroSecurityManager.Instance.RegisterUserCallback(OnHack);
}
void OnHack(OZeroSecurityEvent evt)
{
Debug.LogWarning(
$"OZero: {evt.Type} {evt.AbortCodeHex} {evt.MessageKey} - {evt.Message}");
if (evt.WillAbort)
{
// Last chance to flush your own analytics or save state.
}
Analytics.FlushSync();
}
Injection Detector API OZeroSDK.Security
Injection 検査で信頼済みとして扱うモジュールを登録する API です。ゲームと一緒に配布する overlay、録画ツール、運営用プラグイン、パートナーモジュールなど、正常動作だが検出される可能性があるものを例外にする場合に使います。HashHex は該当モジュールファイルの SHA-256 hash、SignerHex はモジュール署名証明書の SHA-256 hash です。値を推測して入力せず、実際の配布ファイルまたは診断結果で確認した値だけ登録してください。
データ構造 — OZeroInjectionWhitelistEntry
[Serializable]
public class OZeroInjectionWhitelistEntry
{
// SHA-256 of the matched module file. Lowercase 64-char hex. Required.
public string HashHex { get; set; }
// SHA-256 of the module's signing certificate. Lowercase 64-char hex.
// Empty ("") means "match by hash only" (only mode for Android .so / Linux ELF).
public string SignerHex { get; set; }
// Module file format hint — "pe" | "macho" | "so". Defaults to "so".
public string Type { get; set; }
// Optional human-readable note (UI / audit only — never sent to native).
public string Comment { get; set; }
}
Unity でローカル信頼モジュール項目を登録するときのデータ構造です。HashHex は必須の 64 文字 SHA-256 ファイル hash、SignerHex は任意の 64 文字 signer fingerprint です。Type は pe、macho、so のいずれかでモジュール形式を示し、Comment は運用者向けメモです。
ランタイム API — OZeroDispatch
// Returns true when trusted-module policy support is available.
public static bool HasInjectionV3 { get; }
// Replace trusted module entries atomically. Pass null/empty to clear.
// Returns false when the runtime support is unavailable.
public static bool RegisterInjectionWhitelistHash(OZeroInjectionWhitelistEntry[] entries);
// Trusted-module aware scan. Returns true when a relevant runtime signal is observed.
// Output fields are diagnostic context for your review and may be empty.
public static bool DetectAssemblyInjectionV3(
out bool silencedByWhitelist,
out string hashHex,
out string signerHex,
out string matchedModulePath);
実行中に信頼モジュールリストをネイティブ検査器へ渡す、または現在のプラットフォームでこの機能を使用できるか確認するときに使います。多くのプロジェクトでは Config Dashboard の Injection 設定だけで十分です。コードから直接呼び出す場合は、配布ファイル確定後に必要な項目だけ登録し、登録前後の結果を QA ログで確認してください。
Config — OZeroSecurityConfig.InjectionSettings
// Preferred local trusted-module surface in the Injection settings.
public OZeroInjectionWhitelistEntry[] InjectionWhitelistEntries { get; }
public bool EnableWindowsSystemModuleScan { get; }
InjectionWhitelistEntries は Injection 設定に含まれるローカル信頼モジュールリストです。顧客 PC に偶然インストールされたプログラムを広く許可するための設定ではなく、開発会社が一緒に配布し、正常動作を確認したモジュールだけ登録する場所です。このローカルリストはすべてのティアで使えます。Pro 顧客はカスタマーポータルのサーバー管理 whitelist で、運用中に同じ性質のポリシーを更新できます。
Injection 設定の enableWindowsSystemModuleScan の値を返す読み取り専用プロパティです。変更するには、Unity の Injection 設定を調整するか、Low・Standard・Strict のセキュリティプリセットを適用してください。
見慣れないシステムモジュールが見つかっても、すぐにゲームを終了することはありません。まず確認対象として記録し、繰り返し検出されたことやファイルに署名がないことだけを理由にブロックしません。既存の許可ルールは維持され、ハッシュで明示的に拒否したモジュールにはブロック方針が適用されます。このオプションは Unity の Injection 設定で変更できます。Pro ポータルからのリモート変更には対応していません。他のプラットフォームには影響しません。