OZero Security
문서

시작하기

이 가이드는 OZero Security 설치부터 첫 번째 보안 기능 적용까지 단계별로 안내합니다. 보안 전문 지식이 없어도 약 3분이면 기본 설정을 완료할 수 있습니다.

약 3분 · Unity 2021.3 LTS+ · iOS 12.0+ / Android API 21+

목적에 맞게 시작하세요

지금 하려는 작업을 선택하면 이 매뉴얼의 관련 절 또는 API 레퍼런스로 바로 이동합니다.

3분 빠른 시작 패키지를 임포트하고 대시보드를 열어 프리셋을 적용한 뒤 첫 빌드를 만듭니다. 출시 전 체크리스트 빌드 무결성, 플랫폼 설정, 진단 및 Release 구성을 점검합니다. 기능별 설정 가이드 보호 모듈, 공통 정책 및 선택형 Managed UI를 설정합니다. 문제 해결 임포트, IL2CPP, stripping, 플랫폼 및 라이선스 문제를 해결합니다. 라이선스·Pro 운영 활성화, Native Variant, 텔레메트리, 포털 정책 및 구독 생명주기를 확인합니다. API 레퍼런스 공개 타입, 필드, 기본값, enum, 콜백 및 오류 코드를 찾아봅니다.

개요

OZero Security는 일반 해킹 툴이 접근하기 어려운 Native C++ 레이어에서 보안 로직을 실행하여 Unity 게임을 보호합니다. Unity 에디터 내 대시보드에서 모듈을 활성화하는 것만으로 핵심 보호 기능이 동작합니다. 씬 설정이나 별도 코드 작성은 필요하지 않습니다.

기본 제공 보호 기능:
  • 빌드 무결성 검사 (앱 변조 탐지)
  • 스피드핵 및 타임핵 탐지
  • 메모리 인젝션 모니터링
  • 암호화된 인게임 변수 타입 (Secure Types)
  • 암호화된 세이브 파일 및 PlayerPrefs
코드 연결이 필요 없는 자동 부트스트랩 및 Config 암호화
  • 활성화된 탐지기는 SDK 시작 시 자동으로 준비됩니다. 씬에 별도 오브젝트를 배치하거나 반복 초기화 코드를 작성할 필요가 없습니다.
  • 보안 설정은 빌드 과정에서 보호되어 일반 플레이어 배포물에 평문 설정이 노출되지 않습니다.
  • Native C++ 런타임 가드가 관리형 Unity 코드 바깥에서 추가 검증 계층을 제공합니다.
  • 탐지 결과는 콜백, 로그, Pro 텔레메트리에서 확인할 수 있어 테스트와 운영 중 원인을 추적하기 쉽습니다.

1 패키지 임포트

Unity 에디터를 열고 OZero Security 패키지를 임포트합니다. Unity Package Manager를 통하거나 .unitypackage 파일을 더블클릭하여 임포트할 수 있습니다.

Import Unity Package 다이얼로그가 나타나면 모든 항목이 체크된 상태로 Import를 클릭하세요. 필요한 스크립트, 네이티브 플러그인, 에디터 도구가 자동으로 추가됩니다.

Import Unity Package dialog with all OZeroSecurity files selected
임포트 전에는 Unity 플레이 모드를 종료하고, 가능하면 에디터를 한 번 재시작한 뒤 진행하세요. 실행 중인 플레이어가 OZero DLL을 로드한 상태에서는 Windows가 DLL 파일을 잠가 임포트나 덮어쓰기가 실패할 수 있습니다. 임포트 후에는 Unity가 스크립트를 다시 컴파일하므로 우측 하단 진행 표시줄이 사라질 때까지 기다린 다음 다음 단계로 진행하세요.

2 대시보드 열기

임포트가 완료되면 Unity 메뉴바에서 Config Dashboard를 엽니다:

Window -> OZero Security -> Config & Dashboard
OZero Security Config Dashboard opened in the Unity Editor

Config Dashboard가 열리면 이 화면에서 프리셋, 보안 모듈, 라이선스, 빌드 무결성 설정을 한 번에 관리할 수 있습니다. Project 창에서 파일을 직접 찾아 수정할 필요는 없습니다.

Unity 에디터 메뉴 설명

OZeroSecurity는 설정 창은 Window > OZero Security, 진단 및 bake 명령은 Tools > OZero Security 아래에 제공합니다. 보안 프리셋은 Config & Dashboard에서 적용하도록 설계되어 있어, 저장 전에 변경될 설정을 확인할 수 있습니다.

메뉴 역할 사용 시점
Window > OZero Security > Config & Dashboard메인 OZeroSecurityConfig 에셋을 열거나 생성합니다.프리셋 적용, 모듈 활성화, 대응 정책 설정, 빌드 무결성 설정 확인을 가장 먼저 여기서 진행합니다.
Window > OZero Security > License SettingsOZeroLicenseConfig를 열거나 생성합니다.Standard, Plus, Pro 라이선스 설정, 서버 엔드포인트, 텔레메트리 옵션을 입력할 때 사용합니다.
Window > OZero Security > Check Setup프로젝트와 릴리스 설정에서 자주 발생하는 문제를 에디터에서 진단합니다.패키지 임포트 직후, 릴리스 빌드 전, 또는 모듈 동작이 예상과 다를 때 실행합니다.
Tools > OZero Security > Check Integrity ManifestOZero 무결성 매니페스트를 열고 디코딩된 내용을 확인합니다.빌드 무결성 문제를 조사하거나 고객지원 중 매니페스트 내용을 확인할 때 사용합니다.
Tools > OZero Security > Bake Security Config Blob보호된 보안 설정 blob을 StreamingAssets에 다시 생성합니다.고급 디버깅 또는 CI 흐름에서 사용합니다. 일반 플레이어 빌드에서는 자동으로 bake됩니다.
Tools > OZero Security > Bake Assembly Hash가장 최근 빌드 결과물 기준으로 oz_ahash.bin을 다시 생성합니다.전체 빌드 파이프라인을 다시 돌리지 않고 코드만 재빌드한 경우에만 사용합니다.
Tools > OZero Security > Keystore SHA ExtractorAndroid keystore에서 SHA-1, SHA-256 지문을 추출합니다.Build Integrity의 Android 서명 지문 값을 채울 때 사용합니다.
Tools > OZero Security > Add Debug SHA Key to ConfigAndroid debug keystore를 찾아 SHA-256 지문을 config에 추가합니다.로컬 Android 디버그 빌드에서만 사용합니다. 릴리스 빌드는 릴리스 서명 키 지문을 사용해야 합니다.
Tools > OZero Security > Steam Anti-Piracy > Scan Steam RedistributablePC 빌드 또는 플러그인 폴더에서 Steam redistributable 파일과 알려진 emulator artifact를 스캔합니다.Steam 릴리스 패키징 전이나 의심스러운 Steam 파일 포함 여부를 점검할 때 사용합니다.
Tools > OZero Security > Check Time.timeScale UsageOZero 시간 보호와 충돌할 수 있는 직접 Time.timeScale 수정 코드를 스캔합니다.릴리스 전 또는 Speed & Time Hack 모듈이 프로젝트 측 time-scale 정책 문제를 보고할 때 실행합니다.

3 모듈 활성화

대시보드 안에서 보안 모듈 목록과 토글 스위치를 확인할 수 있습니다. 사용하려는 모듈을 활성화하세요. 아래는 권장 시작 구성입니다.

OZero Security Config Dashboard in the Unity Editor

보안 프리셋 선택

먼저 프리셋을 선택한 뒤, 프로젝트에 맞춰 개별 모듈만 조정하세요. 일반적인 라이브 게임은 Standard부터 시작하는 것을 권장합니다. 보호 수준, 성능, 오탐 가능성의 균형이 가장 좋습니다.

프리셋 권장 사용처 적용 정책 요약
Low 프로토타입, 개발 빌드, 초기 QA 가벼운 핵심 검사만 유지합니다. 테스트 환경이 너무 일찍 차단되지 않도록 플랫폼 네이티브 검사와 강제 종료 정책을 완화합니다.
Standard 대부분의 출시 게임에 권장되는 기본값 핵심 보호 세트, 시작 시 검증, 런타임 재검증, 에뮬레이터 검사, 권장 IL2CPP 파일 커버리지를 켭니다. 호환성과 보호 수준의 균형을 맞춘 구성입니다.
Strict 고위험 라이브 서비스, PvP, 경쟁형 빌드 가장 넓은 커버리지를 적용하고 더 많은 실패를 치명적 위반으로 처리합니다. 플랫폼, 서명, 스토어 배포 흐름을 충분히 테스트한 뒤 적용하세요.
모듈 기능 설명 권장 여부
Build Integrity Validator 앱 바이너리 변조 여부 탐지 권장
Speed Hack Detector 시간 조작 치트 탐지 권장
Injection Detector 메모리 후킹 툴 모니터링 권장
Install Source Validator 불법 APK 차단 (Android 전용) 선택

대시보드에서 사용할 모듈을 켜고 OZeroSecurityConfig 에셋을 저장하면 됩니다. 플레이어가 시작될 때 SDK가 활성화된 모듈을 자동으로 준비합니다.

씬에 별도 오브젝트를 배치하지 않아도 됩니다. 원하는 모듈을 켜고 설정을 저장하면 다음 실행부터 해당 모듈이 자동으로 초기화됩니다.

라이선스 모델 — Standard / Plus / Pro

OZero Security는 Standard, Plus, Pro 세 가지 티어로 제공됩니다. Standard는 서버 연동 없이 로컬 보호 기능을 사용합니다. Plus는 프로젝트별 Native Variant 패키지와 매니페스트 / Bundle ID 바인딩을 추가합니다. Pro는 Plus 구성을 포함하고 텔레메트리, 서명된 서버 시간, 원격 정책, 서버 검증, 기기 한도 같은 운영 기능을 제공합니다.

아래 매트릭스는 개발자 관점에서 SDK가 런타임에 티어별로 실제 어떻게 동작하는지를 정리한 것입니다. 전체 기능 비교는 홈페이지의 라이선스 모드 비교표에서 확인하세요.
항목 Standard Plus Pro
라이선스 키 — (없음) OZ-PLS-XXXX ×6 OZ-PRO-XXXX ×6
부팅 시 네트워크 필요 없음 — 오프라인 실행 가능 런타임 서버 불필요 — 포털에서 Variant 다운로드만 진행 디바이스당 POST /v1/activate 1회 후 캐시
10개 보호 모듈 10개 보호 모듈 전체 활성화 10개 전체 (로컬 보호 모듈은 Standard와 동일) 10개 전체 (Standard와 동일)
네이티브 Variant 공용 네이티브 모듈 앱별 Variant + 매니페스트 바인딩 포함
클라우드 텔레메트리 전송하지 않음 꺼짐 (서버리스) 켜짐 — 위협 이벤트를 /v1/telemetry로 전송
서명된 시간 (시계 조작 방지) 꺼짐 — WebTime은 HTTPS HEAD만 사용 꺼짐 — Standard와 동일 켜짐 — 서명된 /v1/time 응답 사용
디바이스당 한도 무제한 (키 없음, 강제 없음) 프로젝트 귀속 라이선스, 런타임 기기 한도 없음 기본 5대 / 조정 가능
소스 코드 접근 관리형 C#만 관리형 C#만 관리형 C#만
오늘 Standard로 출시한 뒤 나중에 Plus 또는 Pro로 업그레이드하더라도 게임플레이 코드는 그대로 유지할 수 있습니다. Plus는 Variant 매니페스트와 네이티브 패키지를 추가하고, Pro는 여기에 OZeroLicenseConfig 기반 서버 기능을 더합니다.

Plus / Pro 라이선스 키 등록

Standard는 별도 라이선스 설정 없이 바로 사용할 수 있습니다. Plus 또는 Pro를 사용하는 경우에는 Unity에서 OZeroLicenseConfig 에셋을 만들고 발급받은 라이선스 키를 입력하세요. Plus 키는 포털에서 프로젝트 전용 Native Variant 패키지를 내려받고, 빌드할 때 그 패키지가 현재 프로젝트와 맞는지 확인하는 데 사용됩니다. Pro 키는 여기에 더해 앱 실행 중 서버 활성화와 Pro 전용 서버 기능에도 사용됩니다.

1. 설정 에셋 생성

Unity 상단 메뉴에서 Window → OZero Security → Config & Dashboard를 엽니다. License & Server 섹션에서 Create OZeroLicenseConfig 버튼을 클릭하세요. 이 버튼을 사용하면 OZeroLicenseConfig 에셋이 올바른 Resources/ 폴더에 자동으로 생성됩니다. 직접 폴더를 만들거나 에셋을 옮길 필요가 없습니다.

Project 창의 Create Asset 메뉴로 직접 만들 수 없습니다. OZeroSecurityConfig와 OZeroLicenseConfig는 잘못된 위치에 생성되는 일을 막기 위해 CreateAssetMenu를 제공하지 않으며, Dashboard 버튼이 지원되는 생성 방법입니다.

2. Inspector 필드 채우기

필드 필수 여부 설명
tier 모든 티어 이 빌드에 적용할 라이선스 단계를 선택합니다. Standard는 공통 네이티브 모듈만 사용합니다. Plus는 프로젝트 전용 Native Variant와 manifest 검증을 추가합니다. Pro는 Plus 구성에 서버 활성화, 텔레메트리, 원격 정책 같은 운영 기능을 더합니다.
licenseKey Plus / Pro 프로젝트에 발급된 라이선스 키입니다. Plus 키는 OZ-PLS-..., Pro 키는 OZ-PRO-... 형식입니다. Plus에서는 이 키로 Native Variant 패키지가 현재 프로젝트용인지 확인합니다. Pro에서는 같은 키를 서버 활성화와 Pro 서버 기능에도 사용합니다.
appIdentifier 자동 전송 Pro 활성화 요청을 보낼 때 SDK가 Unity의 Application.identifier 값을 함께 전송합니다. 고객 포털에 등록된 Bundle ID 또는 Package Name과 이 값이 다르면 활성화가 거부될 수 있으므로, Unity Player Settings의 Identifier를 먼저 맞춰 주세요.
serverBaseUrl Pro 런타임 Pro 기능이 OZero 서버와 통신할 때 사용하는 기본 주소입니다. 활성화, 텔레메트리, signed time, attestation, 서버 정책 확인에 사용됩니다. Standard와 Plus는 런타임에 이 주소를 호출하지 않습니다. OZero 지원팀이 별도 주소를 안내하지 않았다면 기본값 https://api.ozerosecurity.com을 그대로 두세요.
serverPublicKeyHex Pro 런타임 고객 포털 > Server Key 메뉴에 표시되는 서명용 공개키입니다. Pro 런타임은 이 값으로 activation, signed time, attestation, offline policy 응답이 OZero 서버에서 온 것인지 확인합니다. Plus는 이 필드를 사용하지 않습니다. Plus/Pro Variant manifest는 SDK에 내장된 OZero Variant signing key로 따로 검증됩니다.
previousServerPublicKeyHex Pro 선택 Pro 서버 서명 키를 교체하는 짧은 기간에만 사용하는 이전 공개키입니다. OZero 지원팀이 키 교체를 안내한 경우에만 채우고, 평소에는 비워 두세요. Plus는 이 값을 사용하지 않습니다.
tokenTtlSeconds Pro 런타임 Pro 활성화가 한 번 성공한 뒤, 오프라인 상태에서 그 결과를 얼마 동안 사용할지 정하는 시간입니다. 기본값 604800은 7일입니다. 이 시간이 지나면 로컬 보호 기능은 계속 동작하지만, 텔레메트리나 signed time 같은 Pro 서버 기능은 다음 활성화가 성공할 때까지 꺼집니다.
offlineProPolicyMode Pro 오프라인 상태에서도 포털에서 차단한 빌드나 버전을 계속 막을지 정하는 Pro 옵션입니다. 대부분의 라이브 게임은 권장값인 ApplyCachedBlockPolicies를 사용하면 됩니다. 항상 최신 정책이 없으면 실행을 막아야 하는 온라인 전용 게임만 RequireFreshPolicy를 검토하세요. IgnoreCachedBlockPolicies는 호환성 확인이나 특수 테스트용이며 라이브 빌드에는 권장하지 않습니다. 값별 의미는 OZeroOfflineProPolicyMode에서 확인할 수 있습니다.
activationTimeoutSeconds Pro 런타임 Pro 활성화 요청인 /v1/activate 응답을 최대 몇 초까지 기다릴지 정합니다. 기본값은 6.0입니다. 이 시간을 넘기면 씬 로드를 막지 않고, 사용할 수 있는 Pro 캐시가 있으면 캐시를 사용합니다. 캐시가 없어도 선택한 서버 기능만 사용할 수 없으며 로컬 보호는 계속됩니다.
enableLog 선택 켜 두면 라이선스 캐시 사용, 활성화 성공, 타임아웃, 서명 불일치 같은 흐름을 OZeroSecLog에 남깁니다. 연동 중에는 켜 두면 문제를 찾기 쉽고, 출시 빌드에서 로그를 줄이고 싶다면 끄면 됩니다.
Pro 서버 기능
enableDevicePolicyHeartbeat Pro 현재 기기가 고객 포털에서 차단된 상태인지 Pro 서버에 주기적으로 확인합니다. 서버가 DEVICE_BLOCKED를 반환하면 SDK는 저장된 Pro 권한 정보를 지우고 앱 실행을 차단합니다.
devicePolicyHeartbeatInterval Pro 기기 차단 여부를 확인하는 기본 주기입니다. 기본값은 300초입니다. 0으로 두면 주기 확인을 하지 않습니다.
devicePolicyHeartbeatJitterPercent Pro 여러 기기가 같은 순간에 서버를 호출하지 않도록, 확인 주기에 약간의 차이를 주는 비율입니다. 기본값 20은 설정된 주기를 기준으로 호출 시간을 조금씩 다르게 잡는다는 뜻입니다. 입력 범위는 0~75입니다.
enableSecurityLevelCheck Pro 켜 두면 앱 시작 시 /v1/security-level로 이 빌드의 보안 설정을 서버에 확인합니다. 포털에서 요구하는 최소 보안 수준보다 낮으면 서버가 거부할 수 있습니다. 기본값은 false입니다.
declaredSecurityLevel Pro 이 빌드가 서버에 알려 주는 보안 수준입니다. 기본값은 Standard입니다. Low는 프로토타입이나 내부 테스트에 사용하고, Strict는 QA에서 차단 정책까지 충분히 확인한 뒤 사용하세요.
failOnSecurityLevelReject Pro 켜 두면 서버가 보안 수준이나 설정 해시를 명확히 거부했을 때 앱 차단 흐름을 실행합니다. 단순 네트워크 오류, 서버 점검, 라이선스 점검 상태는 변조로 보지 않으며 Pro 서버 기능만 비활성화합니다.
securityLevelCheckInterval Pro 앱 실행 중에도 보안 수준을 다시 확인할지 정하는 주기입니다. 기본값 0은 앱 시작 시 한 번만 확인한다는 뜻입니다.
securityLevelCheckJitterPercent Pro 보안 수준을 다시 확인하는 시간이 여러 기기에서 한꺼번에 겹치지 않도록, 확인 주기에 약간의 차이를 주는 비율입니다. 기본값 20은 설정된 주기를 기준으로 확인 시간을 조금씩 다르게 잡는다는 뜻입니다. 입력 범위는 0~75입니다.

3. 빌드 & 검증

별도 코드를 추가할 필요는 없습니다. 앱이 시작되면 SDK가 OZeroLicenseConfig 에셋을 자동으로 읽습니다. 첫 실행 후 Player 로그에서 [OZeroLicense] activated; tier=pro caps=5 같은 줄을 확인하세요. Pro로 설정했는데 Standard 모드로 실행된다는 로그가 보이면 에셋 위치와 이름을 먼저 확인하세요. 에셋은 Resources/ 폴더 아래에 있어야 하고, 이름은 정확히 OZeroLicenseConfig여야 합니다.

라이선스 키는 비밀번호처럼 런타임에서 숨길 수 있는 값은 아니며, 빌드에 함께 포함됩니다. 다만 공개 저장소, 문서, 스크린샷에 노출되면 다른 사람이 디바이스 한도를 소진할 수 있으므로 제품 코드처럼 조심해서 관리하세요.

서버 활성화 흐름

Pro 라이선스는 앱이 시작될 때 SDK가 자동으로 확인합니다. 이 과정은 게임 시작 흐름을 강제로 막지 않도록 설계되어 있습니다. 서버 응답이 늦거나 네트워크가 잠시 끊긴 경우에는 먼저 저장된 활성화 정보를 확인하고, 사용할 수 있는 정보가 있으면 그 상태로 계속 실행합니다.

부팅 시퀀스

  1. SDK는 앱 시작 시 라이선스 런타임을 자동으로 초기화합니다.
  2. Standard와 Plus는 런타임 서버 활성화가 필요 없으므로 바로 로컬 보호 기능을 실행합니다.
  3. 선택형 Pro 서버 기능을 구성한 경우 백그라운드에서 /v1/activate 요청을 보내 현재 라이선스와 기기 상태를 확인합니다.
  4. 활성화에 성공하면 텔레메트리, 서명된 서버 시간(signed time), 빌드 검증(attestation) 같은 Pro 서버 기능을 사용할 수 있습니다.
  5. 서버 점검, 일시적인 네트워크 오류, 타임아웃이 발생하면 SDK는 가능한 경우 저장된 활성화 정보를 사용합니다. 저장된 정보도 사용할 수 없거나 라이선스가 만료/정지/폐기된 경우에는 선택한 Pro 서버 기능만 사용 불가 또는 제한 상태가 됩니다. 이미 취득한 Native Variant와 로컬 보호는 그대로 유지됩니다.

전송되는 정보

활성화 요청은 작은 JSON POST입니다. 기본 요청에는 licenseKey, deviceId, sdkVersion, platform이 포함됩니다. Unity에서 값을 제공할 수 있는 경우 appIdentifier, companyName, productName, webglOrigin도 함께 전송됩니다. deviceId는 기본적으로 Unity 기기 식별자를 사용하지만, 필요하면 OZeroLicenseRuntime.DeviceIdProvider로 게임 계정 ID나 자체 UUID 방식으로 바꿀 수 있습니다.

POST https://api.ozerosecurity.com/v1/activate
Content-Type: application/json

{
  "licenseKey":    "OZ-PRO-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX",
  "deviceId":      "<DeviceIdProvider result; default SystemInfo.deviceUniqueIdentifier>",
  "sdkVersion":    "<OZeroSdkVersion.ManagedVersion>",
  "platform":      "<android | ios | windows | macos | linux | webgl | ...>",
  "appIdentifier": "<Application.identifier>",
  "companyName":   "<Application.companyName>",
  "productName":   "<Application.productName>",
  "webglOrigin":   "<WebGL origin only>"
}

서버는 서명된 토큰, 결정된 티어, 사용할 수 있는 기능 목록, 서버 기능 사용 여부, 토큰 만료 시각, 서버 연결 실패 시 정책을 반환합니다. SDK는 설정된 공개키로 서명된 토큰을 검증한 뒤에만 Pro 기능을 신뢰합니다.

서버 기능을 사용할 수 없을 때

서버 점검, 네트워크 타임아웃, 라이선스 만료/정지/폐기, Bundle ID 불일치는 곧바로 해킹으로 판단하지 않습니다. 이 경우 선택한 Pro 서버 기능만 사용 불가 또는 제한 상태가 되며, 이미 취득한 Native Variant와 로컬 보호는 그대로 유지됩니다. 반대로 빌드 불일치, 차단된 빌드, 인젝션, 디버거처럼 변조가 명확한 상황은 설정된 대응 정책을 따릅니다.

라이브 활성화가 실제로 성공했는지 확인하고 싶다면 (캐시만 신뢰하지 않고), 자체 부트스트랩에서 await OZeroLicenseRuntime.Initialize();를 호출하고 OZeroLicenseRuntime.Entitlement?.cachedAtMillis를 살펴보세요. 최근 몇 초 이내 값이면 서버가 방금 응답한 것이고, 더 오래된 값이면 캐시 기반으로 동작 중입니다.

오프라인 동작

먼저 결론부터 보면, 네트워크가 끊겨도 게임과 로컬 보호는 멈추지 않습니다. Standard는 원래 서버를 쓰지 않고, Pro도 같은 오프라인 로컬 보호를 유지합니다. 명시적으로 선택한 서버 기능만 일정 기간 저장된 활성화 정보를 사용할 수 있습니다.

Standard — 인터넷 없이 바로 실행

Standard는 서버에 묻는 과정이 없습니다. 앱 안에 포함된 로컬 보호 모듈만 실행하므로, 플레이어가 오프라인이어도 게임은 시작됩니다. 텔레메트리, signed time처럼 Pro 서버가 필요한 기능만 사용되지 않습니다.

Pro — 로컬 보호는 오프라인 유지, 선택한 서버 기능은 캐시 사용

선택형 Pro 서버 기능을 켠 경우 온라인에서 /v1/activate를 통해 기기와 라이선스를 확인합니다. SDK는 결과를 암호화해 PlayerPrefs에 저장하여 해당 서버 기능이 일시적인 연결 끊김을 견디도록 합니다. 이 활성화는 이미 취득한 Native Variant의 빌드나 로컬 보호를 제한하지 않습니다.

tokenTtlSeconds는 저장된 서버 기능 결과의 유효 기간이며 기본값은 604800초, 즉 7일입니다. 오프라인에서 만료되면 텔레메트리 전송과 signed-time 검증 같은 서버 의존 기능만 다음 활성화 성공까지 사용할 수 없습니다. 게임, 취득한 Native Variant 및 로컬 디텍터는 그대로 유지되며, SDK가 제품 등급을 Standard로 조용히 전환하지 않습니다.

Pro — 포털에서 차단한 빌드는 오프라인에서도 막기

서버가 잠시 안 된다고 해서 기본 보호로 계속 실행하는 것과, 운영자가 포털에서 차단한 빌드를 허용하는 것은 다른 문제입니다. Pro 활성화가 성공할 때 서버는 현재 차단 목록(빌드 해시, SDK 버전, 앱 버전)을 서명해서 함께 내려줍니다. SDK는 이 차단 목록을 활성화 정보와 별도로 저장합니다.

권장값인 ApplyCachedBlockPolicies를 쓰면, 저장된 차단 목록이 아직 유효한 동안에는 플레이어가 비행기 모드로 실행해도 차단된 빌드는 Build Integrity에서 거부됩니다. 포털에서 차단 규칙을 새로 바꾼 경우에는 각 기기가 한 번 온라인으로 활성화되어야 새 규칙을 받아옵니다.

단, 어떤 기기가 Pro 활성화를 한 번도 성공한 적이 없다면 완전 오프라인 첫 실행에서는 포털 차단 목록을 받을 방법이 없습니다. 그래서 결제, 보상, 재화 지급처럼 손실이 큰 동작은 게임 서버의 세션 검사나 OZA 검증을 마지막 확인 단계로 두는 것을 권장합니다.
상태 디텍터 텔레메트리 Signed Time
온라인, 방금 활성화 10개 모두 On On On
오프라인, 저장된 Pro 정보 유효 10개 모두 On Off (오프라인 이벤트 저장 안 함) 가능하면 WebTime 대체 사용
오프라인, 저장된 Pro 정보 만료 10개 모두 On Off (다음 온라인 활성화까지) Off (다음 온라인 활성화까지)
첫 실행 + 완전 오프라인 10개 모두 On 첫 온라인 실행까지 Off 첫 온라인 실행까지 Off
텔레메트리는 기기가 온라인일 때만 전송됩니다. 오프라인 중 발생한 이벤트는 파일로 저장하지 않고, 경고 로그 한 줄만 남긴 뒤 버립니다. 로컬 큐를 남기면 공격자가 그 큐를 수정할 수 있기 때문입니다. 다시 온라인이 된 뒤 새로 발생한 탐지 이벤트는 정상적으로 전송됩니다.

트러블슈팅

문제가 생기면 먼저 Unity Player 로그에서 [OZeroLicense]를 검색하세요. 이 로그는 SDK가 Standard로 시작했는지, Pro 활성화가 성공했는지, 실패했다면 왜 Standard로 내려갔는지를 알려줍니다. 보안 이벤트 전송 문제는 [OZeroTelemetry]도 함께 검색하세요.

로그에 보이는 내용 해결 방법
[OZeroLicense] Standard / serverless mode. SDK가 Standard 모드로 시작했습니다. Standard를 쓰려는 설정이면 정상입니다. Pro를 기대했다면 Unity 메뉴 Window → OZero Security → Config & Dashboard에서 만든 OZeroLicenseConfig.assetResources/ 아래에 있는지 확인하세요. 이름은 정확히 OZeroLicenseConfig여야 하고, tier=ProlicenseKey도 채워져 있어야 합니다.
Pro->Standard downgrade: /v1/activate returned LICENSE_NOT_FOUND 서버가 라이선스 키를 찾지 못했거나, 현재 사용할 수 없는 상태입니다. licenseKey를 고객 포털에 표시된 라이선스 키와 다시 비교하세요. 대소문자와 대시를 그대로 유지해야 합니다. 포털에서 키 상태가 pending, suspended, revoked, expired라면 Pro 활성화가 되지 않습니다.
ANDROID_BUNDLE_ID_MISMATCH / WINDOWS_PRODUCT_NAME_MISMATCH 현재 빌드의 앱 정보가 포털에 등록된 값과 다릅니다. Android/iOS는 Player Settings의 Application.identifier, Windows는 Company/Product 이름, WebGL은 실행 origin을 확인하세요. 포털에 등록된 값과 다르면 /v1/activate가 거부됩니다. 값을 바꾼 뒤에는 새 빌드로 다시 테스트하세요.
activation token signature verification failed 서버 응답은 왔지만, SDK가 이 응답을 신뢰할 수 없다고 판단했습니다. 이 항목은 Pro 활성화에서만 확인합니다. 고객 포털 > Server Key의 Active publicKeyOZeroLicenseConfig.serverPublicKeyHex에 넣으세요. 포털에 kid가 함께 보이더라도 Unity에는 64자리 hex publicKey만 넣어야 합니다. 값이 정확한데도 계속 실패하면 회사 프록시/MITM 장비를 거치지 않는 직접 네트워크에서 한 번 더 테스트하세요.
private native variant manifest signature is invalid Variant 패키지의 manifest가 없거나 수정되었거나, 신뢰된 OZero Variant signing key로 서명되지 않았습니다. 이 문제는 serverPublicKeyHex를 채워서 해결하는 문제가 아닙니다. Plus/Pro Variant 패키지는 배정된 패키지를 다시 다운로드하고, manifest와 native plugin 파일을 같은 패키지에서 가져온 상태로 유지하세요. 새로 받은 패키지도 실패하면 manifest 파일과 Unity 빌드 로그를 OZero 지원팀에 전달하세요.
DEVICE_BLOCKED 이 기기는 고객 포털에서 차단된 상태입니다. 포털에서 해당 deviceId의 보안 이벤트를 확인하세요. 실제 공격이나 정책 위반이면 그대로 두고, QA 기기나 오탐이면 포털에서 차단을 해제하세요. SDK는 이 응답을 받으면 저장된 Pro 권한을 지우고 차단 흐름을 실행합니다.
ACTIVATION_LIMIT 이 라이선스로 새로 활성화할 수 있는 기기 수를 넘었습니다. 포털에서 더 이상 쓰지 않는 테스트 기기를 정리하세요. 실제 사용자 기기가 많아진 상황이라면 라이선스의 기기 한도를 늘려야 합니다.
/v1/activate timed out / network error 서버에 연결하지 못했거나, 응답이 너무 늦었습니다. 기기가 https://api.ozerosecurity.com/health에 접속할 수 있는지 확인하세요. 회사 방화벽, 프록시, DNS 문제도 함께 확인합니다. 지연이 큰 지역에서 운영한다면 activationTimeoutSeconds를 조금 늘려 테스트하세요. 이미 저장된 Pro 정보가 있으면 SDK는 그 정보를 먼저 사용합니다.
cached entitlement past TTL; clearing. 저장된 Pro 정보의 유효 기간이 지났습니다. 기기가 tokenTtlSeconds보다 오래 오프라인이면 저장된 Pro 정보를 더 이상 신뢰하지 않습니다. 게임은 Standard 수준으로 계속 실행되고, 다음 온라인 활성화가 성공하면 Pro 기능이 다시 켜집니다. 장기간 오프라인 플레이가 흔한 게임이라면 TTL을 늘릴 수 있습니다.
SERVER_NOT_CONFIGURED / SIGN_FAILED 서버가 활성화 토큰에 서명하지 못했습니다. 대부분 클라이언트 설정 문제가 아니라 서버의 Server Key 또는 암호화 키 상태 문제입니다. 고객 포털의 Server Key 상태를 확인하세요. 운영 서버에서 발생했다면 로그 시간, 일부 licenseKey, 발생 플랫폼을 OZero 지원팀에 전달하세요.
출시 빌드에서는 enableLog를 꺼 두는 것을 권장합니다. QA에는 유용하지만, 일반 사용자에게 배포되는 로그에 티어, 기능 상태, 탐지 흐름이 불필요하게 남을 수 있습니다.

프로젝트 설정

개별 보안 모듈을 조정하기 전에, 네이티브 플러그인과 스토어 빌드, 플랫폼 검증에 영향을 주는 Unity Player Settings를 먼저 확인하세요.

최소 빌드 타겟

플랫폼 최소 타겟 설명
iOS 12.0+ iOS 빌드에서는 Project Settings > Player > iOS > Target minimum iOS Version12.0 이상으로 설정하세요. OZero는 이 PlayerSettings 값을 강제로 덮어쓰지 않으므로, 앱의 지원 정책에 맞춰 유지하면 됩니다.
Android API 21+ Android 빌드는 Project Settings > Player > Android > Minimum API LevelAndroid 5.0 Lollipop (API level 21) 이상으로 설정하세요. 스토어 릴리스 빌드는 IL2CPP와 ARM64 사용을 권장합니다.
Apple 개인정보 manifest: SDK에는 PrivacyInfo.xcprivacy가 포함됩니다. Unity post-process가 iOS Xcode 앱 target과 macOS의 Contents/Resources에 이 파일을 추가합니다. OZero가 앱 내부 SDK 상태에 Unity PlayerPrefs를 사용하므로 UserDefaults CA92.1 사유를 선언합니다. 최종 앱에서 manifest를 유지하고 제출 전 Xcode privacy report를 확인하세요.

릴리스 빌드 시 보안 및 용량 최적화 설정 안내

앱을 최종 출시(릴리스)할 때는 보안을 강화하고 빌드 용량을 줄이기 위해 다음 설정을 권장합니다.

보안 강화 (IL2CPP 설정): Android, iOS 등 Unity가 지원하는 환경에서는 빌드 방식을 IL2CPP로 선택하세요. IL2CPP는 역분석을 완전히 막지는 않지만, C# 코드와 메타데이터가 그대로 노출되는 범위를 줄이고 OZero 보안 검사가 더 단단한 릴리스 환경에서 동작하도록 도와줍니다. 경로: Project Settings > Player > Other Settings > Scripting Backend

용량 최적화 (Managed Stripping 설정): 사용하지 않는 managed code를 제거해 앱 용량을 줄이는 기능입니다. 처음부터 너무 강하게 제거하면 앱이 정상 실행되지 않을 수 있으므로 Low 또는 Medium 단계부터 시작하세요.

주의사항: 설정을 바꾼 뒤에는 화면 전환, 데이터 저장, Addressables, 결제/광고 같은 외부 SDK가 모두 잘 작동하는지 실제 기기에서 테스트한 다음 최적화 단계를 높이세요.

Windows Standalone 빌드를 실행할 PC에는 Microsoft Visual C++ Redistributable 2015-2022 (x64)가 설치되어 있어야 합니다. 이 런타임이 없으면 Windows가 OZero 네이티브 플러그인을 불러오지 못해 앱이 시작 직후 종료될 수 있습니다. Steam, 런처, 자체 설치 파일로 배포할 때는 VC++ Redistributable을 필수 구성 요소로 함께 설치하도록 구성해 주세요.

Android ProGuard / R8 설정

OZero Security는 별도의 Java SDK 패키지를 요구하지 않습니다. 다만 Android 릴리스 빌드에서 Minify, ProGuard, R8을 활성화했다면 Unity Java 브리지와 프로젝트에서 사용하는 커스텀 Android 브리지 클래스를 보존해야 합니다. 그래야 패키지 정보, 설치 출처, APK 서명 인증서 검사, 부트 에셋 로딩에 필요한 JNI 호출이 난독화 이후에도 안정적으로 동작합니다.

Unity에서 Project Settings > Player > Android > Publishing Settings를 엽니다. Minify Release를 켰다면 Custom ProGuard File도 활성화한 뒤 아래 규칙을 proguard-user.txt에 추가하세요. Minify를 사용하지 않는 경우 별도 ProGuard 설정은 필요하지 않습니다.
# OZero Security - Unity Android ProGuard/R8 keep rules
-keep class com.unity3d.player.UnityPlayer { *; }
-keep class com.unity3d.player.UnityPlayerActivity { *; }
-keep class com.unity3d.player.UnityPlayerGameActivity { *; }
-keep class com.unity3d.player.UnityPlayerForActivityOrService { *; }
-keepattributes *Annotation*,InnerClasses,EnclosingMethod,Signature

# If your game adds custom Java/Kotlin bridge classes that OZero or your code
# calls through AndroidJavaClass / AndroidJavaObject, keep those classes too.
# Replace the package below with your own bridge package.
# -keep class com.yourcompany.yourgame.bridge.** { *; }

5 OZeroSecurityConfig 설정

OZeroSecurityConfig ScriptableObject 에셋은 패키지 임포트 시 포함됩니다. Project 창에서 선택하여 모든 보안 모듈 설정을 확인하고 조정하세요. 런타임에서는 OZeroSecurityConfig.Instance로 접근합니다.

OZeroSecurityConfig Inspector panel in the Unity Editor

공통 설정

모든 보안 모듈에 공통으로 적용되는 최상위 설정입니다. 이 표의 기본값은 코드에 정의된 직렬화 기본값을 기준으로 합니다.

필드 Type 기본값 설명
developerSecret string "" OZeroSV_FileOZeroSafePlayerPrefs 데이터를 보호할 때 사용하는 프로젝트별 secret입니다. 직접 임의 문자열을 입력하기보다 Config Dashboard의 Generate Secure Secret 버튼으로 생성하세요. 이 값은 게임마다 다르게 사용하고 문서, 로그, 공개 저장소에 노출하지 마세요. 출시 후 이 값을 변경하면 기존 버전에서 플레이어 기기에 저장한 보호 데이터를 새 버전에서 복호화할 수 없습니다.
enableLog bool false Enable Debug Logs를 켜면 SDK 초기화, 설정 로딩, 라이선스/텔레메트리 흐름, 탐지 이벤트 같은 내부 상태를 Unity Console과 Player 로그에 출력합니다. 개발/QA 단계에서 원인 파악에 유용하지만 탐지 흐름과 모듈 상태가 노출될 수 있으므로 운영 빌드에서는 끄는 것을 권장합니다.
enableFailureDiagnostics bool false Enable Failure Diagnostics를 켜면 보안 위반이 발생했을 때 로컬 진단 파일을 Application.persistentDataPath에 저장합니다. 파일에는 모듈명, 해시, 기기 상태, 설치 패키지명, 런타임 설정 일부가 포함될 수 있으므로 QA나 고객 지원 세션에서만 켜는 것을 권장합니다.

플랫폼별 기본 저장 위치:
  • Windows: %USERPROFILE%\AppData\LocalLow\CompanyName\ProductName
  • macOS: ~/Library/Application Support/CompanyName/ProductName
  • Linux: ~/.config/unity3d/CompanyName/ProductName
  • Android: /storage/emulated/0/Android/data/package.name/files
  • iOS: 앱 샌드박스의 Documents 폴더
  • WebGL: 브라우저 IndexedDB 기반 /idbfs 저장소
중요: 첫 출시 전에 Generate Secure Secret으로 developerSecret을 생성하고 저장하세요. 출시 후에는 변경하지 마세요. 이 값을 변경하면 기존 OZeroSV_File, OZeroSafePlayerPrefs 데이터를 새 버전에서 복호화할 수 없습니다.

검증용 UI 관리

Pro Only 기능입니다. Build Integrity와 Steam DRM 등에서 온라인 재검증, 네트워크 재시도, 차단/종료 안내 팝업을 관리하도록 설정합니다. optional OZero TMP dialog package를 가져오기 전에 Package Manager에서 Unity UI와 TextMeshPro를 설치하세요. Unity 6에서는 TextMeshPro가 Unity UI 2.x에 포함됩니다. TextMeshPro를 쓰지 않는 프로젝트는 Custom UI / Callback Only를 사용하세요.

Build preflight: BuiltInBlockingDialog 또는 BuiltInNonBlockingDialog를 선택하면 모든 player build에 TextMeshPro, TMP Essential Resources, import된 OZeroSecurity_BuiltInDialog_TMP.unitypackage가 필요합니다. 하나라도 누락되면 preflight가 빌드를 중단합니다. Window > OZero Security > Check Setup을 열고 Built-In TMP dialog 오류의 Import Package를 누른 뒤 Unity script compilation이 끝날 때까지 기다리고 다시 빌드하세요. 게임이 자체 UI를 제공한다면 Custom UI / Callback Only를 선택하세요.
가이드: 다국어를 확장하려면 Tools > OZero Security > Localization > Managed Verification Text 메뉴를 사용하세요. Language Code (ISO 639-1)와 각 항목별 번역 데이터를 입력한 뒤 Generate / Save를 누르면 JSON 리소스가 자동 생성됩니다. 이후 해당 언어를 사용하는 고객의 Application.systemLanguage에 따라 표시됩니다.
폰트 범위: optional package에는 OZero가 제공하는 EN·KO·JA·ZH-CN·ZH-TW 대화상자 문구에 필요한 글자만 Static TMP SDF subset으로 포함됩니다. 기본 문구는 별도 폰트 작업 없이 사용할 수 있습니다. 문구를 바꾸거나 언어를 추가하거나 subset 밖의 문자를 표시하려면 charset 파일로 Static SDF를 다시 생성하거나 프로젝트 소유 custom prefab에서 폰트를 직접 참조하세요. 갱신하지 않으면 missing glyph가 표시될 수 있습니다. OZero 원본 대신 프로젝트 소유 폴더의 prefab과 폰트를 수정하세요.
필드 Type 기본값 설명
managedVerificationUiPolicy (Pro)enumBuiltInBlockingDialogPro Only 기능입니다. OZero Managed Build Integrity와 Steam DRM 상태를 어떤 사용자 UI 흐름으로 보여 줄지 선택합니다. 기본 차단형/안내형 다이얼로그는 TextMeshPro와 optional OZeroSecurity_BuiltInDialog_TMP.unitypackage import가 필요합니다. 프로젝트가 이 패키지를 import하지 않거나 자체 UI로 대체하려면 Custom UI / Callback Only를 사용하세요.
managedVerificationDialogPrefabResourcePath (Pro)string""복사해서 커스터마이징한 OZero Managed Verification UI 프리팹의 Resources 경로입니다. 비워 두면 optional TMP dialog package의 기본 프리팹을 사용합니다. 먼저 OZeroSecurity_BuiltInDialog_TMP.unitypackage를 import한 뒤, 디자인을 수정하려면 프로젝트 소유 복사본을 만들어 사용하세요.
managedVerificationRetryTimeoutSeconds (Pro)int15사용자가 Retry를 누른 뒤 retry-timeout 상태로 전환되기 전까지 기다릴 최대 시간입니다. 플레이어가 무기한 검증 대기 상태에 남지 않도록 설정합니다.
managedVerificationOnlineRequiredTimeoutSeconds (Pro)int120온라인 필요 다이얼로그가 표시된 뒤 설정된 timeout action을 적용하기 전까지 기다릴 최대 시간입니다.
managedVerificationTimeoutAction (Pro)enumBlockSession검증이 제한 시간 안에 복구되지 않을 때의 동작을 정합니다. 다이얼로그 유지, 보호 세션 차단, 앱 종료, 또는 커스텀 흐름을 위한 콜백 호출만 선택할 수 있습니다.
autoRetryManagedVerificationWhenNetworkRestored (Pro)boolfalse네트워크 연결이 복구되면 Managed Verification을 자동으로 다시 시도합니다. 명시적인 플레이어 조작 없이 재시도해도 안전한 게임 흐름에서만 켜세요.
managedVerificationLanguageCode (Pro)stringauto기본 검증 UI의 언어 코드입니다. autoApplication.systemLanguage를 따르며, ko, en, ja, zh-CN, zh-TW 또는 커스텀 JSON 파일 코드를 직접 지정할 수 있습니다.
managedVerificationFallbackLanguageCode (Pro)stringen요청한 언어 JSON이 없을 때 사용할 fallback 언어입니다. 커스텀 파일은 Assets/OZeroSDK/Resources/OZeroLocalization/ozero_ui_text_{code}.json 형식을 사용합니다.

Global Threat Response

보안 위협이 확인되었을 때 게임이 어떻게 반응할지 정합니다. 테스트 중에는 콜백과 로그를 먼저 확인하고, 실제 배포 빌드에서는 플레이어에게 안내할 시간과 종료 정책을 프로젝트 운영 방식에 맞춰 선택하세요.

필드 Type 기본값 설명
forceQuitOnDetection bool true forceQuitOnDetection은 확정 위협이 감지되었을 때 SDK가 게임을 자동 종료할지 결정합니다. 끄면 콜백과 로그만 확인할 수 있어 QA에는 편하지만, 실제 배포 빌드에서는 우회된 클라이언트가 계속 실행될 수 있으므로 신중하게 선택하세요.
fatalCallbackGraceSeconds float 10 fatalCallbackGraceSeconds는 위협 감지 후 게임 쪽 보안 콜백이 플레이어 안내 UI를 보여줄 수 있는 최대 시간입니다. 기본값은 10초입니다. 0으로 설정하면 안내 시간을 두지 않고 즉시 종료하는 이전 방식으로 동작합니다.

연동 문제 자가진단 가이드

OZero Security를 켠 뒤 앱이 종료되거나 특정 환경에서만 문제가 생긴다면, 먼저 로컬 진단 파일로 어느 모듈이 반응했는지 확인하세요. 원인을 추측하기보다 진단 파일의 module, subCode, reason 값을 기준으로 아래 표를 따라가면 됩니다.

Enable Failure Diagnostics는 QA나 고객 지원 중 원인을 확인할 때만 켜세요. 진단 파일에는 모듈명, 해시, 기기 상태, 설치 패키지명, 런타임 설정 일부가 들어갈 수 있습니다. 공개 릴리스 빌드에는 켜둔 채로 배포하지 마세요.

앱이 계속 종료될 때 먼저 할 일

순서 할 일 확인 방법
1Unity 메뉴 Window > OZero Security > Config Dashboard를 열고, OZeroSecurityConfigKey Common Settings > Enable Failure Diagnostics를 켭니다.QA 또는 고객 지원 중 원인을 확인할 때만 사용하고, 공개 릴리스에는 켜둔 채로 배포하지 마세요.
2설정을 더 바꾸기 전에 같은 빌드로 문제를 한 번 재현합니다.재현 전에 여러 설정을 한꺼번에 바꾸면 원인 추적이 어려워집니다. 먼저 현재 상태의 진단 파일을 확보하세요.
3Application.persistentDataPath에서 가장 최근의 ozero_*_failure.log 파일을 엽니다.Build Integrity는 ozero_integrity_failure.logozero_integrity_platform_native_failure.log 같은 세부 파일을 함께 만들 수 있습니다. 앱이 아주 이른 단계에서 종료되어 failure log가 없다면 ozero_abort.txt와 Player log도 확인하세요.
4파일을 확보한 뒤 Enable Failure Diagnostics를 다시 끕니다.지원 프로세스에서 명시적으로 필요하지 않다면 공개 릴리스 빌드에서는 켜두지 마세요.

진단 파일에서 자주 보는 항목

항목 의미 다음 확인
module이벤트를 발생시킨 보호 영역입니다.먼저 같은 이름의 FAQ 행을 보고, 필요하면 Build Integrity, Speed & Time Hack, Injection, Install Source, License 등 해당 매뉴얼 섹션으로 이동하세요.
subCode / checkName짧은 원인 코드 또는 검사 이름입니다.아래 FAQ에서 같은 계열의 증상을 찾는 데 사용하세요. 예를 들어 platform_native는 플랫폼, 에뮬레이터, 서명, 루팅/탈옥 관련 설정을 먼저 보라는 뜻입니다.
reason / subReasonSDK가 반응한 이유를 사람이 읽을 수 있게 요약한 내용입니다.이 문장은 최종 판정이 아니라 힌트입니다. modulesubCode를 먼저 보고, 그 다음에 아래 증상 표에서 같은 키워드가 있는 행을 확인하세요.
platform / buildType앱이 실행된 플랫폼과 빌드 종류입니다.QA용 에뮬레이터/개발 빌드 문제인지, 실제 릴리스 기기 문제인지 분리해서 보세요.

자주 보이는 증상과 첫 확인 사항

증상 가능성이 높은 영역 먼저 해볼 일
OZero를 적용한 뒤 앱이 시작 직후 종료됩니다.시작 과정에서 보안 대응이 실행되었을 수 있습니다.Failure Diagnostics를 켜고 한 번 재현한 뒤 module, subCode, reason을 확인하세요. failure log가 없으면 ozero_abort.txt와 Player log를 확인하고, Unity에서는 Window > OZero Security > Check Setup도 실행하세요.
Android 에뮬레이터에서 앱이 계속 꺼집니다.Build Integrity 또는 플랫폼 네이티브 검사가 에뮬레이터를 미지원 런타임으로 판단했을 수 있습니다.에뮬레이터 QA라면 blockEmulator (Android) 또는 관련 플랫폼 검사 정책을 완화하세요. 단, 릴리스 빌드에서 에뮬레이터를 막아야 하는 게임이라면 스토어 배포 전 정책을 다시 켜야 합니다.
라이선스 활성화가 실패하거나 선택형 Pro 서버 기능을 사용할 수 없습니다.라이선스 키, 앱 식별 정보, 네트워크, 서버 서명 키 문제일 수 있습니다.License Troubleshooting에서 LICENSE_NOT_FOUND, ANDROID_BUNDLE_ID_MISMATCH, timeout, signature verification failure 같은 실제 로그를 확인하세요.
Build Integrity 또는 manifest 검증이 실패합니다.manifest가 없거나 오래되었거나, 서명/해시가 현재 빌드와 맞지 않을 수 있습니다.Check Setup을 실행하고, integrity manifest를 다시 생성하고, signing key를 검증한 뒤 clean build를 만드세요. 코드, IL2CPP 결과물, watched file, manifest signing key가 바뀐 뒤에는 manifest를 다시 생성해야 합니다.
일시정지, 슬로우 모션, 배속 후 앱이 종료됩니다.직접 Time.timeScale을 수정한 코드가 시간 조작처럼 보일 수 있습니다.OZeroTime.timeScale을 사용하세요. TimeScaleTamperExemptions는 검증된 플러그인이나 레거시 adapter script에만 최소로 사용하세요.
긴 로딩 화면에서 앱이 종료됩니다.메인 스레드가 오래 막혀 native Watchdog이 heartbeat를 받지 못했을 수 있습니다.신뢰할 수 있는 긴 로딩 구간만 OZeroWatchdog.BeginLoadingGrace로 감싸세요. 일반 게임플레이 멈춤을 숨기는 용도로 쓰면 안 됩니다.
Android에서 Minify/ProGuard/R8을 켠 뒤 앱이 꺼집니다.Unity bridge 또는 프로젝트의 custom Android bridge class가 제거되거나 이름이 바뀌었을 수 있습니다.Android ProGuard / R8 keep rule을 적용하세요. Unity bridge class와 프로젝트에서 호출하는 Java/Kotlin bridge class를 보존해야 합니다.
깨끗한 Windows PC에서 Standalone 빌드가 바로 종료됩니다.OZero 네이티브 플러그인 또는 VC++ 런타임을 불러오지 못했을 수 있습니다.Microsoft Visual C++ Redistributable 2015-2022 (x64)를 설치하고, OZero 네이티브 플러그인이 빌드 산출물에 포함되었는지 확인하세요.
정상 overlay나 녹화 툴을 켰는데 Injection 이벤트가 나옵니다.Injection/Hooking 모듈이 로드된 모듈을 관측한 상황일 수 있습니다.먼저 Failure Diagnostics 또는 Pro telemetry에서 감지된 모듈 경로, hash, signer fingerprint를 확인하세요. QA로 신뢰할 수 있는 모듈임을 확인한 뒤에만 예외로 등록하세요. 로컬 예외는 Injection 설정의 injectionWhitelistEntries에 등록하고, Pro 고객은 고객 포털의 서버 관리 whitelist로 같은 정책을 운영 중 갱신할 수 있습니다.
optional Built-In TMP Dialog package가 준비되지 않았다는 오류와 함께 빌드가 중단됩니다.Built-In Managed Verification UI 정책을 선택했지만 TextMeshPro, TMP Essential Resources, OZero 다이얼로그 컴포넌트 또는 기본 프리팹이 누락된 상태입니다.Window > OZero Security > Check Setup을 열고 Built-In TMP dialog 오류의 Import Package를 누르세요. package import와 script compilation이 끝난 뒤 Check Setup을 다시 실행하고 빌드하세요. 게임이 자체 UI를 제공한다면 Custom UI / Callback Only를 선택하세요.
릴리스 전 확인: 문제 해결을 위해 완화한 설정은 출시 전에 다시 점검하세요. emulator 차단, 강제 종료 정책, manifest 서명, ProGuard rule, 로컬 진단 파일 설정을 릴리스 정책에 맞게 되돌리거나 의도한 값으로 확정해야 합니다.

빌드 무결성 검증기 설정

수정된 게임 파일, 디버거 연결, 비정상적인 실행 환경을 감지하는 빌드 무결성 검사기를 구성합니다.

필드 Type 기본값 설명
Activate Build Integrity checkbox On Dashboard의 Build Integrity 활성화 체크박스입니다. 내부적으로는 useIntegrity 값에 연결됩니다.
validateOnStartupbooltrue게임 실행 즉시 빌드 무결성 검사를 실행합니다.
validateInEditorboolfalseUnity 에디터에서도 검증을 실행합니다(테스트용으로 유용하지만 일반 개발 중에는 끄는 것을 권장).
enablePeriodicValidationbooltrue게임 실행 중에도 무결성 검증을 반복합니다. 시작 시 1회 검증만으로 충분한 빌드가 아니라면 켜두는 것을 권장합니다.
periodicCheckIntervalfloat300 s게임 실행 중 빌드 무결성 재검증을 반복하는 기준 간격(초)입니다. 0 이하로 설정하면 주기 검사를 끕니다.
periodicCheckJitterPercentfloat35%검증 주기에 무작위 변화를 더해 검사 타이밍을 예측하기 어렵게 합니다. 구체적인 주기와 범위는 공개 문서에서 안내하지 않으며, 기본값 사용을 권장합니다.
timingAnomalyConsecutiveRequiredint7타이밍 기반 이상 신호가 몇 번 누적되어야 디버거 타이밍 드리프트로 판단할지 정합니다. 강한 디버거 신호는 즉시 실패할 수 있습니다.
timingAnomalyWindowSecondsfloat900 s타이밍 기반 이상 신호를 누적하는 시간 창입니다. 길수록 관대하고, Strict는 더 짧은 창을 사용합니다.
timingAnomalyFrameHitchSuppressionSecondsfloat20 s씬 로딩, 셰이더 컴파일, GC, OS 스케줄링 지연처럼 큰 프레임 히치가 발생한 뒤 일정 시간 동안 타이밍 기반 디버거 검사를 완화합니다.
checkAssemblyHashbooltrue빌드 시 생성된 manifest를 기준으로 컴파일된 어셈블리의 해시를 검증합니다.
checkDebuggerbooltrue디버거 연결과 디버거처럼 보이는 런타임 타이밍 이상을 감지합니다.
failOnDebugBuildboolfalseUnity 디버그 빌드를 위반으로 간주합니다(릴리스 빌드에서 권장).
checkPlatformNativebooltrue플랫폼별 네이티브 검사를 실행합니다. 플랫폼에 따라 루팅/탈옥, APK 서명, 실행 환경, 프록시 또는 분석 도구 신호를 확인합니다.
failIfManifestMissingboolfalse*Inspector 기본값은 개발 편의를 위해 꺼져 있습니다. 하지만 development가 아닌 플레이어 빌드에서는 자동으로 켜지며, oz_manifest.ozero 파일이 없거나 읽을 수 없으면 위반으로 처리합니다.
failIfAssemblyHashBlobMissingboolfalse*Inspector 기본값은 개발 편의를 위해 꺼져 있습니다. 하지만 development가 아닌 플레이어 빌드에서는 자동으로 켜지며, 생성된 어셈블리 해시 블롭이 삭제되어도 해시 검증을 우회할 수 없게 합니다.
requireCodeSignature (Windows)boolfalse메인 실행 파일이 코드 서명되어 있어야 합니다. Windows 전용.
blockVirtualMachine (Windows)boolfalse가상 머신 내부에서 게임 실행을 차단합니다. Windows 전용.
blockHyperV (Windows)boolfalseHyper-V VMBus 신호를 차단합니다. WSL2, Docker Desktop, Windows Sandbox 사용자도 막힐 수 있으므로 통제된 환경에서만 신중히 사용하세요.
blockNetworkProxies (Windows)boolfalse네트워크 프록시, 패킷 검사, 트래픽 분석 도구로 의심되는 실행 신호를 감지합니다. 경쟁형 빌드에서 사용하되 오탐 가능성을 테스트하세요.
blockReverseEngineeringTools (Windows)boolfalse역공학 또는 디버깅 도구로 의심되는 실행 신호를 감지합니다. 개발/QA 환경과 라이브 환경을 분리해 검증하세요.
blockSystemMonitorTools (Windows)boolfalse프로세스/시스템 모니터링 도구로 의심되는 실행 신호를 감지합니다. 일반 사용자 환경에서 오탐 가능성을 고려하세요.
il2cppHashGameAssemblybooltrueWindows IL2CPP 빌드의 GameAssembly.dll을 해시 검증합니다. IL2CPP 파일 보호의 최소 권장 항목입니다.
il2cppHashGlobalGameManagersboolfalseWindows IL2CPP 파일 해시 범위에 globalgamemanagers를 추가합니다. 코드 기본값은 꺼짐이지만 Standard와 Strict 프리셋은 이 값을 켭니다.
il2cppHashSharedAssetsboolfalseWindows IL2CPP 파일 해시 범위에 sharedassets* 파일을 추가합니다. 코드 기본값은 꺼짐이지만 Standard와 Strict 프리셋은 이 값을 켭니다.
il2cppHashSceneFilesboolfalseWindows IL2CPP 파일 해시 범위에 level* 같은 Unity 씬 파일을 추가합니다. 코드 기본값은 꺼짐이지만 Standard와 Strict 프리셋은 이 값을 켭니다.
il2cppHashResourcesAssetsboolfalseIL2CPP 검증 범위에 resources.assets를 추가합니다. Strict 모드에 유용하지만 패치 흐름을 먼저 테스트하세요.
il2cppAdditionalWatchedFilesList<string>프로젝트가 별도 네이티브 페이로드를 포함하는 경우, 추가로 감시할 Windows IL2CPP 출력 파일을 지정합니다.
blockEmulator (Android)booltrueAndroid 전용입니다. 에뮬레이터 또는 미지원 런타임 신호를 무결성 위반으로 취급합니다. QA/에뮬레이터 테스트 중에는 완화하고, 프로덕션 빌드에서는 더 엄격한 정책을 권장합니다.
blockSystemRwMount (Android)booltrueAndroid 전용입니다. 시스템 파티션이 쓰기 가능하거나 root 계열 mount 상태가 보이면 무결성 위반으로 처리합니다. Standard 프리셋은 unlocked/rooted QA 기기 오탐을 줄이기 위해 이 값을 완화합니다.
androidShaKeysList<string>기대되는 APK 서명 인증서의 SHA-256 지문 목록. 설치된 APK가 이 키 중 하나로 서명되지 않았다면 검증에 실패합니다(Android 전용).
expectedBundleIds (iOS)List<string>허용할 iOS Bundle ID 목록입니다. 비워두면 Bundle ID 검사를 건너뜁니다.
excludedAssembliesList<string>해시 검증에서 제외할 어셈블리 이름 목록(.dll 확장자 제외). 런타임에 변경되는 어셈블리(예: 생성된 코드)에 사용하세요.
checkIntegrityWithServer (Pro)boolfalsePro의 nonce → attest 흐름을 켭니다. 로컬 Build Integrity가 통과하면 SDK가 빌드 무결성 증거를 OZero에 제출합니다. attestationVerificationMode=OZeroManaged, optional OZero TMP dialog package, 기본 Managed Verification UI를 함께 쓰면 사용자 인증 흐름을 위한 게임 코드를 작성하지 않아도 됩니다.
attestationVerificationMode (Pro)enumCustomerGameServerOZA 토큰의 최종 판정을 누가 수행할지 선택합니다. OZeroManaged는 SDK가 OZero에 관리형 클라이언트 세션 판정을 요청하므로, 자체 백엔드가 없는 팀도 기본 UI로 서버 인증을 사용할 수 있습니다. CustomerGameServer는 고객 서버가 OZero API로 OZA 토큰을 검증하는 구조에 사용합니다.
attestationNetworkPolicy (Pro)enumBestEffort최신 서버 재검증이 필요한 시점에 네트워크 또는 서버가 unavailable일 때의 처리를 정합니다. OZeroManaged와 기본 UI를 함께 쓰면 RequireOnlineRevalidation 상태의 온라인 필요, 재시도, timeout, 차단 안내를 SDK 다이얼로그가 자동으로 처리합니다.
manifestSigningPublicKeystring""서명된 Build Integrity manifest를 검증하는 Base64 RSA-2048 공개키입니다. Window > OZero Security > Config & DashboardGenerate Key Pair로 생성하세요. OZeroLicenseConfig의 고객 포털 서버 키와는 다른 키입니다.
requireManifestSignatureboolfalse*Inspector 기본값은 키 생성 전 개발 편의를 위해 꺼져 있습니다. 릴리스 플레이어 빌드에서는 manifest 서명 검증이 강제로 켜지므로, 출시 전에 반드시 키 쌍을 생성하세요.
manifestSigningPrivateKeyPathstring""빌드 시 manifest 서명에 사용할 private key PEM 경로입니다. 비워 두면 [ProjectRoot]/OZeroSigningKeys/manifest_private_key.pem을 사용합니다. Editor 전용이며 플레이어 빌드에는 포함되지 않습니다.

기본 UI로 사용하는 OZero 서버 인증

코드 없이 쓰는 흐름은 checkIntegrityWithServer를 켜고, attestationVerificationModeOZeroManaged로 설정한 뒤, optional OZero TMP dialog package를 import하고 기본 설정의 Managed Verification UI 정책을 기본 다이얼로그로 유지하면 됩니다. SDK가 nonce를 받고, attest 증거를 제출하고, OZero 관리형 세션 판정을 받은 뒤, 네트워크 필요, 재시도, timeout, 경고, 차단 상태를 기본 다이얼로그로 표시합니다. 기본 UI를 대체하거나 TextMeshPro를 쓰지 않거나 별도 telemetry/session gate를 붙일 때는 RegisterUserManagedVerificationStateCallback을 등록하세요.

💡 Tip: 라이브 빌드를 더 강하게 보호하려면

Pro Strict Attestation은 고객 포털의 Pro 정책에서 켤 수 있는 강화 모드입니다. 이 모드를 사용하면 OZero 서버는 OZA 토큰을 바로 발급하지 않고, 먼저 포털에 등록된 활성 빌드인지 확인합니다. 클라이언트가 제출한 nonce, manifest hash, platform, SDK version, 앱 식별 정보가 같은 요청 흐름에 맞는지도 함께 검사합니다. 또한 debugger, platform, speedhack, injection 검사 결과가 정상이어야 하고, assembly/file/IL2CPP 해시 중 하나가 검증되어야 합니다. 조건이 맞지 않으면 OZA 토큰이 발급되지 않으므로, 새 빌드를 배포하기 전에는 고객 포털에 해당 빌드 버전과 manifest hash를 먼저 등록하세요.

Events

Event Description
OnValidationPassed 로컬 무결성 검사가 위반 없이 완료되었을 때 발생합니다. Pro 서버 검증(attestation)은 아직 진행 중일 수 있습니다.
OnAttestationPassed Pro OZA 검증(attestation) 토큰이 실제 발급된 뒤에만 발생합니다. 게임 서버 로그인, PvP, 랭킹, 재화 흐름은 이 이벤트 또는 AttestationToken.IsValid(nowMillis)로 검사해 통과한 경우에만 진행하세요.
OnValidationFailed 무결성 검사가 위반을 감지했을 때 발생합니다. 글로벌 onHackDetected 이벤트도 ModulationType.BuildIntegrity와 함께 트리거됩니다.

RSA 서명 키 생성 (Build Integrity Manifest)

Build Integrity는 빌드할 때 생성되는 manifest에 RSA 서명을 추가할 수 있습니다. 이 manifest에는 런타임에 검증할 assembly hash와 파일 무결성 정보가 들어 있습니다. 릴리스 빌드에서는 manifest 자체가 바뀌면 검증 기준을 믿을 수 없으므로, 배포 전에 Window > OZero Security > Config & Dashboard에서 Generate Key Pair를 실행해 manifest 서명 키 쌍을 생성하세요.

Manifest 서명이 필요한 이유

빌드 시점에 OZero는 개인 키로 manifest에 서명합니다. 플레이어 빌드에는 개인 키가 포함되지 않고, OZeroSecurityConfig에 저장된 공개 키만 포함됩니다. 런타임에는 SDK가 공개 키로 manifest 서명을 확인합니다. 서명이 맞으면 “빌드 때 만든 manifest가 그대로 유지되었다”고 보고, 그 manifest를 기준으로 assembly/file hash 검사를 진행합니다. 서명이 없거나 맞지 않으면 manifest가 교체되었거나 수정되었을 가능성이 있으므로 Build Integrity 실패로 처리합니다. 이후 동작은 Response Settings 설정에 따라 로그, 콜백, 앱 종료 등으로 이어집니다.

Build Integrity Manifest Signing UI in the Unity Editor

키 쌍 생성하기

  1. Unity 에디터에서 Window > OZero Security > Config & Dashboard를 엽니다.
  2. 인스펙터에서 Build Integrity 섹션을 펼칩니다.
  3. Require Manifest Signature 체크박스를 활성화합니다.
  4. Generate Key Pair 버튼을 클릭합니다.
  5. OZero가 Build Integrity manifest signing용 키 쌍을 생성합니다. 공개 키는 OZeroSecurityConfig에 저장되고, 개인 키는 기본적으로 다음 경로에 저장됩니다:
    [ProjectRoot]/OZeroSigningKeys/manifest_private_key.pem
  6. 개인 키 위치를 보여주는 확인 대화상자가 나타납니다. OK를 클릭하여 닫습니다.
중요 — 개인 키 보안
  • 개인 키 파일은 Unity가 빌드에 포함시키지 않도록 Assets/ 폴더 외부에 저장됩니다. 절대로 Assets/ 안으로 이동하지 마세요.
  • OZeroSigningKeys/는 버전 관리에서 제외하세요. Git을 사용한다면 .gitignore에, SVN을 사용한다면 svn:ignore에 추가합니다. 개인 키를 저장소에 커밋하는 것은 심각한 보안 위험입니다.
  • 개인 키는 안전한 장소에 백업하세요. 이 파일을 가진 사람은 해당 프로젝트의 유효한 Build Integrity manifest를 만들 수 있습니다.
  • 개인 키를 분실한 경우 기존에 출시된 빌드는 그대로 동작합니다. 다만 다음 업데이트 빌드에서는 기존 키로 새 manifest를 서명할 수 없으므로 새 키 쌍을 생성해야 합니다. 새 키를 만들었다면 integrity manifest를 다시 생성하고 clean build로 배포하세요. 공개 키, fingerprint, manifest, signature가 서로 맞지 않으면 Build Integrity 검증이 실패할 수 있습니다.

사용자 지정 개인 키 경로 (CI/CD & 팀 환경)

Manifest Signing Private Key Path 필드는 Generate Key Pair가 새 키를 생성할 위치를 고르는 옵션이 아닙니다. 빌드와 검증 도구가 읽을 PEM 개인 키 위치를 지정하는 옵션입니다. Generate Key Pair는 프로젝트 기본 경로에 키를 만들고 그 경로를 Config에 기록합니다. CI/CD나 팀 빌드에서는 같은 PEM을 안전한 위치에 복원한 뒤 이 필드가 그 경로를 가리키게 하세요.

  • CI/CD 파이프라인 — PEM을 CI Secret에 보관하고 Unity 빌드 직전에 복원합니다. 빌드 러너가 개인 키를 디스크에 상시 보관할 필요는 없습니다.
  • 팀 환경 — 릴리스 빌드 담당자나 빌드 러너만 접근할 수 있는 보안 마운트 경로 또는 시크릿 매니저를 사용합니다. 모든 릴리스 빌드 머신은 같은 PEM을 사용해야 합니다.

기존 키 쌍 유효성 검사

디스크의 개인 키와 OZeroSecurityConfig에 저장된 공개 키가 일치하는지 확인하려면 Validate Key Pair를 클릭하세요. OZero가 PEM에서 공개 키를 다시 계산해 Config의 공개 키와 비교합니다. 일치하지 않으면 맞는 개인 키를 복원하거나 새 키 쌍을 생성한 뒤 clean build를 만들어야 합니다.

키 쌍을 재생성해야 하는 경우
  • 개인 키를 분실하거나 유출된 경우.
  • Validate Key Pair에서 불일치를 보고하는 경우(키가 동기화되지 않음).
  • 예정된 보안 정책의 일환으로 의도적으로 키를 교체하는 경우.

재생성 후에도 기존에 출시된 빌드는 자기 빌드에 포함된 키와 manifest로 그대로 동작합니다. 다음 업데이트 빌드에서는 새 공개 키, fingerprint, manifest, signature가 한 세트로 맞아야 하므로 integrity manifest를 다시 생성하고 clean build로 배포하세요.

듀얼 핑거프린트 키 회전

Generate Key Pair 버튼을 누르면 공개 키가 OZeroSecurityConfig에 저장되고, 같은 키를 식별하는 fingerprint가 Assets/OZeroSDK/Scripts/Security/BuildIntegrity/OZeroManifestTrustAnchor.cs에도 기록됩니다. 런타임에는 먼저 asset의 공개 키가 신뢰된 fingerprint와 맞는지 확인한 뒤, 서명된 manifest를 받아들입니다.

현재 키와 이전 키

ExpectedPublicKeyFingerprintHex는 현재 빌드에서 사용할 키의 fingerprint이고, PreviousPublicKeyFingerprintHex는 키 회전 기간에만 쓰는 보조 슬롯입니다. 이 기능은 manifest 재생성을 대신하지 않으며, 서버에서 키를 자동 관리하는 기능도 아닙니다. OZeroSecurityConfig의 공개 키, fingerprint, manifest, signature는 여전히 같은 키 기준으로 한 세트를 이뤄야 합니다.

안전한 키 회전 절차

  1. 현재 fingerprint를 백업합니다. Assets/OZeroSDK/Scripts/Security/BuildIntegrity/OZeroManifestTrustAnchor.cs를 열어 ExpectedPublicKeyFingerprintHex 값을 임시 메모에 복사해 둡니다.
  2. 이전 키 슬롯에 등록합니다. 같은 파일의 PreviousPublicKeyFingerprintHex에 1단계에서 복사한 값을 붙여 넣고 저장합니다.
  3. 새 키 쌍을 생성합니다. Window > OZero Security > Config & Dashboard를 열고 Build Integrity 섹션에서 Generate Key Pair를 클릭합니다. 새 공개 키와 Expected fingerprint가 자동으로 갱신됩니다. 2단계에서 넣은 Previous 값은 유지하세요.
  4. integrity manifest를 다시 생성하고 clean build를 만듭니다. 새 manifest와 signature는 새 개인 키 기준으로 생성되어야 합니다.
  5. 업데이트 전환 기간을 둡니다. 기존 출시 빌드는 자기 빌드에 포함된 키와 manifest로 계속 동작합니다. Previous 슬롯은 새 빌드 배포 중 롤백이나 산출물 혼재 상황을 줄이기 위한 안전장치입니다. 라이브 게임에서는 보통 1~4주 정도를 기준으로 잡습니다.
  6. 이전 키 슬롯을 비웁니다. 옛 빌드 사용량이 충분히 낮아진 뒤 PreviousPublicKeyFingerprintHex = ""로 되돌리고 한 번 더 릴리스하면 회전이 완료됩니다.
Previous 슬롯이 하는 일

이미 출시된 옛 빌드는 새 키를 몰라도 계속 동작합니다. 옛 빌드에는 당시의 공개 키, fingerprint, manifest, signature가 함께 들어 있기 때문입니다. Previous 슬롯은 새 릴리스 라인에서 통제된 키 회전 기간 동안만 사용하는 안전장치입니다. 전환 기간이 끝나면 다음 릴리스에서 비워 두는 것이 좋습니다.

트러블슈팅

문제가 생기면 먼저 간단한 순서로 확인하세요. 키가 없으면 Generate Key Pair를 실행하고, 키가 있다면 Validate Key Pair를 실행한 뒤, integrity manifest를 다시 생성하고 clean build를 만듭니다. 대부분의 문제는 개인 키 PEM, OZeroSecurityConfig의 공개 키, OZeroManifestTrustAnchor의 fingerprint, 생성된 manifest/signature 파일 중 하나가 서로 맞지 않을 때 발생합니다.

증상 1 — 릴리스 빌드가 실행 직후 종료됨

ExpectedPublicKeyFingerprintHex가 비어 있을 수 있습니다. 릴리스 빌드는 fingerprint가 없으면 안전을 위해 바로 실패합니다. 해결: Generate Key Pair를 실행하고 Unity 재컴파일이 끝난 뒤 clean build를 만드세요. 값이 이미 채워져 있다면 Validate Key Pair를 실행하고 빌드 출력 폴더를 비운 뒤 다시 빌드하세요.

증상 2 — 빌드 시작 직전 "no manifest signing public key is configured" 로 빌드 중단

OZeroSecurityConfig.Integrity.ManifestSigningPublicKey가 비어 있을 때 발생합니다. 해결: Window > OZero Security > Config & Dashboard를 열고 Build Integrity 섹션의 Generate Key Pair를 클릭한 뒤 다시 빌드하세요.

증상 3 — 런타임 로그: "Manifest signing public key does NOT match the pinned trust anchor fingerprint — APK appears to have been repacked with attacker-controlled keys"

OZeroSecurityConfig의 공개 키와 OZeroManifestTrustAnchor에 기록된 fingerprint가 맞지 않을 때 발생합니다. 일부 키 파일만 복원했거나 생성 파일을 수동으로 수정했을 때 자주 보입니다. 해결: Validate Key Pair를 실행하세요. 불일치가 보고되면 맞는 PEM을 복원하거나 새 키 쌍을 생성한 뒤 integrity manifest를 다시 만들고 clean build를 생성하세요. 배포된 빌드에서만 이 메시지가 보이면 APK 또는 실행 파일이 재패키징되었는지도 확인하세요.

증상 4 — 키 회전 후 새 빌드에서 manifest 검증 실패

회전한 키 세트 중 일부가 이전 값일 가능성이 큽니다. 공개 키, fingerprint, manifest, signature가 서로 같은 키 기준인지 확인하세요. 해결: 전환 기간이 필요할 때만 이전 fingerprint를 PreviousPublicKeyFingerprintHex에 유지하고, Validate Key Pair 실행, integrity manifest 재생성, 빌드 출력 폴더 정리, clean build 순서로 다시 빌드하세요.

증상 5 — 로컬 빌드는 성공하지만 CI 빌드가 "private key not found" 또는 잘못된 키로 서명

CI에는 로컬 PC의 OZeroSigningKeys/manifest_private_key.pem 파일이 자동으로 존재하지 않습니다. 해결: PEM을 CI Secret에 보관하고 Unity 빌드 직전에 기본 경로 또는 Manifest Signing Private Key Path에 지정한 경로로 복원하세요. 모든 릴리스 러너가 같은 PEM을 사용해야 하며, 릴리스 환경에서도 Validate Key Pair를 한 번 실행해 확인하는 것을 권장합니다.

설치 출처 설정

게임이 승인된 스토어 또는 경로에서 설치된 경우에만 실행되도록 제한합니다. 사이드로드 또는 재패키징된 APK 방지에 유용합니다.

필드 Type 기본값 설명
Activate Install Source bool true Install Source 모듈을 활성화합니다.
allowGooglePlayStore bool true Google Play 스토어를 통한 설치를 허용합니다.
allowSamsungGalaxyStore bool false Samsung Galaxy Store를 통한 설치를 허용합니다.
allowAmazonAppstore bool false Amazon Appstore를 통한 설치를 허용합니다.
allowHuaweiAppGallery bool false Huawei AppGallery를 통한 설치를 허용합니다.
allowOneStore bool false 원스토어(한국)를 통한 설치를 허용합니다.
allowXiaomiGetApps bool false Xiaomi GetApps를 통한 설치를 허용합니다.
allowOppoAppMarket bool false OPPO App Market를 통한 설치를 허용합니다.
allowVivoAppStore bool false Vivo App Store를 통한 설치를 허용합니다.
allowADB bool false ADB(Android Debug Bridge)를 통한 설치를 허용합니다. 내부 테스트 용도에서만 활성화하세요.
allowDetectionFailedboolfalseAndroid installer package 조회 자체가 실패했을 때도 실행을 허용합니다. ADB처럼 빈 값이 돌아온 상황과 다르며, JNI 호출 실패나 변형 ROM처럼 조회가 끝까지 수행되지 못한 경우입니다. 릴리스 빌드에서는 보통 꺼둡니다.
allowUnknownSourcesboolfalse기본 스토어 목록과 customAuthorizedPackages에 없는 installer package를 허용합니다. 지역 스토어 배포가 필요할 때만 실제 기기에서 확인한 뒤 사용하세요.
enableServerSync (Pro)boolfalsePro 전용입니다. 로컬 감지 후 /v1/install-source/verify로 installer package를 보내 서버 allowlist와 감사 로그를 적용합니다. 서버에 규칙이 아직 없으면 opt-in 방식으로 허용 처리됩니다.
customAuthorizedPackages List<string> 추가로 허용할 설치자 패키지 이름 (예: com.yourcompany.launcher).
reportViolationToCallback bool true 허용되지 않은 설치 출처가 감지되면 서버로 리포트를 전송합니다.
logRawInstallerPackage bool true 감지된 원본 installer package name을 로그로 남깁니다. 커스텀 스토어 패키지명을 찾는 QA 단계에서는 유용하지만, 릴리스 로그 노출 범위를 확인한 뒤 유지 여부를 정하세요.

Steam Anti-Piracy 설정

Steam으로 배포되는 PC 빌드에서 Steam 실행 경로, App ID, 권한 상태, 릴리스 설정을 확인합니다. Standard는 로컬 검증이며, Steam 서버를 통한 더 강한 소유권 검증은 Pro의 서버 Steam Attestation에서 제공합니다.

필드 Type 기본값 설명
Activate Steam Anti-PiracycheckboxOffSteam Anti-Piracy 활성화 체크박스입니다. Steam App ID와 배포 방식이 프로젝트마다 다르므로 기본값은 Off입니다. 먼저 관찰/QA 모드에서 상태를 확인한 뒤 출시 빌드 정책을 강화하세요.
expectedSteamAppIdint0프로젝트의 Steam App ID입니다. 개발 확인용 AppID 480을 사용했다면 출시 전 반드시 실제 App ID로 바꾸세요.
requireSteamLaunchbooltrue게임이 실행 파일 직접 실행이 아니라 Steam을 통해 시작되었는지 확인합니다. 개발 중 직접 실행 테스트와 릴리스 정책을 구분하세요.
requireSteamApiInitbooltrueSteam API 초기화 성공 여부를 확인합니다. 로컬 개발 실행에서는 Steamworks 설정에 따라 실패할 수 있으므로, 최종 판정은 실제 Steam 배포 경로에서 확인하세요.
requireSubscribedCurrentAppbooltrue현재 Steam 계정이 앱을 소유하거나 사용 권한을 가지고 있는지 확인합니다. 이 항목은 로컬 검증이며, 서버 측 소유권 검증은 Pro에서 수행합니다.
requiredDlcAppIdsList<int>소유 여부를 확인할 Steam DLC App ID 목록입니다. 유료 DLC나 필수 DLC가 있을 때 해당 App ID를 추가하세요.
blockSteamAppIdTxtInReleasebooltruesteam_appid.txt는 Steam 없이 로컬 실행 테스트를 할 때만 쓰는 개발용 파일입니다. 릴리스 빌드에 남아 있으면 Steam 우회 실행으로 이어질 수 있어 OZero가 위반으로 처리합니다. 배포 전에 프로젝트 루트와 빌드 출력물에서 제거하세요.
validateSteamApiDllHashboolfalseSteam API DLL 해시를 알려진 SHA-256 값과 비교해 검증합니다.
allowFamilySharing / allowFreeWeekend / allowTimedTrialbooltrueSteam 가족 공유, 무료 주말, 시간 제한 체험 권한 상태를 허용할지 제어합니다.
requireSteamBuildIdNonZeroboolfalseSteam이 Build ID를 0으로 보내거나 Build ID를 확인할 수 없으면 위반으로 처리합니다. Steam depot/build 배포 흐름에서 Build ID가 정상적으로 잡히는 것을 확인한 뒤 켜세요.
expectedSteamApiDllSha256HashesList<string>validateSteamApiDllHash를 켰을 때 비교할 Steam API 파일의 허용 SHA-256 목록입니다. 배포하는 Steamworks SDK 버전, 플랫폼, 릴리스 브랜치별로 해시를 등록하세요.
requireValveSignedSteamApibooltrueSteam API 재배포 파일이 Valve 플랫폼 신원과 일치하는지 확인합니다. Windows는 Authenticode 서명자를 검증하고, macOS는 Valve Team ID를 검증하며, 그 외 플랫폼에서는 SHA-256 검증을 fallback identity check로 사용하세요.
detectKnownSteamEmulatorsbooltrue실행 파일 주변에서 Goldberg, CreamAPI, SmartSteamEmu, ColdClientLoader 같은 Steam 에뮬레이터가 남기는 파일이나 폴더를 찾습니다. 발견되면 정상 Steam 실행이 아닐 가능성이 있는 것으로 봅니다.
requireSteamEnvironmentConsistencybooltrue릴리스 빌드에서 Steam이 정상적인 개인 SteamID와 0이 아닌 Build ID를 돌려주는지 확인합니다. Steam 클라이언트/라이브러리 경로 사전 점검은 경고 로그로 남기고, SteamID나 Build ID가 잘못되면 위반으로 처리합니다.
observeSteamAuthTicketHeuristicbooltrue로컬 Steam 인증 티켓의 크기와 기본 형식만 참고 신호로 확인합니다. 티켓 내용은 로그로 남기지 않으며, 이 검사 하나만으로 게임을 차단하지 않습니다.
detectionActionOZeroSteamDetectionActionCallbackSteam 검증이 실패했을 때 SDK가 로컬에서 어느 단계까지 처리할지 정합니다. Observe는 로그와 진단만 남기고, Callback은 게임 콜백을 호출하며, Block은 위반을 차단 정책으로 올립니다. 실제 앱 종료 여부는 Global Threat Response 설정을 따릅니다.
reportViolationToCallbackbooltrueCallback 모드에서 Steam 검증이 실패했을 때 일반 OZero 보안 콜백으로 전달할지 정합니다. 로그만 보고 게임 콜백은 막고 싶은 QA 빌드에서만 끄세요.
forceSteamAntiPiracyObserveOnlyboolfalseSteam 검증 실패를 임시로 로그와 진단 기록만 남기는 관찰 모드로 처리합니다. 특이한 Steam 실행 환경을 테스트할 때만 사용하고, 릴리스 전에는 다시 끄세요.
checkSteamDrmWithServer (Pro)boolfalsePro 전용입니다. 서버 근거를 사용한 Steam DRM 검증을 켭니다. 로컬 Steamworks 상태만 보지 않고 Steam 소유권을 서버 검증으로 확인해야 할 때 사용하세요.
steamDrmVerificationMode (Pro)OZeroSteamDrmVerificationModeOZero ManagedSteam DRM 검증 주체를 선택합니다. OZeroManaged는 SDK가 OZero /v1/steam/attest를 직접 호출하고, activation cache를 관리하며, optional OZero TMP dialog package를 import한 뒤 사용자 표시 상태를 기본 Managed Verification UI로 전달합니다. CustomerGameServer는 고객 서버가 OZA 토큰과 Steam 티켓을 받아 OZero 서버 API를 호출하는 구조입니다.
steamDrmNetworkPolicy (Pro)OZeroSteamDrmNetworkPolicyBest Effort최신 서버 재검증이 필요한 시점에 네트워크 또는 서버가 unavailable일 때의 처리를 정합니다. OZeroManaged와 import된 기본 UI package를 함께 쓰면 RequireOnlineRevalidation은 게임 코드 없이 온라인 필요와 재시도 흐름을 자동으로 표시합니다.
Standard Steam Anti-Piracy 출시 빌드는 Unity가 지원하는 대상에서 IL2CPP를 사용하고, Build Integrity를 Standard 또는 Strict 프리셋으로 켠 상태를 유지하세요. manifest trust-anchor fingerprint가 고정되어 있는지 확인하고, Steamworks SDK 파일을 교체할 때마다 Tools > OZero Security > Steam Anti-Piracy > Scan Steam Redistributable을 다시 실행하세요. unsupported accessor, 비표준 library path, verification-unavailable 같은 compatibility 진단은 단독 불법복제 증거가 아니라 먼저 QA 신호로 다루세요.
Steam Activation / DRM이 OZeroManaged를 사용하면 보호 대상 첫 실행은 OZero 서버를 통해 유효한 activation token을 온라인에서 받습니다. 이후에는 유효한 cache token과 오프라인 유예 시간이 남아 있으면 오프라인 실행을 허용할 수 있습니다. 토큰이 만료되었거나 유예 시간이 끝났거나 재검증이 필요한 시점에 사용자가 오프라인이라면 OZeroSecurity_BuiltInDialog_TMP.unitypackage를 import한 기본 Managed Verification UI가 온라인 필요 안내, Retry 버튼, retry timeout, 차단 상태를 자동으로 처리합니다.

OZero 서버 직접 Steam DRM 인증

checkSteamDrmWithServer를 켜고, steamDrmVerificationModeOZeroManaged로 설정한 뒤, OZeroSecurity_BuiltInDialog_TMP.unitypackage를 import하고 기본 설정의 Managed Verification UI 정책을 기본 다이얼로그로 유지합니다. SDK가 Steam 인증 티켓을 OZero 서버로 보내고 activation token cache를 관리하며, 온라인 필요, 재시도, timeout, 경고, 차단 상태를 기본 UI로 표시합니다. 기본 UI를 그대로 쓰면 callback이나 retry 코드를 작성할 필요가 없습니다.

고객 서버에서 Steam 인증

checkSteamDrmWithServer를 켜고 steamDrmVerificationModeCustomerGameServer로 설정합니다. 클라이언트는 OZA 토큰과 Steam Auth Ticket을 고객 서버로 보내고, 고객 서버가 /v1/validate/v1/steam/attest를 호출합니다. Steam Web API Key와 OZero Server API Key는 서버에만 보관하고, 네트워크 복구 후에는 SDK 재시도가 아니라 고객 서버의 로그인/세션 요청을 다시 보내는 방식으로 처리하세요.

디바이스 바인딩 설정

처음 신뢰한 기기의 식별 정보를 저장하고, 이후 실행 때 같은 기기인지 비교합니다. 같은 계정이나 보호된 세이브 데이터가 다른 기기로 옮겨졌는지 확인하는 데 사용합니다.

운영에서 사용하는 방식

Pro에서는 Device Binding을 운영 도구로 사용할 수 있습니다. 서버가 라이선스별 기기 지문을 기억하므로, 반복적으로 위험 신호가 나오는 기기를 차단하고, 단순 라이선스 공유를 줄이며, 정상적인 기기 변경은 전체 라이선스를 막지 않고 지원할 수 있습니다.

반복적으로 위험한 기기 차단

SpeedHack, Injection, Install Source, Build Integrity 같은 보안 이벤트를 검토한 뒤 사용하세요. 차단된 기기는 다음 활성화, 기기 등록, 기기 검증, 정책 확인에서 DEVICE_BLOCKED를 받습니다.

정상적인 기기 변경 지원

실제 플레이어가 휴대폰을 바꾸거나, OS를 다시 설치하거나, 하드웨어를 교체했거나, 정상적인 환경 변화로 기기 정보가 달라졌을 때 Reset Token을 사용합니다. 토큰은 5분 동안만 유효하고 한 번만 사용할 수 있으며, 정확한 라이선스와 기기에 묶입니다.

등록 가능한 기기 수 제한

Pro 라이선스에서는 maxDevices로 한 라이선스에 등록할 수 있는 활성 기기 지문 수를 제한합니다. 차단된 기기가 자동으로 안전한 빈 슬롯이 되는 것은 아니므로, 리셋이나 삭제는 고객지원 확인 후 처리하세요.

Reset Token 지원 절차

  1. 기기 초기화가 필요한 이유와 플레이어 계정을 일반 고객지원 절차로 먼저 확인합니다.
  2. Customer Portal → Device Binding에서 대상 기기를 선택하고 Reset Token을 발급합니다.
  3. 토큰은 인증된 고객지원 채널로만 전달하세요. 공개 채팅, 스크린샷, 오래 남는 티켓에 남겨두지 마세요.
  4. 게임 또는 지원용 UI에서 토큰을 OZeroDeviceBindingDetector.Instance?.ClearStoredFingerprint(token.Trim())에 전달합니다.
  5. SDK가 토큰을 승인한 뒤 앱을 다시 시작하거나 보호 초기화 흐름을 다시 실행하면, 현재 기기 지문이 다시 등록됩니다.
Device Binding을 실제 사용자를 100% 증명하거나 영구적인 하드웨어 신원을 보장하는 기능처럼 설명하지 마세요. OS 초기화, 개인정보 설정 변경, 하드웨어 교체 후에는 플랫폼 식별자가 바뀔 수 있습니다. 라이브 게임에서는 공유와 악용 비용을 높이는 운영 레이어로 설명하고, Build Integrity, Injection, SpeedHack, Install Source, 서버 검증과 함께 사용하세요.
필드 Type 기본값 설명
Activate Device Binding bool true Device Binding 검사를 켭니다. 켜면 SDK가 시작 시 로컬 기기 지문을 만들고 저장된 값과 비교합니다.
hardwareChangeTolerance int (0–3) 1 기기 지문을 구성하는 항목이 몇 개까지 달라져도 같은 기기로 볼지 정합니다. 0은 엄격하게 보고, 3은 큰 하드웨어/OS 변화도 더 넓게 허용합니다. 기본값 1은 작은 OS 또는 펌웨어 변경을 흡수하기 위한 값입니다.
storageKey string "ozero_dfp" SDK가 암호화된 로컬 기기 지문을 저장할 때 사용하는 PlayerPrefs 키입니다. 게임에서 이미 같은 키를 쓰고 있을 때만 변경하세요.
enableServerSync (Pro) bool false Pro 전용입니다. 첫 사용 시 /v1/device/register로 기기 지문을 등록하고, 이후 실행에서는 /v1/device/verify로 서버 기록과 비교합니다. 네트워크나 서버 장애만으로 게임을 막지는 않지만, 서버가 DEVICE_BLOCKED, FINGERPRINT_MISMATCH, DEVICE_LIMIT_REACHED처럼 명확한 거부를 반환하면 Device Binding 위반으로 처리합니다.
maxDevices (Pro)int0한 라이선스에 등록할 수 있는 기기 수를 보여주는 참고값입니다. 0은 제한 없음을 뜻합니다. 실제 한도는 서버 라이선스 기록 또는 고객 포털 정책으로 적용되므로, 클라이언트에서 이 값만 바꿔도 운영 한도가 늘어나지는 않습니다.

Speed & Time Hack 설정

Unity 시간, 네이티브 기준 시계, 백그라운드 타이머, 웹/Pro 서버 시간을 서로 비교해 게임 속도를 빠르게 또는 느리게 바꾸는 조작과 기기 시계 변경을 감지합니다.

필드 Type 기본값 설명
Activate Speed & Time Hack bool true Speed & Time Hack 검사를 켭니다. 켜면 SDK가 실행 중 시간 흐름 비율과 기기 시계 변경을 감시합니다.
autoStart bool true 게임 시작 시 감지를 자동으로 시작합니다. 끄면 OZeroSpeedHackDetector.StartDetection()을 직접 호출해야 합니다.
checkInterval float 1.0 s Unity 시간과 기준 타이머를 비교하는 주기입니다. 값을 너무 낮추면 더 빨리 반응하지만 CPU 사용량과 오탐 가능성이 늘 수 있습니다.
requiredDetections int 3 위반으로 처리하기 전에 이상 신호가 연속으로 몇 번 나와야 하는지 정합니다. 불안정한 기기에서 오탐을 줄이고 싶으면 값을 높이세요.
ratioTolerance float 0.15 Unity 시간과 네이티브 기준 시간이 이 비율만큼 차이 나는 것은 정상으로 봅니다. 기본값으로 시작하고 실제 플레이에서 오탐이 있을 때만 조정하세요.
maxAllowedRatio float 4.0 시간 흐름 비율이 이 값을 넘으면 강한 스피드핵 신호로 봅니다. 일반 게임은 기본값을 유지하세요.
detectSlowHack bool false 시간을 빠르게 만드는 조작뿐 아니라 느리게 만드는 조작도 감지합니다. 기본값은 false입니다. 슬로우 모션, 컷신, 연출 속도 변경이 많은 게임은 실제 플레이 흐름을 확인한 뒤 켜세요.
enableTimeScaleDetection bool true Time.timeScale이 허용되지 않은 코드나 메모리 편집으로 바뀌었는지 확인합니다. 의도적으로 시간 배율을 바꾸는 코드는 OZeroTime.timeScale을 사용하세요.
hackDetectMultiplier float 1.3 시간 흐름이 maxAllowedRatio보다 훨씬 크게 튀었을 때 더 강한 의심 신호로 세는 기준입니다. 일반적으로 기본값을 유지하세요.
enableThreadTimerCheck bool true 백그라운드 스레드 타이머를 추가 기준 시계로 사용합니다. 메인 스레드만 조작하는 도구를 잡는 데 도움이 됩니다.
useWebTimeValidation bool true HTTPS 시간 응답의 Date 헤더 또는 Pro 서버 시간을 사용해 기기 시계 조작을 교차 확인합니다. 기본값은 true입니다.
webSyncInterval float 15 s 웹/서버 시간과 다시 비교하는 주기입니다. 너무 짧게 잡으면 네트워크 요청이 늘어납니다.
webRatioTolerance float 0.15 기기 시간과 웹/서버 시간이 이 비율만큼 차이 나는 것은 정상으로 봅니다. 지역별 네트워크 지연을 고려해 실제 기기에서 확인하세요.
timeOffsetTolerance float 60 s 기기 시계와 웹/서버 시간의 절대 차이가 이 초 단위를 넘으면 시간 조작으로 봅니다.
focusIgnoreTime float 4 s 앱이 백그라운드에서 돌아온 직후 이 시간 동안 감지 결과를 무시합니다. OS가 앱을 멈췄다가 재개할 때 생기는 오탐을 줄입니다.
loadingGraceTime float 6 s 앱 시작이나 씬 로딩 직후 이 시간 동안 감지 결과를 무시합니다. 무거운 로딩 구간의 오탐을 줄입니다.
lagSpikeIgnore float 0.5 s 프레임 시간이 이 값을 넘는 샘플은 지연으로 보고 버립니다. 실제 렉을 시간 조작으로 오해하지 않기 위한 설정입니다.
buildFailIfTimeScaleTamperedbooltrue보호 대상 코드가 정책 밖에서 Time.timeScale을 직접 바꾸는 것으로 보이면 빌드/검증 단계에서 실패하게 합니다.
timeScaleTamperExemptionsList<string>Time.timeScale을 직접 바꿔도 되는 스크립트/메서드 이름 패턴입니다. 일시정지, 불릿타임, 컷신처럼 이미 검토한 코드만 최소한으로 등록하세요.
webTimeUrlsstring[]웹/서버 시간 확인에 사용할 HTTPS 주소 목록입니다. 하나의 주소가 막혀도 검사가 멈추지 않도록, 직접 운영하거나 신뢰할 수 있는 주소를 2개 이상 넣는 것을 권장합니다.
minSuccessfulEndpointsint2한 번 검사할 때 최소 몇 개의 주소가 정상적인 Date 헤더를 돌려줘야 시간을 신뢰할지 정합니다. 등록한 주소 수보다 크게 잡지 마세요.
maxConsecutiveFailuresint6웹/서버 시간 확인이 연속으로 이 횟수만큼 실패하면 onWebTimeUnavailable 정책을 실행합니다. 일시적인 네트워크 오류까지 바로 위반으로 보지 않기 위한 완충값입니다.
onWebTimeUnavailableWebTimeUnavailablePolicyWarnOnly웹/서버 시간을 계속 확인하지 못할 때 어떻게 처리할지 정합니다. WarnOnly는 경고만 남기고 계속 실행합니다. Strict는 반복 실패를 의심 상황으로 보고 SpeedHack 콜백을 발생시킵니다. Silent는 로그도 남기지 않으므로 특수 테스트가 아니면 사용하지 마세요.
detectTimeHackbooltrue기기 시계 변경과 웹/서버 시간 차이를 검사합니다. 속도 비율 검사와 별도로 시스템 시간 조작을 잡는 옵션입니다.
webSyncJitterPercentfloat20%웹/서버 시간 확인 시간이 모든 기기에서 한꺼번에 겹치지 않도록, 설정한 주기 주변에서 호출 시점을 조금씩 분산합니다. 예: 20%이면 15초 주기가 대략 12~18초 사이에서 실행됩니다.
sustainedLagThresholdfloat0.15최근 프레임 평균이 이 값보다 느리면 SDK는 먼저 실제 렉 상황으로 보고, 그동안의 시간 검사 결과를 바로 위반으로 확정하지 않습니다. 기본 0.15초는 평균 약 6~7 FPS 수준입니다.
sustainedLagGraceDurationfloat3 s렉으로 판단된 뒤 시간 검사를 잠시 보류하는 시간입니다. 렉이 이어지는 동안에는 이 보류 시간이 계속 연장되고, 런타임에서는 최대 10초 안에서 제한됩니다.
overloadStrictMultiplierint3onWebTimeUnavailable이 Strict일 때, 실패가 서버의 명확한 거부가 아니라 단순 타임아웃처럼 보이면 몇 번 더 기다릴지 정하는 배수입니다. 기본 3maxConsecutiveFailures의 3배까지 기다린 뒤 콜백을 발생시킨다는 뜻입니다.
enableRemoteSpeedHackConfig (Pro)boolfalsePro 서버 정책으로 일부 Speed & Time Hack 기준값을 앱 업데이트 없이 조정합니다. 서버에 연결할 수 없으면 로컬 Inspector 값으로 계속 동작하므로 보호가 꺼지지는 않습니다.
remoteSpeedHackConfigIntervalfloat300 sPro 서버에서 Speed & Time Hack 원격 설정을 다시 가져오는 주기입니다. 0이면 앱 시작 시 한 번만 가져오고, 양수이면 주기적으로 새 정책을 확인합니다.
remoteSpeedHackConfigJitterPercentfloat20%Pro 원격 설정을 여러 기기가 동시에 요청하지 않도록, 설정 갱신 시간을 조금씩 분산합니다.
enableSignedServerTime (Pro)boolfalsePro 활성화가 가능할 때 서명된 /v1/time 응답을 우선 신뢰 시간으로 사용합니다. 서버 키와 시간 엔드포인트를 설정하고 테스트한 뒤 켜세요.

Time.timeScale 대신 OZeroTime.timeScale 사용

Speed & Time Hack 보호 모듈이 활성화된 상태에서는 Unity의 기본 Time.timeScale을 프로젝트 코드에서 직접 수정하면 안 됩니다. OZero를 거치지 않고 직접 수정하면, OZero 디텍터는 이를 외부 치트 툴에 의한 의심스러운 런타임 시간 조작으로 판단하고 설정된 대응 정책을 실행할 수 있습니다.

예외 조항 (TimeScaleTamperExemptions)

OZeroSecurityConfig.TimeScaleTamperExemptions는 코드에서 Time.timeScale을 직접 수정해야 하는 스크립트를 등록하는 예외 처리 목록입니다. 이 목록은 최소한으로 유지해야 합니다.

구조상 직접 Time.timeScale에 접근할 수밖에 없는 검증된 외부 플러그인이나 레거시 어댑터 스크립트(adapter script)에만 제한적으로 할당하세요.

이를 제외한 프로젝트 내 모든 게임 로직에서는 예외 없이 OZeroTime.timeScale을 우선 사용해야 합니다. 일시정지, 슬로우 모션, 컷신 속도 제어, 배속 플레이 등 게임 내에서 의도적으로 속도를 변경할 때는 반드시 OZeroTime.timeScale을 사용하세요.

using OZeroSDK.Security;

// Correct: the detector knows this is an intentional game-side change.
OZeroTime.timeScale = 0.5f;

// Avoid: this bypasses OZero's expected time-scale tracking.
Time.timeScale = 0.5f;
릴리스 전에는 Tools > OZero Security > Check Time.timeScale Usage를 실행하고, 가능한 모든 직접 수정 코드를 OZeroTime.timeScale로 교체하세요.

장시간 로딩 시 Watchdog 의도치 않은 종료 방지하기

OZero의 네이티브 Watchdog은 Unity 메인 스레드가 정상적으로 살아 있는지 주기적인 heartbeat로 확인합니다. 릴리스 빌드에서는 비-Android 플랫폼은 약 6초, Android는 시작 유예 이후 약 10초 동안 heartbeat가 들어오지 않으면 앱이 멈춘 것으로 판단하고 설정된 대응 정책에 따라 종료할 수 있습니다.

대형 씬 로딩, 동기식 에셋 압축 해제, 셰이더 warmup, Addressables 준비처럼 메인 스레드가 의도적으로 몇 초 이상 막히는 정상 작업에서도 이 상황이 발생할 수 있습니다.

이런 신뢰 가능한 로딩 구간은 OZeroWatchdog.BeginLoadingGrace(maxGraceMs)로 감싸세요. 로딩이 시작될 때 grace가 시작되고, scope가 dispose되거나 End()가 호출되는 순간 즉시 종료되므로 실제 로딩 시간을 미리 정확히 알 필요가 없습니다.

using OZeroSDK.Security;
using UnityEngine.SceneManagement;

public void LoadLargeScene()
{
    using (OZeroWatchdog.BeginLoadingGrace(60000))
    {
        SceneManager.LoadScene("Battle", LoadSceneMode.Single);
        // Grace ends as soon as the using scope exits.
    }
}

public void WarmUpLargeAssets()
{
    OZeroWatchdog.RunWithLoadingGrace(() =>
    {
        BuildLargeRuntimeCache();
    }, 60000);
}
주의: Loading Grace 오용 금지

이 기능은 긴 로딩 중 시스템에 의해 앱이 강제 종료되는 것을 방지하는 임시 유예 기능입니다. 보안 우회나 앱을 계속 켜두는 용도로 사용할 수 없습니다.

허용 시간을 초과할 수 있는 무거운 로딩 작업이 있다면, 작업을 작은 단위로 나누어 처리하거나 Unity의 코루틴/비동기(async) 기능을 사용해 화면과 메인 스레드가 멈춰 있지 않도록 제어하세요.

Physics Hack Detector

이동하는 플레이어 오브젝트마다 컴포넌트를 붙이는 방식입니다.
OZeroSecurityConfig.PhysicsHack는 전체 켜기/끄기와 Pro telemetry만 제어합니다. 이동 속도, 거리 허용치, 벽 통과 검사, 가속도 검사는 각 OZeroPhysicsHackDetector 컴포넌트에서 설정합니다. 플레이어 프리팹에 컴포넌트를 붙이고, 캐릭터나 차량의 실제 이동 규칙에 맞게 값을 조정한 뒤, 스폰 로직에서 Initialize(playerId)를 호출하세요.
필드 Type 기본값 설명
physicsHack.useGlobalPhysicsHackbooltrue모든 Physics Hack 컴포넌트에 적용되는 전체 켜기/끄기 스위치입니다. 모든 물리 이동 검사를 의도적으로 멈춰야 할 때만 끄세요.
physicsHack.enableServerTelemetry (Pro)boolfalsePro 전용이며 기본값은 꺼짐입니다. 프로젝트가 명시적으로 동의한 경우에만 일반 보안 이벤트와 상세 PhysicsHack telemetry를 전송합니다. 끄면 신규 telemetry 전송을 중단하며, 서버 정책은 로컬 동의 없이 전송을 켤 수 없고 끄기만 할 수 있습니다.
physicsHack.telemetryThrottlePerMinute (Pro)int30이 클라이언트가 1분 동안 보낼 수 있는 PhysicsHack telemetry 수를 제한합니다. 기준값을 잘못 잡았을 때 서버로 이벤트가 과도하게 몰리는 것을 막습니다.
필드 Type 기본값 설명
maxAllowedSpeed float 15 u/s 이 오브젝트가 정상적으로 낼 수 있는 최대 이동 속도입니다(Unity units/second). 캐릭터나 차량이 실제 게임에서 낼 수 있는 가장 빠른 정상 속도에 맞춰 설정하세요.
distanceTolerance float 2.0 u maxAllowedSpeed로 계산한 이동 가능 거리 외에 추가로 허용할 거리입니다. 네트워크 위치 보정, 물리 계산 오차, 컨트롤러의 작은 흔들림을 흡수할 때 사용합니다.
obstacleLayer LayerMask 벽이나 단단한 장애물로 볼 레이어입니다. OZero는 이전 안전 위치에서 현재 위치까지의 경로를 검사하고, 중간에 이 레이어가 있으면 벽 통과로 볼 수 있습니다.
checkInterval float 0.05 s 이 컴포넌트가 위치, 벽 통과, 가속도 이상을 확인하는 주기입니다. 값을 낮추면 더 빨리 반응하지만 CPU 사용량이 늘 수 있습니다.
maxDeltaTimeCap float 0.1 s 거리 계산에 사용할 프레임 시간의 최대값입니다. 한 프레임이 길게 밀렸다는 이유로 허용 이동 거리가 지나치게 커지는 일을 막습니다.
violationThresholdint2콜백을 발생시키기 전에 몇 번의 이상 검사가 연속으로 필요한지 정합니다. 정상 검사가 나오면 카운터가 줄어들어, 작은 물리 흔들림 한 번이 바로 위반으로 이어지지 않습니다.
castRadiusfloat0벽 통과 검사에 사용할 반경입니다. 0이면 CharacterController 또는 CapsuleCollider에서 자동으로 추정하고, 찾지 못하면 선 검사로 대체합니다.
enableAccelerationCheckbooltrueRigidbody 속도가 갑자기 크게 바뀌는지 확인합니다. kinematic 오브젝트, 서버 권위 이동, 커스텀 이동처럼 물리 방식이 아닌 속도 변경이 정상적으로 발생하는 오브젝트에서는 끄는 것을 검토하세요.
maxAllowedAccelerationfloat60검사 사이에 허용할 최대 속도 변화량입니다. 대시, 넉백, 점프대, 차량처럼 순간적으로 크게 가속하는 정상 이동이 있다면 그 값에 맞춰 높이세요.
enableLogbooltrue튜닝 중 이 컴포넌트의 디버그 로그를 표시합니다. 단, obstacle layer가 비어 있는 것처럼 중요한 설정 문제는 이 값이 꺼져 있어도 경고합니다.

Injection Detector 설정

예상하지 못한 네이티브 모듈 로드, 실행 가능한 private memory, inline hook, 원격 thread 방식의 주입, 위험한 외부 프로세스 handle, WebGL 런타임 변조 신호를 감지합니다.

대부분의 프로젝트는 기본값으로 시작하면 됩니다.
정상 overlay, 녹화 도구, 파트너 DLL처럼 게임과 함께 배포하는 모듈이 Injection으로 잡히는 경우에만 Failure Diagnostics 또는 Pro telemetry에서 모듈 경로, 파일 hash, signer fingerprint를 확인한 뒤 whitelist에 추가하세요. 고객 PC에 우연히 설치된 프로그램을 넓게 허용하는 용도로 쓰면 보호 범위가 약해집니다.
필드 Type 기본값 설명
Activate Injection & HookingcheckboxOnConfig Dashboard의 Injection & Hooking 켜기/끄기 항목입니다. 내부적으로는 useInjection에 연결됩니다. 플레이어 빌드에서는 네이티브 Injection 검사를 실행하고, 개발 빌드에서는 원인 확인을 위해 완화된 진단 흐름으로 동작하며, 에디터에서는 일반적으로 검사하지 않습니다.
injectionWhitelistEntriesOZeroInjectionWhitelistEntry[]empty모든 티어에서 사용할 수 있는 로컬 신뢰 모듈 목록입니다. 각 항목은 모듈 파일의 SHA-256 hash와 선택 signer fingerprint로 특정 파일 하나를 허용합니다. 게임과 함께 배포하거나 QA, 진단 파일, Pro telemetry, OZero 지원팀 안내로 검증한 모듈만 등록하세요.
enableServerWhitelist (Pro)boolfalsePro 전용입니다. 서버에서 관리하는 신뢰 모듈 목록을 내려받아 SDK 기본 목록, 로컬 목록과 합칩니다. 서버에 연결할 수 없어도 보호가 꺼지는 것은 아니며, 마지막으로 적용된 목록과 로컬 기준으로 계속 검사합니다.
serverWhitelistRefreshIntervalfloat0 sPro 서버 whitelist를 다시 가져오는 주기입니다. 0이면 앱 시작 시 한 번만 가져옵니다. 신뢰 모듈 목록은 자주 바뀌지 않으므로 대부분의 프로젝트는 0으로 충분합니다.
requireSignerForNativeWhitelistboolfalseWindows PE와 Apple Mach-O whitelist 항목에 파일 hash뿐 아니라 signer fingerprint도 요구합니다. 더 강한 방식이지만 정확한 signer 값을 수집할 수 있을 때만 켜세요. Android/Linux 공유 라이브러리는 보통 hash-only로 처리합니다.
enableRemoteInjectionConfig (Pro)booltruePro 전용입니다. 포털 정책으로 검사 주기, signer 요구 여부, 개별 검사 스위치 같은 Injection 설정 일부를 앱 업데이트 없이 조정할 수 있습니다.
remoteInjectionConfigIntervalfloat300 sPro 원격 Injection 설정을 다시 확인하는 주기입니다. 0이면 앱 시작 시 한 번만 가져옵니다.
enableWindowsModuleIdentityScanbooltrue새로 로드된 네이티브 모듈이 신뢰할 수 있는 모듈인지 확인합니다. 필드 이름에는 Windows가 남아 있지만 지원되는 네이티브 타깃에서 같은 설정을 사용합니다. Windows/macOS는 hash와 signer를 함께 쓸 수 있고, Android/iOS/Linux는 보통 hash-only로 비교합니다.
scanIntervalSecondsfloat1 sInjection 검사를 실행하는 기본 주기입니다. 값을 낮추면 더 빨리 감지하지만 런타임 비용이 늘 수 있습니다. 실제 적용값은 안전한 범위로 제한됩니다.
windowsModuleIdentityScanIntervalSecondsfloat5 s상대적으로 무거운 네이티브 모듈 식별 검사의 별도 주기입니다. 모듈 열거, hash 계산, signer 확인이 대상 기기에서 부담된다면 이 값을 늘려보세요.
scanJitterPercentfloat20%주기 검사 시간이 항상 정확히 같은 초에 실행되지 않도록, 설정한 주기 주변에서 실행 시점을 조금 분산합니다.
enableExecutablePrivateMemoryScanbooltrue정상 DLL/.so/.dylib로 로드되지 않았는데 실행 권한을 가진 private memory 영역을 감지합니다. shellcode 방식의 주입을 잡는 데 유용하지만, 신뢰하는 런타임이 실행 가능한 메모리를 직접 만드는 경우에는 QA 확인이 필요합니다.
enableInlineHookScanboolfalse중요 네이티브 API 진입점이 패치된 흔적이 있는지 확인합니다. 강한 신호지만 overlay나 보안 소프트웨어도 API를 hook할 수 있으므로, Strict 성격의 빌드에서 호환성 테스트 후 켜는 것을 권장합니다.
enableThreadStartAddressScanbooltrue네이티브 thread가 신뢰 모듈 밖의 의심스러운 메모리에서 시작했는지 확인합니다. 지원 플랫폼에서 remote-thread 방식의 주입을 잡는 데 도움이 됩니다.
enableExternalProcessHandleScanbooltrue디버거, 메모리 에디터처럼 외부 프로세스가 게임 프로세스에 위험한 handle을 들고 있는지 확인합니다. 주로 Windows에서 사용됩니다.
detectionConfidenceThresholdint70강한 Injection 신호를 즉시 위반으로 볼 때 필요한 최소 신뢰 점수입니다. 점수가 낮은 신호도 연속 감지나 누적 판단 로직으로 처리될 수 있습니다.
enableWebRuntimeTamperScan (WebGL)booltrueWebGL 전용 브라우저 런타임 검사입니다. WebGL은 Native C++ 모듈 스캔을 사용할 수 없으므로, 브라우저 측 변조 신호를 수집하는 보조 클라이언트 증거로 이해하세요.
webRuntimeScanIntervalSeconds (WebGL)float1.5 sWebGL 런타임 검사의 기본 주기입니다. 브라우저를 너무 자주 검사하지 않도록 실제 적용값은 0.5~30초 범위로 제한됩니다.
webRuntimeScanJitterPercent (WebGL)float20%WebGL 런타임 검사가 항상 같은 순간에 실행되지 않도록, 설정한 주기 주변에서 실행 시점을 조금 분산합니다.
WebGL probesboolstrueWebGL 빌드에서 DevTools, clock hook, network hook, WebAssembly hook, storage hook, crypto API hook 검사를 각각 켜고 끄는 스위치입니다.

PlayerPrefs 암호화

Unity의 PlayerPrefs에 저장하던 값 중 보호가 필요한 새 key는 OZeroSafePlayerPrefs로 저장하세요. 자주 쓰는 Get/Set, HasKey, Delete, Save 형태는 익숙한 이름으로 제공되지만, 암호화된 데이터는 기존 plain PlayerPrefs 데이터와 호환되지 않습니다.

using OZeroSDK.Security;

// Save a value
OZeroSafePlayerPrefs.SetInt("score", 4200);
OZeroSafePlayerPrefs.SetFloat("volume", 0.8f);
OZeroSafePlayerPrefs.SetString("username", "Hero");

// Read a value
int    score    = OZeroSafePlayerPrefs.GetInt("score", 0);
float  volume   = OZeroSafePlayerPrefs.GetFloat("volume", 1.0f);
string username = OZeroSafePlayerPrefs.GetString("username", "");

세이브 파일 암호화

세이브 파일을 암호화하려면 File.ReadAllText / File.WriteAllText 대신 OZeroSV_File을 사용하세요. 파일은 쓸 때 자동으로 암호화되고 읽을 때 복호화됩니다. 로드 시 변조 여부도 검사합니다.

using OZeroSDK.Security;

string path = Application.persistentDataPath + "/save.json";

// Write encrypted file
OZeroSV_File.WriteAllText(path, jsonString);

// Read and decrypt file
string json = OZeroSV_File.ReadAllText(path);
중요: 출시 후에는 developerSecret을 변경하지 마세요. developerSecretOZeroSafePlayerPrefsOZeroSV_File 데이터를 보호하는 암호화 키의 기준값입니다. 출시 후 이 값을 바꾸면 기존 버전에서 저장한 보호 데이터를 새 버전에서 복호화할 수 없습니다.

4 인게임 변수 보호 (Secure Types)

Secure Types는 일반 C# 변수 타입을 암호화된 타입으로 대체합니다. 값이 Native C++ 힙에 저장되어 Cheat Engine 같은 도구로는 메모리를 스캔해도 찾을 수 없습니다. 타입 이름만 바꾸면 되고 나머지 코드는 그대로 동작합니다.

Unity Inspector showing OZeroSV_Int and OZeroSV_Float fields

예시 코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OZeroSDK.Security;

public class PlayerStats : MonoBehaviour
{
    [SerializeField] OZeroSV_Int Gold = 5000;
    [SerializeField] OZeroSV_Float Speed = 3.5f;
    [SerializeField] OZeroSV_Int HP = 1000;
}

지원 타입

기존 OZero 적용 후
intOZeroSV_Int
longOZeroSV_Int64
uintOZeroSV_UInt
ulongOZeroSV_UInt64
shortOZeroSV_Short
ushortOZeroSV_UShort
byteOZeroSV_Byte
floatOZeroSV_Float
doubleOZeroSV_Double
decimalOZeroSV_Decimal
boolOZeroSV_Bool
stringOZeroSV_String
Vector2OZeroSV_Vector2
Vector3OZeroSV_Vector3
byte[]OZeroSV_Buffer
Secure Types는 OZeroSDK.Security 네임스페이스를 사용합니다. 해당 타입을 선언하는 스크립트에 using 지시문을 추가하세요.

7 보안 이벤트 처리 (선택)

기본적으로 OZero는 위협을 감지하면 설정된 대응 정책에 따라 앱을 종료하거나 로그만 남깁니다. 직접 경고 화면을 표시하거나, 자체 서버 로그를 보내거나, 종료 직전 저장 처리를 해야 한다면 콜백을 등록할 수 있습니다. 콜백은 OZeroSecurityEvent를 통해 변조 타입, 중단 코드, 메시지 키, 상세 메시지, 종료 예정 여부를 함께 전달합니다.

using UnityEngine;
using OZeroSDK.Security;

public class SecurityHandler : MonoBehaviour
{
    void OnEnable()
    {
        // RegisterUserCallback runs on the user chain only.
        // The built-in default handler runs independently and cannot be silenced.
        OZeroSecurityManager.Instance.RegisterUserCallback(OnThreatDetected);
    }

    void OnDisable()
    {
        OZeroSecurityManager.Instance.UnregisterUserCallback(OnThreatDetected);
    }

    void OnThreatDetected(OZeroSecurityEvent evt)
    {
        // evt contains Type, AbortCode, AbortCodeHex, MessageKey, Message, and WillAbort.
        Debug.LogWarning(
            $"OZero threat: {evt.Type} {evt.AbortCodeHex} {evt.Message}");

        switch (evt.Type)
        {
            case ModulationType.SpeedHack:
                // e.g. kick the player, show warning, report to server
                break;

            case ModulationType.BuildIntegrity:
                // evt.WillAbort is usually true for fatal build integrity violations.
                break;

            case ModulationType.Injection:
                break;
        }

        if (evt.WillAbort)
        {
            // Last chance to flush your own analytics or save state.
        }
    }
}

OZeroSecurityEvent, ModulationType, OZeroAbortCode의 전체 목록은 API 레퍼런스에 문서화되어 있습니다.

evt.WillAbort가 true이면 현재 대응 정책상 콜백 이후 앱이 종료됩니다. 이때는 자체 analytics flush나 저장 처리만 짧게 수행하세요.
탐지 이벤트 전달 흐름

위협이 감지되면 OZero는 먼저 SDK 기본 대응 정책을 적용하고, 프로젝트에서 등록한 콜백과 인스펙터 이벤트에도 같은 보안 이벤트를 전달합니다. 직접 경고 화면을 띄우거나 서버 로그를 남기고 싶다면 콜백을 등록하세요. 앱 종료 여부는 Config Dashboard의 Response 설정과 evt.WillAbort 값으로 확인할 수 있습니다.

포털 정책 콜백

고객 포털 정책은 단계적으로 운영할 수 있습니다. Observe는 근거만 기록하고, Callback은 서버에서 발행한 정책 액션을 게임으로 전달하며, Device Block은 이후 서버 검증에서 선택한 기기를 차단합니다. 강한 기기 차단을 쓰기 전에 게임에서 경고 표시, 매치메이킹 제한, 재로그인 요청, 고객지원 안내를 직접 처리하려면 Callback을 사용하세요.

using UnityEngine;
using OZeroSDK.Security;

public class PolicyActionHandler : MonoBehaviour
{
    void OnEnable()
    {
        OZeroSecurityManager.Instance.RegisterUserPolicyActionCallback(OnPolicyAction);
    }

    void OnDisable()
    {
        OZeroSecurityManager.Instance.UnregisterUserPolicyActionCallback(OnPolicyAction);
    }

    void OnPolicyAction(OZeroPolicyActionEvent evt)
    {
        Debug.LogWarning(
            $"Policy callback: {evt.Module}, score={evt.Score}, reason={evt.Reason}");

        if (evt.Score >= 80)
        {
            // Show your own UI, limit sensitive actions, or ask the player to reconnect.
        }
    }
}

다음 단계

이제 OZero의 핵심 보호 기능이 실행 중입니다. API 레퍼런스에서 모든 클래스, 메서드, 설정 옵션을 자세히 확인해 보세요.