OZero Security
APIリファレンス

APIリファレンス

OZeroSecurity SDKのすべての公開クラスとメソッドの完全なリファレンスです。特に記載がない限り、すべてのクラスはOZeroSDK.Security名前空間に属します。

OZeroSecurityManager OZeroSDK.Security

アクティブなセキュリティモジュールとセキュリティイベントを管理するグローバルマネージャーです。シーン遷移時も維持され、Instanceプロパティからアクセスできます。SDKがアプリ起動時に自動生成するため、直接インスタンス化する必要はありません。

プロパティ

名前 型 説明
Instance OZeroSecurityManager 静的シングルトンアクセサ。アクティブなインスタンスを返します。

メソッド

void RegisterUserCallback(DelegateSecurityViolation callback)

プロジェクト側でセキュリティイベントを受け取りたいときにコールバックを登録します。OZero の基本対応は別の流れで実行されるため、このコールバックを登録または解除しても SDK の保護動作は無効になりません。コールバックには、検出領域、公開 abort code、メッセージキー、診断メッセージ、アプリ終了予定の有無を含む OZeroSecurityEvent が渡されます。

void UnregisterUserCallback(DelegateSecurityViolation callback)

以前に登録したユーザーコールバックを削除します。メモリリーク防止のため、OnDisableまたはOnDestroyで必ず呼び出してください。

void RegisterSecurityTerminationNoticeCallback(Action<OZeroSecurityTerminationNoticeEvent> callback)

CustomCallbackOnly で使用するプロジェクトコールバックを登録します。ネイティブコードが取り消せない致命的終了期限を設定した後にのみ呼び出されます。通知表示や短いクリーンアップは可能ですが、終了の取り消しや期限延長はできません。

void UnregisterSecurityTerminationNoticeCallback(Action<OZeroSecurityTerminationNoticeEvent> callback)

登録済みのセキュリティ終了通知コールバックを解除します。

static void ExitNow()

すでに保留中の致命的セキュリティ終了を直ちに完了します。通常は通知ダイアログの OK ボタンから呼び出し、保留中の終了がなければ何もしません。

void RegisterUserPolicyActionCallback(Action<OZeroPolicyActionEvent> callback)

Pro device policy heartbeat を通じてサーバーから送られる policy action を受け取るコールバックを登録します。ポータルの Callback 対応ポリシーをゲームコードへ接続するための API で、ローカルのセキュリティ検知コールバックとは別に動作します。

void UnregisterUserPolicyActionCallback(Action<OZeroPolicyActionEvent> callback)

以前に登録した policy action コールバックを削除します。シーンオブジェクトで使う場合は OnEnable で登録し、OnDisable で解除してください。

void RegisterUserActivationStateCallback(Action<OZeroUserActivationStateEvent> callback)

ユーザー向けの Activation / DRM 状態変化を受け取るコールバックを登録します。OZero-managed Steam DRM と既定の Managed Verification UI を使う場合、SDK がオンライン必須、再試行、timeout、ブロック状態を表示します。カスタム UI、telemetry、ゲーム固有の session gate が必要な場合だけ登録してください。

void UnregisterUserActivationStateCallback(Action<OZeroUserActivationStateEvent> callback)

以前に登録した Activation 状態コールバックを削除します。シーンオブジェクトで使う場合は OnEnable で登録し、OnDisable で解除してください。

void RegisterUserManagedVerificationStateCallback(Action<OZeroUserManagedVerificationStateEvent> callback)

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.

void UnregisterUserManagedVerificationStateCallback(Action<OZeroUserManagedVerificationStateEvent> callback)

Removes a previously registered managed verification state callback. Register in OnEnable and unregister in OnDisable when using a scene object.

void RegisterUserManagedVerificationTextProvider(IOZeroUserManagedVerificationTextProvider provider)

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.

void UnregisterUserManagedVerificationTextProvider(IOZeroUserManagedVerificationTextProvider provider)

Removes a previously registered managed verification text provider.

bool RequestBuildAttestationRetry()

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.

bool RequestSteamActivationRetry()

OZero サーバーが管理する Steam Activation / DRM 検証を再試行します。既定の Managed Verification UI は必要な retry フローを SDK 内で呼び出します。ゲームコードから呼ぶのは既定 UI を意図的に置き換えるカスタム UI の場合だけです。自社ゲームサーバーが Steam 検証を行う構成では、このメソッドではなく自社のログインまたはセッション要求を再送してください。

デリゲート

delegate void DelegateSecurityViolation(OZeroSecurityEvent evt)

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

ネイティブの致命的終了期限が設定された後にのみ発行されるユーザー向けペイロードです。元の安全なセキュリティイベントと、サポートへ報告できる安定したサポートコードを含みます。

名前 型 説明
SecurityEventOZeroSecurityEvent元の安全なイベントです。WillAbort は true で、ログとローカライズには AbortCode と MessageKey を使用します。
SupportCodestringプレイヤーがカスタマーサポートへ報告できる、安定した非機密コードです。
重要
UI 表示やコールバック呼び出しは、ネイティブの致命的終了期限を一時停止、取り消し、延長しません。処理は短時間に限定し、早期終了するときだけ OZeroSecurityManager.ExitNow() を呼び出してください。

OZeroSecurityEvent class

RegisterUserCallback に渡される顧客公開用の違反イベントです。内部の検知詳細情報の代わりに、安定して安全な診断情報のみを公開します。

名前 型 説明
TypeModulationType違反を発生させたセキュリティモジュールです。
AbortCodeOZeroAbortCode安定した公開中絶コードカテゴリです。
AbortCodeValueintサーバーログに使用するのに適した数字のコード値です。
AbortCodeHexstring0x0C のような16進数の文字列です。
MessageKeystringローカライズや analytics のグループ化に使用できる安定した英文メッセージキーです。
Messagestring顧客に公開可能な安全な英文診断メッセージです。
WillAbortboolコールバック返却後、または猶予時間が終わった後に現在の対応ポリシーがアプリを終了する予定であれば true です。

OZeroPolicyActionEvent class

RegisterUserPolicyActionCallback に渡されるサーバーポリシーのコールバックペイロードです。顧客ポータルのポリシーシステムから発行され、Pro device policy heartbeat により Unity メインスレッドで配信されます。

名前 型 説明
Modulestringvariant、injection、physics、user_policy(ユーザー UUID ブロックルール。強制終了ルールでセッションが終了する直前は ActionId = "user_blocked")などのポリシーモジュールです。
ActionIdstring配信を識別する一意の ID です。SDK はこの値で重複配信を防ぎ、サーバーへ ack を送ります。
PolicyIdstring利用可能な場合の顧客ポータル policy ID です。
Reasonstringポリシーに設定された理由、または一致した証拠から生成された理由です。
Scoreintポリシーが一致した時点のリスクスコアです。
EventCountintポリシー一致に反映されたイベント数です。
WindowMinutesintポリシー評価ウィンドウ(分)です。
IssuedAtUnixMslongサーバーが action を発行した UTC epoch milliseconds です。
ExpiresAtUnixMslongack されなかった 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 は、キャッシュ済みのオフライン実行がまだ許可されるか、オンライン再検証が必要かをこのイベントでゲームへ通知します。

名前 型 説明
Providerstringsteam などの Activation provider です。
Statestringvalid、revalidated、online_required、offline_degraded、rejected などの安定した状態値です。
Reasonstringsteam_activation_revalidate_unavailable や steam_activation_cache_expired などの reason code です。
Actionstring利用可能な場合、この状態に関連付けられたポータル policy action 値です。
OfflineAllowedbool署名検証済みトークンがオフライン許可期間内にある場合のみ true です。
OnlineRequiredbool保護セッションを許可する前に、ユーザーへネットワーク接続と再検証を求める必要がある場合は true です。
RefreshAtUnixMslongオンライン更新が必要になる UTC epoch milliseconds です。
OfflineUntilUnixMslong署名済みオフライン許可期間が終了する 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 が必要な場合だけです。

名前 型 説明
ProviderOZeroManagedVerificationProviderSource module: BuildIntegrity or SteamDrm.
StateOZeroUserManagedVerificationStateCurrent state such as Checking, Allowed, Warning, OnlineRequired, RetryTimedOut, Blocked, or StandardFallback.
ReasonstringStable reason code suitable for custom UI text, analytics, or customer support logs.
VerdictstringServer verdict when available, such as allow, warn, block, or fallback.
CanRetryboolTrue when a Retry action is meaningful for the current provider and state.
TimestampUnixMslongUTC 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 を押して修正してください。

値 説明
CustomCallbackOnlySDK は RegisterUserManagedVerificationStateCallback の状態だけを通知します。ゲーム側で独自 UI、再試行ボタン、timeout 処理、session gate をすべて実装する場合に使用します。
BuiltInBlockingDialogOZero の内蔵ブロッキングダイアログを使用します。オンライン再検証が必要な間、保護セッションを停止またはブロックすべきリリースビルドの推奨既定値です。
BuiltInNonBlockingDialogOZero の内蔵非ブロッキング通知を使用します。再試行や警告状態を案内している間もゲーム進行を安全に継続できる場合だけ使用してください。

OZeroSecurityTerminationNoticePolicy enum

すでに設定された致命的セキュリティ終了の直前に表示する任意のユーザー通知を制御します。Standard と Pro の両方で利用できます。

値 説明
Disabled = 0終了通知を表示しません。ネイティブの致命的終了期限は引き続き有効です。
OZeroBuiltIn = 1ローカライズ済みの OZero 組み込みダイアログを表示し、プレイヤーの確認時に早期終了します。
CustomCallbackOnly = 2登録済みの終了通知コールバックを呼び出して、プロジェクト独自の UI を表示します。終了の取り消しや延長はできません。

OZeroManagedVerificationTimeoutAction enum

オンライン必須または再試行状態が設定された制限時間内に解決しない場合、内蔵 managed verification UI が行う動作を決めます。

値 説明
KeepDialogtimeout 後もダイアログを表示し続けます。プレイヤーに手動再試行を続けさせる必要があるフローでのみ選択してください。
BlockSessiontimeout 後に保護セッションをブロックします。オンライン検証は必須だがアプリは開いたままにする場合の推奨既定値です。
AbortApplicationtimeout 後にアプリケーションを終了します。未解決の検証失敗で即時終了するリリースポリシーの場合だけ使用してください。
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の起動フローに合わせてセキュリティ設定を読み込み、ゲーム開始前に有効なディテクターを準備します。

公開APIなし — 呼び出し側でこの型をインスタンス化したり継承したり参照したりしないでください。サポートされる連携サーフェスはセキュリティ設定(OZeroSecurityConfig、Project Settings で編集)のみです。

OZeroSecurityConfigRuntime OZeroSDK.Security

保護されたビルド設定をプレイヤー実行時に読み込んで検証する構成ローダーです。パッケージ化された設定を検証し、メモリ上のOZeroSecurityConfig実行設定を準備し、検証失敗時は設定された脅威対応ポリシーを適用します。

プロパティ

名前 型 説明
Current OZeroSecurityConfig プレイヤービルドに含まれる保護設定を読み込んで作成した設定スナップショットです。初回アクセス時にEnsureLoaded()を呼び出します。

メソッド

static void EnsureLoaded()

繰り返し呼び出しても安全なローダーです。最初の呼び出しでパッケージ化された設定を検証・ロードし、以降の呼び出しでは同じ設定スナップショットを再利用します。失敗時の処理は設定された対応ポリシーに従います。

この型は内部ローダーです。サポートされる連携面はOZeroSecurityConfigとUnityエディターウィンドウと考えてください。

OZero Secure Variables OZeroSDK.Security

通常の数値、文字列、ベクター型の代わりに使用できる暗号化タイプです。値は保護されたメモリ領域に保存され、ほとんどの算術演算子と暗黙的な変換をサポートします。既存のコードで型名を変更するだけで適用できます。

対応型一覧

クラス 置き換え対象
OZeroSV_Intint
OZeroSV_Int64long
OZeroSV_UIntuint
OZeroSV_UInt64ulong
OZeroSV_Shortshort
OZeroSV_UShortushort
OZeroSV_Bytebyte
OZeroSV_Floatfloat
OZeroSV_Doubledouble
OZeroSV_Decimaldecimal
OZeroSV_Boolbool
OZeroSV_Stringstring
OZeroSV_Vector2Vector2
OZeroSV_Vector3Vector3
OZeroSV_Bufferbyte[]

対応演算子

数値型(Int、Int64、UInt、UInt64、Short、UShort、Byte、Float、Double、Decimal)はすべての算術(+ - * / %)、比較(== != < > <= >=)、複合代入(+= -= *= /=)、インクリメント/デクリメント(++ --)演算子と、対応する基本型との暗黙的変換をサポートします。Vector2・Vector3は算術および等価演算子をサポートします。Boolは等価演算子のみサポートします。Stringは==、!=、+をサポートします。Bufferはインデックス演算子を通じてバイト配列に直接アクセスできます。

Secure Types は、サポートされる基本演算での繰り返しのアロケーションを減らすように設計されています。ただし、文字列/バッファ変換、ログ出力、ボクシング、LINQ、ユーザーコードのパターンによっては GC が発生する可能性があるため、毎フレームの大量更新に適用する前にプロファイリングを行ってください。

OZeroSafePlayerPrefs OZeroSDK.Security

UnityのPlayerPrefsと同じように使用できる暗号化ストレージです。キー名と値が保護されているため、WindowsのレジストリやiOSの設定ファイルを直接開いても元の値を読み取るのは困難です。

メソッド

static void SetInt(string key, int value)
static int GetInt(string key, int defaultValue = 0)
static void SetFloat(string key, float value)
static float GetFloat(string key, float defaultValue = 0f)
static void SetString(string key, string value)
static string GetString(string key, string defaultValue = "")
static void SetInt64(string key, long value)
static long GetInt64(string key, long defaultValue = 0L)
static void SetDouble(string key, double value)
static double GetDouble(string key, double defaultValue = 0.0)
static void SetBool(string key, bool value)
static bool GetBool(string key, bool defaultValue = false)
static int IncrementInt(string key, int defaultValue = 0)
static bool HasKey(string key)
static void DeleteKey(string key)
static void DeleteAll()
static void Save()
static void Initialize(string newPassword = "", string newSalt = "") // obsolete compatibility no-op

よく使う PlayerPrefs 形式のメソッドに加えて、Int64、Double、Bool、IncrementInt ヘルパーを提供します。既存の plain PlayerPrefs 値は自動移行されないため、今後保護したい key から OZeroSafePlayerPrefs で保存してください。

OZeroSafePlayerPrefsで書き込まれたデータは標準のPlayerPrefsと互換性がありません。両者を切り替えると既存データが読めなくなります。

OZeroSV_File OZeroSDK.Security

内部の暗号化ロジックでファイルの読み書きを保護します。デバイスバインディングキーを使用しないため、Steamクラウドセーブのように複数のデバイスで同じセーブファイルを読み込む必要がある場合でも使用できます。ファイルを意図的に改ざんした場合、読み込み時に整合性チェックが失敗し、InvalidDataException が発生します。

暗号化されたファイルはSDK内部形式で保存されます。ファイル構造や特定位置を直接解析せず、読み書きは必ずOZeroSV_File APIを通して処理してください。

メソッド

static void WriteAllText(string path, string contents)

contentsを暗号化し、pathに保存します。親フォルダは自動作成されないため、必要な場合は保存前にDirectory.CreateDirectoryで作成してください。

static string ReadAllText(string path)

pathのファイルを読み取り、整合性を検証したうえで復号済み文字列を返します。ファイルが改ざんされている場合はInvalidDataExceptionを発生させます。

static void WriteAllBytes(string path, byte[] bytes)

データを暗号化し、pathに安全に保存します。

static byte[] ReadAllBytes(string path)

pathに指定されたファイルを読み込みます。読み込み時にファイル整合性を検証し、改ざんを検知します。

static string DecryptBytesToText(byte[] encryptedData)

ファイルパスではなく、すでにメモリに読み込まれた暗号化バイトバッファを復号します。リモートダウンロードやカスタムストレージから受け取ったデータを 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 AttestationPro サーバー検証がオンになっている場合、ローカル検査の通過後にサーバーに検証トークンを要求します。自社のゲームサーバーがない場合、OZero サーバーが許可/警告/遮断の結果を一緒に返すこともできます。

公開プロパティ

名前 型 説明
InstanceOZeroBuildIntegrityValidatorモジュールが作成されている場合の現在の validator インスタンスです。
LastValidationResultbool?直近のローカル検証結果です。初回実行前は null です。
IsValidatingbool検証実行中は true です。
IsIntegrityVerifiedbool最近アクティブになったローカル検査を通過すると true です。
OZeroOnlineAttestation.TokenOZeroBuildAttestationToken直近の Pro attestation トークンです。サーバー attestation が成功または失敗するまでは null であり、トークンには再利用追跡のための固有 ID が含まれます。

イベントとメソッド

UnityEvent OnValidationPassed { get; }

有効なすべてのローカル検査が通過したときに呼び出されます。

UnityEvent OnValidationFailed { get; }

有効なローカル検査または Pro attestation がビルドを拒否したときに呼び出されます。

static UnityEvent OZeroOnlineAttestation.OnAttestationPassed { get; }

オンラインアドオンでは OZeroOnlineAttestation.OnAttestationPassed です。サーバーアテステーションが成功し、OZeroOnlineAttestation.Token に有効なトークンがあるときに呼び出されます。

static bool OZeroOnlineAttestation.RequestGameServerAttestation(string audience, string challenge, string sessionId)

オンラインアドオンでは OZeroOnlineAttestation.RequestGameServerAttestation に移動しました。CustomerGameServer 専用で、ローカル検査が通った後、サーバーが発行した audience、hex 64 文字の challenge、session ID でアテステーションを開始します。トークンは OZeroOnlineAttestation.TokenReady で届きます。サーバー API キーでゲームサーバーと連携するを参照してください。

void Validate()

手動検証を開始します。通常のプロジェクトでは、ダッシュボードの起動時/定期検証設定を使用する方が良いでしょう。

OZeroBuildAttestationToken

Pro attestation の結果です。AttestToken をゲームサーバーへ渡し、ログイン、PvP、ランキング、財貨処理などのフローに使用する前に IsValid(nowMillis) で有効性を確認してください。

bool IsExpired(long nowMillis)

サーバーが発行した有効期限を過ぎている場合 true を返します。

bool IsValid(long nowMillis)

トークン発行が成功し、まだ期限切れでない場合 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 を限定的に猶予します。

メソッド

OZeroWatchdog.OZeroLoadingGraceScope BeginLoadingGrace(int maxGraceMs = 60000)

制限付きの loading grace scope を開始します。信頼できるロード処理が終わったら、返された scope の End() を呼ぶか dispose してください。ネストされた scope に対応し、最後の scope が終わると通常の Watchdog timing に戻ります。

void OZeroLoadingGraceScope.End()

この loading grace scope を手動で終了します。Dispose() も同じ処理を呼ぶため、using ブロックと明示的な End() は同じように動作します。

void RunWithLoadingGrace(Action work, int maxGraceMs = 60000)

同期ロード処理のための便利な 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);
    }
}
この API は Watchdog deadline だけを猶予します。他の保護モジュールを無効化したり、native heartbeat を public に公開したりするものではありません。信頼できるロード境界にのみ使用し、keep-alive 目的では使わないでください。

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)も提供します。

この API は QA ビルドで一時的に観察モードに変更したり、ゲーム内の運営者メニューから基本設定に戻す用途に適しています。Pro サーバーのポリシーが別途適用されるプロジェクトでは、ポータルのポリシーが優先される可能性があるため、実際の遮断ポリシーはポータルの設定と一緒に確認してください。

OZeroPCStoreDetectionAction

値 説明
Offローカルの Steam Anti-Piracy 応答を適用しません。制限された問題分析の状況でのみ使用してください。
Observe診断情報だけを記録し、ゲーム実行は継続して許可します。
CallbackOZeroSecurityManager コールバックを発生させ、ゲームが UI 表示、ログ記録、独自の処理を行えるようにします。このオプションだけではアプリは自動終了せず、実際の終了可否は Global Threat Response 設定とプロジェクトのコールバック処理方法に従います。
Block違反を遮断ポリシーとして扱います。実際のアプリの終了可否は Global Threat Response 設定に従います。

メソッド

static void SetDetectionActionOverride(OZeroPCStoreDetectionAction action)

実行中の Steam Anti-Piracy の検知後動作を変更します。QA ビルドで一時的に Observe に下げたり、運営者用メニューで特定の動作を選択させる場合に使用します。

static void ClearDetectionActionOverride()

実行中に変更した 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();

最後の検証結果

OZeroPCStoreAntiPiracyValidator.Instance.GetLastResult()

直近の Steam 検証スナップショットを返します。reported AppID、BuildID、SteamID、サーバー検証状態、soft signal、native score フィールドを含みます。デバッグ UI や QA レポートには使用できますが、ゲームプレイ権限の判断の唯一の基準として使用しないでください。

QueryDlcOwnership

static OZeroDlcOwnership QueryDlcOwnership(uint dlcAppId)

現在の Steam ユーザーが任意 DLC を所有しているかを照会します。ゲームを終了しない照会専用の API で、DLC コンテンツの解放などの判断に使います。requiredDlcAppIds に入れた必須 DLC は起動時に自動で確認されるため、この呼び出しは不要です。Steamworks.NET を直接呼び出す代わりにこの API を使ってください。

フィールド 説明
bool Owned現在のアカウントが DLC を所有しているか(Steam BIsSubscribedApp 基準)。解放の判断にはこの値を使ってください。
bool InstalledDLC がこの PC にインストールされているか。所有しているがインストールされていない状態(未発売またはダウンロードしていない DLC)は正常で、違反ではありません。
bool CrossCheckConsistentSteam API の応答どうしが整合しているかの確認結果です。応答間の整合性の確認にすぎず改ざんの証拠ではないため、この値だけでブロックを判断しないでください。
string Reasonowned、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();
}
コンテンツの解放はゲームのマネージドコードで行われるため、この照会結果に基づく判断はローカル保護の限界の範囲内にあります。ビルド全体の整合性(steam_api ハッシュ、ネイティブモジュール一覧、Valve 署名)は起動時に別途執行されます。

OZeroInstallSourceValidator OZeroSDK.Security

Android のインストール元検証器です。Install Source が有効な場合、コンポーネントは自動生成されます。顧客コードでは主にサポート UI、診断ログ、ストア別分岐処理のために最後の結果を読みます。

メソッドとイベント

event Action<InstallSourceResult> OnInstallSourceDetected

インストール元の確認が終わったときに呼び出されます。

InstallSourceResult GetLastResult()

直近の検査結果を返します。

InstallSourceResult GetAndroidInstallationSource()

必要に応じて初期化を行い、Android のインストール元結果を返します。

InstallSourceResult

DetectedSource解決されたインストール元です。型は AndroidInstallSource enum です。
RawInstallerPackageAndroid PackageManager が返した元の installer package name です。
IsAuthorizedローカル設定と、有効な場合は Pro サーバーポリシーがこのインストール元を許可しているなら true です。
ServerVerifiedPro 専用です。サーバー検証呼び出しが完了したなら true です。
ServerAuthorizedPro 専用です。ServerVerified が true の時のサーバー側の許可状態です。

AndroidInstallSource

InstallSourceResult.DetectedSource が返す正確な enum 値です。manual のストア名は読みやすい表示名であり、コードでは下記の enum 名で比較してください。

値 説明
GooglePlayStoreGoogle Play Store からインストールされた場合です。
SamsungGalaxyStoreSamsung Galaxy Store からインストールされた場合です。
AmazonAppstoreAmazon Appstore からインストールされた場合です。
HuaweiAppGalleryHuawei AppGallery からインストールされた場合です。
OneStoreONE Store からインストールされた場合です。
XiaomiGetAppsXiaomi GetApps からインストールされた場合です。
OppoAppMarketOPPO App Market からインストールされた場合です。
VivoAppStoreVivo App Store からインストールされた場合です。
Custom元の installer package が customAuthorizedPackages と一致した場合です。
ADBAndroid が空の installer package を返した場合です。通常は ADB または sideload 形式のインストールで発生します。
DetectionFailedJNI またはプラットフォーム API を利用できず、installer 照会そのものが失敗した場合です。ADB とは別です。
UnknownAndroid が package name を返したものの、標準リストにもカスタム許可リストにもない場合です。
EditorUnity Editor 内で実行中に返されます。
NotApplicableAndroid のインストール元という概念が適用されないプラットフォームで返されます。

OZeroDeviceBindingDetector OZeroSDK.Security

端末に紐づくセーブスロットとサポート用リセットフローのためのヘルパー API です。Device Binding が有効な場合、検証器は自動で開始されます。クラウドセーブ、アカウントセーブスロットなどを現在の端末に紐づけたい場合だけ、以下のトークンメソッドを直接呼び出してください。

メソッド

void Initialize()

ローカル端末フィンガープリントを準備し、登録または検証します。通常のプロジェクトでは SDK 起動時に自動で呼び出されます。

string BindToSaveSlot(string saveSlotKey)

セーブスロットキーを現在の端末に紐づけるトークンを作成します。トークンはセーブメタデータまたはサーバー記録に保存し、プレイヤーが直接編集できるセーブ本文には入れないでください。

bool ValidateSaveSlot(string saveSlotKey, string storedToken)

保存済みのセーブスロットトークンが現在の端末と一致するか確認します。一致しない場合、設定された Device Binding 違反応答が発生します。

string GetCurrentFingerprintHash()

現在の端末フィンガープリント hash を返すデバッグ/デモ用ヘルパーです。本番ゲームコードで表示、アップロード、保存しないでください。

void ClearStoredFingerprint(string authorizationToken = "")

この端末に保存された指紋を消去し、現在の端末を再登録します。プレイヤービルドで Pro ライセンスとサーバー設定がある場合、空でないトークンはバックグラウンドでサーバーが発行した Reset Token として検証されるため、解除はこの呼び出しが戻った後に行われます。結果を待つには ClearStoredFingerprintAsync を使ってください。それ以外の場合は認可コールバックが判断し、トークンを AuthorizationToken で受け取ります。サーバー Reset Token を使わない場合は RequestReset を使ってください。

Task<bool> ClearStoredFingerprintAsync(string authorizationToken)

ClearStoredFingerprint の await 可能な形式です。メインスレッドから呼び出して await し、指紋を消去した場合は true を返します。サーバー検査はメインスレッドをブロックしないため、HTTP がメインスレッドで動く環境(Windows・macOS の IL2CPP、WebGL)でもゲームが止まりません。

bool RequestReset(string reason = null)

保存済みの指紋の消去を要求します。プレイヤービルドでは認可コールバックが判断し、reason はログ用にコールバックへ渡されます。承認されると指紋を消去し、同じセッションで現在の端末を再登録して true を返します。エディターでは常に解除されます。

static void SetResetAuthorizer(Func<OZeroDeviceBindingResetRequest, bool> authorizer)

プレイヤービルドで解除を承認(true)または拒否するゲームのコールバックを登録します。null を渡すと登録を解除します。コールバックは解除を要求したスレッドで同期的に呼ばれます。認可コールバックがない場合、プレイヤービルドは有効なサーバー Reset Token がない限り解除を拒否します。

解除を無条件に承認しないでください。アカウントの再認証やサーバーが確認したサポートコードなど、実際の確認を経た後にだけ true を返してください。常に true を返すコールバックは、プロセス内の任意のコードにバインディングの消去を許してしまいます。同梱のデモシーンは、デモのためにすべての要求を承認します。

OZeroDeviceBindingResetRequest

認可コールバックに渡される読み取り専用の構造体です。

string Reason { get; }

RequestReset に渡した理由です。ない場合は空文字列です。

string AuthorizationToken { get; }

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.useGlobalPhysicsHackbooltrueすべての OZeroPhysicsHackDetector コンポーネントをまとめてオン/オフするグローバルスイッチです。オブジェクトごとの移動基準値は各コンポーネントの Inspector に残ります。
Security Config > Pro - License & Online Services > Telemetry > Enable Remote Physics Config (Pro)booltruePro でアプリ起動時に全体の PhysicsHack 設定を取得します。新規設定の既定値は true で、セキュリティプリセットはライセンスのティアが Pro のときだけ有効にし、Standard では無効にします。ポータルで指定した項目(useGlobalPhysicsHack、telemetryThrottlePerMinute)だけがローカル値を上書きし、テレメトリーはサーバーから無効化のみ可能で有効化はできません。無効時やサーバー接続に失敗した場合はローカル設定を使います。キャラクターごとの移動しきい値は別の設定です。コードからは読み取り専用の EnableRemotePhysicsConfig で確認できます。
Security Config > Pro - License & Online Services > Telemetry > Enable Server Telemetry (Pro)booltruePro 専用で、既定で有効です。有効なライセンスに権限がある限り、プロジェクトが無効化しなければ一般セキュリティイベントと詳細な PhysicsHack telemetry を送信します。無効化すると新しい telemetry 送信を停止し、サーバーポリシーはローカルで無効化された送信を有効化できません。
Security Config > Pro - License & Online Services > Telemetry > Telemetry Throttle Per Minute (Pro)int30このクライアントが 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)booltruePro サーバーから Injection 設定を取得します。新規設定の既定値は true で、セキュリティプリセットはライセンスのティアが Pro のときだけ有効にし、Standard では無効にします。サーバーで指定した項目だけを上書きし、無効時はローカル設定のみ使います。サーバーホワイトリストの取得は enableServerWhitelist で別途設定します。
Security Config > Injection & Hooking > Pro - Online > Enable Server Whitelist (Pro)boolfalsePro で署名付きのモジュール許可・拒否リストを取得します。enableServerWhitelist 一つで取得を切り替え、許可リストと拒否リストの有効期限はそれぞれ管理します。許可したモジュールはサイレント処理し、明示的に拒否したモジュールにはブロック方針を適用します。検証済みリストだけをキャッシュし、オフラインでは有効期限内の判定のみ使います。既定値は false です。
Security Config > Injection & Hooking > Pro - Online > Enable Injection Scoped Exact (Pro)boolfalse観測したモジュールを、リスト全体を取得する代わりにハッシュ単位でサーバーに照会します。まだ提供されていません: OZero サーバーでこの照会機能が有効になっていないため、有効にしても効果はなく、SDK は通常のサーバーホワイトリストを使い続けます。無効のままにしてください。
Security Config > Injection & Hooking > Pro - Online > Enable Injection Bloom Advisory (Pro)boolfalse署名付きのグローバルモジュールインデックスで照会の優先順位を決めます(参考のみ)。まだ提供されていません: グローバルインデックスのルートキーが公開されていないため、enableInjectionScopedExact と一緒に有効にすると root_unconfigured でビルドが失敗します。無効のままにしてください。
injection.serverWhitelistRefreshIntervalfloat0 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ログもコールバックも発生させません。短い互換性テスト用に留め、リリースビルドでは推奨しません。
developerSecretは最初のリリース前に必ず設定し、その後変更してはいけません。変更すると既存のセーブデータ(PlayerPrefsおよびファイル)を新しいビルドで復号できません。

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)を使います。

フィールド

フィールド 型 説明
tierOZeroLicenseTierStandard は完全オフラインで動作します。Pro はプロジェクトバインディング native variant とサーバーベースのランタイム機能を有効化します。
licenseKeystringプロジェクトに発行された Pro ライセンスキーです。Pro はプロジェクト専用 Native Variant の確認、ランタイム有効化、サーバー機能に使います。空の場合は Standard と同じ方法で動作します。
serverBaseUrlstringPro ランタイムサーバーの Base URL です。有効化、テレメトリー、signed time、attestation、サーバーポリシー呼び出しに使います。Standard ランタイムはこの URL を呼び出しません。
serverPublicKeyHexstringPro 専用のサーバー署名公開キーです。Customer Portal > Server Key の Active publicKey をコピーして入力します。Pro ランタイムの signed activation、time、attestation、offline policy トークン検証に使います。Pro Variant manifest はこのフィールドではなく、SDK 内蔵の OZero Variant signing key で検証されます。
previousServerPublicKeyHexstringPro サーバーキーのローテーション猶予期間だけ使う以前の署名用公開キーです。通常は空のままにしてください。
offlineProPolicyModeOZeroOfflineProPolicyModeデバイスがオフラインのとき、ローカル Build Integrity が署名済み Pro ポータルブロックポリシーをどう適用するかを決めます。
activationTimeoutSecondsfloatSDK が Pro アクティベーションの応答を待つ時間です。ポータルポリシーで許可されていれば、保存済みの Pro 情報を使用します。新しいオンライン確認が必要な場合、サーバー応答なしでは起動を続行できません。
enableLogboolインスペクターに表示されなくなった旧フィールドです。ライセンスフローのログは OZeroSecurityConfig の Enable Debug Logs に従います。
enableDevicePolicyHeartbeatboolPro 専用。現在のデバイスが引き続き許可されている状態か定期的に確認します。
devicePolicyHeartbeatIntervalfloatPro デバイスポリシー確認の基本間隔です。既定値は 300 秒で、0 にすると定期確認を無効にします。
devicePolicyHeartbeatJitterPercentfloat多くの端末が同時にデバイスポリシーを確認しないよう、設定周期の前後に確認時刻を少し分散する比率です。0〜75 に制限されます。
enableSecurityLevelCheckboolPro 専用。ビルドが期待されるセキュリティレベルを宣言しているかサーバーで確認できるようにします。
declaredSecurityLevelOZeroDeclaredSecurityLevelこのビルドがサーバーに宣言するセキュリティレベルです。
failOnSecurityLevelRejectbooltrue の場合、サーバーが宣言されたセキュリティレベルまたは設定ハッシュを明示的に拒否すると、設定された強い対応を実行します。
securityLevelCheckIntervalfloatサーバー側セキュリティレベル再確認の間隔です。0 は起動時に一度だけ確認します。
securityLevelCheckJitterPercentfloat多くの端末が同時にセキュリティレベルを再確認しないよう、設定周期の前後に確認時刻を少し分散する比率です。0〜75 に制限されます。

ローカル時刻ポリシー

このポリシーは Customer Portal > License > Local clock で管理するため、Unity 側で同期する設定項目はありません。オンラインの Pro アクティベーションにはサーバー時刻が使われます。許容差は、保存済みの Pro アクティベーション情報をオフライン起動に使用できるか判断するときだけ適用されます。

既定値は ローカル時刻を使用 / 10 分です。アプリ起動のたびにオンライン確認が必要なサービスでは、ローカル時刻を使用しないを選択してください。オンライン確認が必要な状態でサーバーに接続できない場合、Standard に切り替えず、アプリの起動を停止します。

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 を失敗させます。
IgnoreCachedBlockPoliciesBypass モードです。オフライン中はキャッシュ済み Pro ブロックポリシーを読みません。テストや移行以外ではライブビルドに使わないでください。

プロパティ

static OZeroLicenseConfig RuntimeInstance { get; }

Resources からランタイム設定をロードします。null の場合は Standard と同じ方法で処理してください。

bool IsServerlessMode { get; }

Standard または空のライセンスキーであれば true です。Pro アクティベーションが必要な場合は false です。

bool IsVariantTier { get; }

Pro の場合 true です。Native Variant 検証対象の tier を識別し、private Variant の有無はインポート済みの署名 manifest から自動判定します。

bool AllowStandardBuildWithPremiumLicense { get; }

現在のプロジェクトで明示的な native variant 互換性オーバーライドが有効な場合のみ true です。

OZeroLicenseRuntime OZeroSDK.Security.License

現在のライセンス状態を読み取る実行中 API です。アプリ起動時に自動初期化されるため、ほとんどのプロジェクトは状態を読み取るか HasCapability を呼び出すだけで十分です。

プロパティ

名前 型 説明
EntitlementOZeroLicenseEntitlement現在アクティベートされている Pro 権限情報です。Standard モードでは null です。
HasEntitlementbool現在の Pro アクティベーション情報が存在すれば true です。
IsServerlessboolSDK が Pro サーバー機能なしで実行中であれば true です。
Initializedboolライセンスランタイムの初回の起動処理が終わると true になります。
IsProDowngradedboolSDK が明示的に Standard の動作へ切り替わった場合は true です。オンライン確認を必須とするポータルポリシーでは、この切り替えを使わずアプリの起動を停止します。
DowngradeReasonstring直近の自動ダウングレード(基本保護の切り替え)の診断理由です。
CurrentDeviceIdstring有効化とデバイスポリシーに使うデバイス ID です。既定の設定では Customer Portal とテレメトリに表示される値と同じです。必要に応じてゲームサーバーに保存してください。
HasExternalUserIdboolSetExternalUserId でユーザー UUID が紐付いている間は true です。
LastServerBlockCodestringセッションを終了させた最後のサーバー側ブロックのコードです。"USER_BLOCKED"(ユーザー UUID ブロック)または "DEVICE_BLOCKED"(デバイスブロック)で、ない場合は null です。セキュリティコールバックで読み取り、両者を区別してください。
DeviceIdProviderFunc<string>有効化に使うデバイス ID を置き換えます。変更は推奨しません。サーバーがデバイスを別の ID で認識するようになり、テレメトリは元の ID のままになります。ゲームのアカウント ID を紐付けるには、デバイス ID を保つ SetExternalUserId を使ってください。

メソッド

static Task Initialize()

複数回呼び出しても安全な開始メソッドです。通常は SDK が自動で呼び出し、カスタムブートストラップではライセンス状態を読み取る前に await することができます。

static bool HasCapability(string cap)

現在のアクティベーション情報に telemetry、signed_time、attestation などの機能権限があるか返します。Standard では false です。

static bool SetExternalUserId(string userId)

[Pro] ユーザー UUID(アカウント ID)をこのセッションに紐付けます。サインイン後に呼び出してください。値はデバイス内でハッシュ化され、ハッシュだけが送信されます。前後の空白を除いて空の場合、128 文字を超える場合、制御文字を含む場合は false を返します。有効化前に呼び出しても構わず、ハッシュは有効化後に送信されます。およそ 5 秒後にデバイスポリシー確認が実行されます(最短 30 秒間隔)。

static void ClearExternalUserId()

[Pro] ユーザー UUID の紐付けを解除します。サインアウト時に呼び出してください。

ハッシュ形式(自社サーバーで照合する場合):SHA-256("OZERO-EXTUID-V1|" + salt + "|" + uuid) の小文字 hex です。salt はライセンスの 64 文字 hex salt、uuid は前後の ASCII 空白を除いた値(大文字小文字は保持)です。テストベクター:salt 000102…1e1f、player-123 → 2d5a23607064e6dbc02dc32dc96af8cf54862d2f51d778596cfbe9d6ba8f8a3a。
Standard は実行時のライセンスアクティベーションを行いません。Pro は、保存済みの情報が有効で、ポータルポリシーがオフライン利用を許可している場合にだけその情報を使用できます。新しいオンライン確認が必要な場合、保護レベルを下げず、接続に失敗するとアプリの起動を停止します。

ライセンスサーバーのランタイム呼び出し

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/telemetryPro テレメトリ権限がアクティブになっている場合、セキュリティイベントをサーバーに送信します。
/v1/customer-api/*お客様サーバー API:read / block スコープを持つサーバー API キーで、ユーザー・デバイスの照会とユーザーブロックルールの管理を行います。お客様サーバー API を参照してください。

POST /v1/activate contract

Unity SDK が Pro ライセンスを有効化するときに送信する基本リクエスト契約です。サーバーの schema はネイティブ検証用フィールドを追加で受け取れますが、現在の SDK の基本アクティベーション要求は以下のフィールドを送信します。

フィールド型必須説明
licenseKeystringyes/v1/activate で使う Pro ライセンスキーです。サーバーはキー形式、状態、ティア、有効期限を確認します(アクティベーション数と端末数は無制限)。
deviceIdstringyesデバイス識別子です。必要な場合は OZeroLicenseRuntime.DeviceIdProvider で独自に指定できます。
sdkVersionstringyesクライアント SDK のバージョンです。
platformenum stringyesUnity の実行プラットフォームです。サーバーは許可された platform 値だけを受け付けます。
appIdentifierstringoptionalUnity アプリケーション識別子です。空でない場合だけ送信されます。
companyNamestringoptionalUnity PlayerSettings の会社名です。空でない場合だけ送信されます。
productNamestringoptionalUnity PlayerSettings の製品名です。空でない場合だけ送信されます。
webglOriginstringoptionalWebGL ビルドで検出した origin です。値がある場合だけ送信されます。
フィールド型説明
activatedboolアクティベーションが成功したかどうかです。正常応答では true です。
tierstringサーバーが認めたライセンス tier です。
capabilitiesstring[]このライセンスで使用できる機能一覧です。
serverFeaturesEnabledboolPro サーバー機能を使用できるかどうかです。
signedTokenstringサーバーが署名したアクティベーショントークンです。以降の Pro サーバー機能検証に使われます。
keyIdstring署名キーの識別子です。トークン検証キーのローテーションに使われます。
expiresAtnumberアクティベーション情報の有効期限です。
失敗応答は 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 の明示的な拒否と一時的なネットワーク失敗を分けて扱います。
Pro アクティベーションは SDK が自動で処理します。一時的な接続問題では、保存済みの情報が有効で、ポータルポリシーがオフライン利用を許可している場合にだけ使用できます。オンライン確認が必要なポリシーでは、サーバーに接続できるまでアプリの起動を停止します。

サーバー 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.
やりたいこと スコープ エンドポイント
サインイン時にビルドが正規品か確認するvalidatePOST /v1/validate
サポート:通報されたユーザーが使ったデバイス、最近の検知、特定のデバイスにそのユーザーが紐付いているかreadGET /v1/customer-api/...
不正ユーザーのブロック、期間付きの制裁、解除block(照会も行うなら + read)POST / DELETE /v1/customer-api/user-blocks
CI からリリースビルドのセキュリティ設定ハッシュを登録するconfigGET / POST / DELETE /v1/customer-api/config-hashes

サーバー API キーのスコープ

この API は既存のサーバー API キー(Customer Portal > サーバー鍵 > サーバー API キー)にスコープを付けて使います。キーの発行時にスコープを選び、キー一覧に各キーのスコープが表示されます。以前に発行したキーは validate のみです。

発行画面で 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 に設定する必要があります。

  1. ゲームサーバーがこのサインイン用のバインディングを発行します。audience(ゲーム固有の名前、英数字と ._:- で 3~128 文字)、challenge(毎回新しく作る 32 バイトの乱数を hex 64 文字で)、sessionId(同じ文字で 16~128 文字)です。
  2. クライアントが OZeroOnlineAttestation.RequestGameServerAttestation(audience, challenge, sessionId) を呼び出します。上の設定がオフの場合、ローカル検査がまだ通っていない場合、実行中の場合、値が不正な場合は false を返します。
  3. トークンは OZeroOnlineAttestation.TokenReady(または RegisterTokenCallback)で届きます。クライアントは context.AttestToken をゲームサーバーに送ります。
  4. ゲームサーバーが validate スコープを持つキーで POST /v1/validate を呼び出します。本文は { attestToken, licenseKey, audience, challenge, sessionId, enforceVersionWhitelist } で、自分が発行した値をそのまま使います。
  5. 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 説明
401SERVER_API_KEY_REQUIREDキーがない、不明または失効したキー、または validate スコープのないキーです。
503SERVER_NOT_CONFIGUREDライセンスに validate スコープを持つ有効なサーバー API キーがありません(read、block、config のみのキーは対象外)。Customer Portal で validate スコープ付きのキーを発行してください。
400BAD_JSON, BAD_REQUEST本文が正しい JSON ではない、またはフィールドの形式が誤っています。
403TOKEN_REPLAYEDすでに検証済みのトークンです。新しいバインディングを発行し、クライアントに新しいトークンを要求してください。
403AUDIENCE_MISMATCH, CHALLENGE_MISMATCH, SESSION_MISMATCH送った値で発行されたトークンではありません。発行した audience、challenge、sessionId をそのまま渡しているか確認してください。
400 / 401MALFORMED_TOKEN, INVALID_SIGNATURE, TOKEN_EXPIREDトークンが破損、偽造、または期限切れです。サインインを拒否してください。
403MANIFEST_HASH_NOT_WHITELISTED, BUILD_BLOCKED, COUNTRY_BLOCKED, DEVICE_MISMATCHライセンスポリシーがビルドまたはデバイスを拒否しました(enforceVersionWhitelist を有効にしたバージョンホワイトリスト、ブロックされたビルド、ブロックされた国、デバイスの不一致)。
403LICENSE_*, TIER_REQUIREDライセンスが利用できない状態です(停止、期限切れ、失効、保留中、または Pro ではない)。

2. 照会(read):ユーザーとデバイスを調べる

サポートでよくある質問:通報されたユーザーがどのデバイスでプレイしたか、OZero が最近何を検知したか、通報のデバイスにこのユーザーが紐付いているか。ユーザーの UUID とライセンスの salt で h1 を計算し、下の GET エンドポイントを呼び出してください。サーバーはキーのハッシュだけを保存するため、ユーザーの h1 の一覧は返せません。デバイス上の特定ユーザーを確認するには ?h1= を渡してください。

エンドポイント スコープ 用途
GET /customer-api/users/{h1}/devicesreadそのユーザーが観測されたデバイス(初回・最終観測、イベント数)と、適用中のルール(block、ない場合は null)。
GET /customer-api/users/{h1}/eventsreadそのユーザーの最近のイベント(新しい順)。source は telemetry、physics、policy のいずれかです。from / to(Unix ms)で期間を絞り込みます。
GET /customer-api/devices/{deviceId}/usersreadデバイスに紐付いたユーザー:count と各ユーザーのキーの先頭 8 文字。サーバーは h1 を保存しないため、ユーザーのハッシュ一覧は返せません。?h1= を渡すと、そのユーザーがこのデバイスに紐付いているかを linked(true/false)で返します。
GET /customer-api/user-blocksreadブロックルールの一覧(新しい順)。?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-blocksblockルールの作成:{ "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-hashesconfigポリシー(mode:enforced または compatibility、activeCount)、承認済みハッシュ(status:active、expired、revoked)、サーバーが受信したハッシュ(observed:初回・最終観測、観測回数、宣言レベル、SDK バージョン、プラットフォーム、approval)。
POST /customer-api/config-hashesconfigハッシュの承認:{ "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 説明
400BAD_CONFIG_HASH, BAD_EXPIRY, BAD_JSONconfigHash が hex 64 文字ではない、または期限の値が不正です。
403SCOPE_REQUIREDキーに config スコープがありません。
404CONFIG_HASH_NOT_FOUNDこのライセンスの一覧にないハッシュです(DELETE)。
409ENFORCEMENT_ACK_REQUIREDacknowledgeEnforcement: true なしで最初の承認を要求しました。
503CONFIG_HASH_SCHEMA_NOT_READYサーバーはまだセキュリティ設定ハッシュを処理する準備ができていません。しばらくしてから再試行してください。

お客様 API のエラー

4xx・5xx のレスポンスも本文に { "code", "message" } を返します。本文を読み、code で分岐してください。

HTTP code 説明
401API_KEY_REQUIRED, API_KEY_INVALIDAuthorization ヘッダーがない、またはキーが不明か失効しています。
403SCOPE_REQUIREDキーにリクエストに必要なスコープ(read、block または config)がありません。
403PRO_REQUIREDライセンスが Pro ではありません。
403LICENSE_SUSPENDED, LICENSE_EXPIRED, LICENSE_REVOKED, LICENSE_PENDINGライセンスが停止、期限切れ、失効、または保留中です。
400BAD_*不正なリクエストです。例:BAD_USER_HASH(h1 が小文字 hex 64 文字ではない)、BAD_ACTION、BAD_EXPIRY、BAD_DEVICE_ID、BAD_JSON。
404NOT_FOUNDこのライセンスにそのルールはありません。
429RATE_LIMITEDリクエストが多すぎます(キーごとまたは IP ごと)。しばらくしてから再試行してください。
503EXTERNAL_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. 運用のヒント

OZeroAbortCode とイベントメッセージ

OZero がセキュリティ脅威を確定すると、OZeroSecurityEvent を作成し、SDK の基本対応フローとプロジェクトで登録したコールバックへ渡します。イベントには ModulationType、安定した公開 OZeroAbortCode、MessageKey、安全な英語 Message、WillAbort が含まれます。

Abort code とメッセージ表

コード OZeroAbortCode ModulationType MessageKey メッセージ
0x01MemoryModulationMemoryModulationmemory_modulationProtected memory value changed unexpectedly.
0x02InjectionInjectioninjectionUnexpected module, hook, or runtime injection signal detected.
0x0ABuildIntegrityBuildIntegritybuild_integrityBuild integrity validation failed.
0x0CSpeedOrTimeHackSpeedHackspeed_hackSuspicious time scale or execution speed change detected.
0x0CSpeedOrTimeHackTimeHacktime_hackSystem clock or trusted time anomaly detected.
0x0EDeviceOrInstallPolicyDeviceBindingModulationdevice_bindingDevice binding policy rejected the current device.
0x0EDeviceOrInstallPolicyInstallSourceinstall_sourceApplication install source is not trusted.
0x0FPhysicsHackPhysicsHackphysics_hackAbnormal physics behavior exceeded the configured policy.
0x10EnvironmentModulationEnvironmentModulationenvironment_modulationUnsupported or unsafe runtime environment detected.
0x13SteamAntiPiracySteamAntiPiracysteam_antipiracySteam ownership or ticket validation failed.
0x15InjectionScannerUnavailableInjectionScannerUnavailableinjection_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 Detector silenced -> Add to Whitelist workflow
流れ: 初回検出 -> 信頼モジュール項目を追加 -> 以降のスキャンで該当モジュールを許可処理できます。

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 ポータルからのリモート変更には対応していません。他のプラットフォームには影響しません。