OZero Security
문서

시작하기

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

약 3분 · Unity 2022.3 LTS+ · iOS 14.0+ / Android API 24+

목적에 맞게 시작하세요

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

3분 빠른 시작 패키지를 임포트하고 대시보드를 열어 프리셋을 적용한 뒤 첫 빌드를 만듭니다. 출시 전 체크리스트 빌드 무결성, 플랫폼 설정, 진단 및 Release 구성을 점검합니다. 기능별 설정 가이드 보호 모듈, 공통 정책 및 선택형 Managed UI를 설정합니다. 문제 해결 QA 또는 고객 지원에서 지원 코드, 종료 코드, 1차 확인 표를 기준으로 원인을 좁힙니다. 라이선스·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가 스크립트를 다시 컴파일하므로 우측 하단 진행 표시줄이 사라질 때까지 기다린 다음 다음 단계로 진행하세요.

Windows: 1.2.0 → 1.2.1 업데이트 중 "Copying file failed / Access is denied"가 나올 때

1.2.0 프로젝트에 1.2.1을 가져올 때 Plugins/x86_64/OZeroSecurity64.dll에 대해 Copying file failed … Access is denied 팝업이 나오고, Unity를 다시 시작해도 반복될 수 있습니다. 에디터가 시작할 때 SDK 네이티브 라이브러리를 미리 불러와 Windows가 파일을 잠그기 때문입니다. macOS·Linux는 해당하지 않으며, 다음 업데이트에서 이 단계가 필요 없도록 고칠 예정입니다.

  1. 팝업에서 Cancel을 누릅니다(Force Quit 아님).
  2. Unity를 완전히 종료합니다. 작업 관리자에 Unity.exe가 남아 있지 않은지 확인하세요.
  3. 탐색기에서 Assets/OZeroSecurity/Plugins/x86_64/ 폴더의 OZeroSecurity64.dll과 OZeroPCStoreBridge64.dll만 삭제합니다. .meta 파일은 그대로 둡니다.
  4. Unity로 프로젝트를 엽니다. DLL 관련 콘솔 오류는 무시해도 됩니다.
  5. Package Manager에서 1.2.1 전체를 다시 Import합니다.
  6. Unity를 다시 시작한 뒤 Check Setup을 실행합니다.

Welcome 창

OZero SDK 패키지를 import하면 Welcome 창(Window > OZero Security > Welcome)이 자동으로 열립니다. OZero SDK 패키지를 import할 때마다(같은 버전을 다시 import해도) 열리고, 새 SDK 버전이 처음 로드될 때도 열립니다. Built-in TMP 대화상자 패키지나 TMP 리소스를 import할 때는 열리지 않습니다.

OZero Security Welcome window with Check Setup, Read the Manual and Security Config OZero Security Welcome window with Check Setup, Read the Manual, Security Config and Online Services

창 위쪽에는 STEP 1 · Run Setup 큰 카드가 있습니다. Start setup을 누르면 Check Setup이 열려 프로젝트에 아직 필요한 설정을 보여 주고, Auto Setup이 변경마다 확인을 받으며 적용합니다. 그 아래에는 설정을 마친 뒤 쓰는 카드 두 장이 있습니다. Read the Manual에는 Online(OZero 웹사이트 매뉴얼)과 Offline(패키지에 포함된 HTML 매뉴얼, Assets/OZeroSecurity/Documentation/HTML/index.html) 버튼이 있으며, 포함된 매뉴얼이 없으면 Offline 버튼은 비활성화됩니다. Security Config(Open config)는 Edit > Project Settings > OZero Security를 열어, 설정을 마친 뒤 프리셋이나 개별 보호 기능을 바꿀 수 있게 합니다.

애드온이 설치된 프로젝트에서는 Online Services 행이 추가로 보입니다. Open을 누르면 Window > OZero Security > Online Services 창을 엽니다(설정이 없으면 만듭니다).

애드온이 설치되어 있으면 Offline 버튼은 애드온에 포함된 매뉴얼(Assets/OZeroSecurity/Online/Documentation/HTML/index.html)을 엽니다.

Don't show this window automatically again을 켜면 이 프로젝트에서는 더 이상 자동으로 열리지 않으며, 메뉴로는 언제든 열 수 있습니다. batch 모드이거나 OZERO_SUPPRESS_WELCOME 환경 변수가 설정돼 있으면 열리지 않으므로 CI와 자동 빌드는 멈추지 않습니다.

보안 설정(ProjectSettings/OZeroSecuritySettings.asset, Standard 프리셋)과, 온라인 애드온을 쓰면 라이선스 설정(ProjectSettings/OZeroLicenseSettings.asset)도 import 뒤 Unity 에디터가 한가해질 때 자동으로 만들어집니다. -batchmode -quit로 패키지만 import하는 CI에서는 만들어지지 않으므로, 프로젝트를 에디터로 한 번 열거나 빌드 스크립트에서 OZeroSecurityProjectSettings.LoadOrCreate()(애드온은 OZeroLicenseProjectSettings.LoadOrCreate()도)를 호출하세요. ProjectSettings/OZeroSecuritySettings.asset와 ProjectSettings/OZeroLicenseSettings.asset는 버전 관리에 커밋하고, 사용자별 비밀값이 든 UserSettings/OZeroSecuritySecrets.json는 커밋하지 마세요.

2 대시보드 열기

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

Window -> OZero Security -> Config & Dashboard
OZero Security page in Edit > Project Settings (Config & Dashboard)

Config Dashboard가 열리면 이 화면에서 프리셋, 보안 모듈, 라이선스, 빌드 무결성 설정을 한 번에 관리할 수 있습니다. Project 창에서 파일을 직접 찾아 수정할 필요는 없습니다. 대시보드는 Edit > Project Settings의 OZero Security 페이지이며, 그곳에서 바로 열어도 됩니다.

Unity 에디터 메뉴 설명

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

자세히: 전체 메뉴 목록
메뉴 역할 사용 시점
Window > OZero Security > WelcomeSDK 시작 화면을 엽니다. Check Setup, 매뉴얼(온라인/오프라인), Security Config로 바로 이동할 수 있습니다.OZero SDK 패키지를 import한 뒤 자동으로 열립니다. 닫은 뒤에는 이 메뉴로 다시 엽니다.
Window > OZero Security > Config & Dashboard보안 설정을 편집하는 Edit > Project Settings > OZero Security를 엽니다. 프로젝트에 설정이 없으면 Standard 프리셋으로 만듭니다.프리셋 적용, 모듈 활성화, 대응 정책 설정, 빌드 무결성 설정 확인을 가장 먼저 여기서 진행합니다.
Window > OZero Security > Online ServicesOnline Services 설정(ProjectSettings/OZeroLicenseSettings.asset)을 별도 창으로 엽니다. 라이선스, 서버 연결, 온라인 서비스를 설정합니다. 같은 설정이 Security Config(각 모듈의 Pro - Online 영역과 Pro - License & Online Services 카드)에도 있습니다.Standard / Pro 라이선스 설정, 서버 엔드포인트, 텔레메트리 옵션을 입력할 때 사용합니다.
Window > OZero Security > Check Setup프로젝트와 릴리스 설정에서 자주 발생하는 문제를 에디터에서 진단합니다.패키지 임포트 직후, 릴리스 빌드 전, 또는 모듈 동작이 예상과 다를 때 실행합니다.
Window > OZero Security > macOS Distribution Signing모든 macOS 플레이어 빌드에 Developer ID 서명, 선택적 공증, OZero 무결성 재생성을 자동으로 적용하도록 설정합니다.Mac에서 만든 macOS 빌드를 배포하기 전에 켭니다. Windows에서 만든 빌드는 서명 스크립트를 씁니다(macOS 서명 참고).
Tools > OZero Security > Internal > Check Integrity ManifestOZero 무결성 매니페스트를 열고 디코딩된 내용을 확인합니다.빌드 무결성 문제를 조사하거나 고객지원 중 매니페스트 내용을 확인할 때 사용합니다.
Tools > OZero Security > Internal > Bake Security Config Blob보호된 보안 설정 blob을 StreamingAssets에 다시 생성합니다.고급 디버깅 또는 CI 흐름에서 사용합니다. 일반 플레이어 빌드에서는 자동으로 bake됩니다.
Tools > OZero Security > Internal > Bake Assembly Hash가장 최근 빌드 결과물 기준으로 oz_ahash.bin을 다시 생성합니다.전체 빌드 파이프라인을 다시 돌리지 않고 코드만 재빌드한 경우에만 사용합니다.
Tools > OZero Security > Re-bake Integrity After Code Signing (Windows or Linux player)...코드 서명한 Windows·Linux 플레이어의 OZero 무결성 파일을 다시 생성합니다.실행 파일이나 네이티브 모듈에 서명한 뒤 사용합니다(Windows / Linux 서명 참고).
Tools > OZero Security > Setup > Android > Keystore SHA ExtractorAndroid keystore에서 SHA-1, SHA-256 지문을 추출합니다.Build Integrity의 Android 서명 지문 값을 채울 때 사용합니다.
Tools > OZero Security > Internal > Add Debug SHA Key to ConfigAndroid debug keystore를 찾아 SHA-256 지문을 config에 추가합니다.로컬 Android 디버그 빌드에서만 사용합니다. 릴리스 빌드는 릴리스 서명 키 지문을 사용해야 합니다.
Tools > OZero Security > Setup > PC Store > Scan RedistributablePC 빌드 또는 플러그인 폴더에서 Steam redistributable 파일과 알려진 emulator artifact를 스캔합니다.Steam 릴리스 패키징 전이나 의심스러운 Steam 파일 포함 여부를 점검할 때 사용합니다.
Tools > OZero Security > Diagnostics > Check Time.timeScale UsageOZero 시간 보호와 충돌할 수 있는 직접 Time.timeScale 수정 코드를 스캔합니다.릴리스 전 또는 Speed & Time Hack 모듈이 프로젝트 측 time-scale 정책 문제를 보고할 때 실행합니다.

Check Setup과 Auto Setup

Window > OZero Security > Check Setup을 열거나 Welcome 창에서 Start setup을 누르세요. 패키지를 임포트한 직후와 출시 빌드 전에 한 번씩 실행하는 것을 권장합니다.

Check Setup은 프로젝트, 현재 빌드 대상, 보안 설정을 검사해 빌드를 막는 항목과 권장 항목을 보여 줍니다. 항목마다 Docs(설명)와 수정 버튼이 있으니, 빌드를 막는 항목부터 해결하세요.

Check Setup window with one blocking issue, two warnings and the Auto Setup banner

Auto Setup을 누르면 권장 변경을 한 단계씩 보여 줍니다. 단계마다 현재 값과 바뀔 값을 확인하고 Apply 또는 Skip을 고르세요. 끝나면 적용한 항목과 건너뛴 항목, 남은 항목이 요약으로 표시됩니다. 빌드를 막는 항목과 Auto Setup이 적용할 변경이 모두 없어지면 Setup complete 상자와 Open Build Settings 버튼이 나오며, 평소처럼 빌드하면 됩니다.

Auto Setup step 1 of 5 showing the change with Apply, Skip and Stop

Auto Setup이 바꾸는 항목: Development Build 끄기, IL2CPP 전환, Managed Stripping Low, Android API 24·ARM64, DeveloperSecret 생성, TMP 다이얼로그 패키지 임포트, manifest 서명 키 생성(Require Manifest Signature 켜기 포함), Android 디버그 SHA 등록.

서명 키 파일: Auto Setup이 만든 manifest 서명 개인 키 파일은 프로젝트와 버전 관리 밖에 두고, CI를 포함한 모든 빌드 머신에서 쓸 수 있게 하세요.
자세히: Auto Setup 단계와 동작

Check Setup(Window > OZero Security > Check Setup, 또는 Welcome 창의 Start setup)은 프로젝트, 활성 빌드 대상, 보안 설정, 플랫폼별 릴리스 설정을 점검해 차단 문제, 권장 사항, 정보를 보여 줍니다. 항목마다 Docs와 수정 버튼이 있고, 창 위쪽에 Auto Setup이 적용할 수 있는 권장 변경 수가 표시됩니다. 창 위쪽 버튼은 왼쪽부터 Auto Setup(적용할 변경 수 표시), Re-check(검사 다시 실행), Manual, Security Config입니다. Security Config는 Edit > Project Settings > OZero Security를 엽니다. 실제 플레이어 빌드 때도 빌드 검증은 그대로 실행됩니다.

OZero SDK 패키지를 import한 뒤 Check Setup을 처음 열 때, Auto Setup이 적용할 수 있는 변경이 있으면 Auto Setup을 시작할지 묻습니다(Start Auto Setup / Not now). 같은 버전을 다시 import해도 import할 때마다 다시 묻고, batch 모드에서는 묻지 않습니다. 나중에 Auto Setup 버튼으로 시작할 수도 있습니다.

OZero Auto Setup prompt with Start Auto Setup and Not now over the Check Setup window

Auto Setup은 먼저 적용할 단계 목록을 보여 줍니다. 프로젝트와 활성 빌드 대상에 필요한 단계만 나오며, Start(또는 Cancel)를 고릅니다. 단계마다 현재 값과 바뀔 값을 보여 준 뒤 Apply, Skip, Stop 중에서 고르게 합니다. 마지막 요약 화면에 적용한 항목, 건너뛴 항목, 아직 남은 항목이 나오며, Run Auto Setup Again으로 처음부터 다시 실행할 수 있습니다. 나올 수 있는 단계는 다음과 같습니다.

Auto Setup planned steps with Start and Cancel
  1. Development Build 끄기
  2. Scripting Backend를 IL2CPP로 변경
  3. IL2CPP일 때만: Managed Stripping Level을 Low로 설정
  4. Android: 최소 API 레벨 24, 대상 아키텍처 ARM64
  5. DeveloperSecret 생성
  6. Built-in TMP 대화상자 패키지 import. 필요하면 TextMeshPro/Unity UI를 설치하고 TMP Essential Resources와 대화상자 패키지를 차례로 import합니다. 모두 끝날 때까지 Processing 화면에 진행 단계와 경과 초가 나오며, 끝나면 다음 단계로 넘어갑니다. 스크립트 리로드를 거쳐도 이어집니다.
  7. 매니페스트 서명 키 쌍 생성(트러스트 앵커는 자동으로 갱신됩니다). 개인키 파일이 만들어질 위치는 단계 화면에 나옵니다. 결과 대화상자는 뜨지 않고, 개인키 위치와 지문은 콘솔 로그에 남습니다. 이미 키가 있으면 덮어쓸지 묻는 대화상자가 뜹니다. 다음 단계에서 Require Manifest Signature를 켭니다.
  8. Android 디버그 SHA 추가. 결과는 콘솔 로그에 남습니다. 디버그 keystore가 없으면 만들지 묻는 대화상자가 뜹니다. Security Config나 Keystore SHA Extractor의 버튼으로 직접 실행하면 예전처럼 결과 대화상자가 뜹니다.
Auto Setup Processing screen with the elapsed time Auto Setup summary with Applied, Skipped and Still open

보안 설정의 Fail Build on Setup Warnings가 켜져 있으면(기본값) 키 생성과 Require Manifest Signature도 일반 단계입니다. 건너뛰면 경고가 남아 플레이어 빌드가 preflight에서 실패하며, 단계 화면과 요약 화면에 이 안내가 나옵니다. 이 설정이 꺼져 있을 때만 "(optional, review first)" 표시가 붙은 선택 단계가 됩니다. 키 생성 단계는 개인키 파일이 만들어질 위치를 보여 줍니다. 개인키는 프로젝트 밖에 두고 버전 관리에서 빼며, CI를 포함한 모든 빌드 머신에서 쓸 수 있게 하세요.

이 에디터에서 적용할 수 없는 단계(예: Unity 에디터에 IL2CPP 모듈이 설치돼 있지 않을 때의 IL2CPP 단계)는 단계별로 진행하지 않고, 개요와 요약 화면의 Needs your action에 따로 표시됩니다. IL2CPP 모듈이 없으면 Unity Hub > Installs > Add modules에서 해당 플랫폼의 IL2CPP 모듈을 추가한 뒤 Auto Setup을 다시 실행하세요. Player Settings 변경은 Undo로 되돌릴 수 없으며, 적용한 변경은 모두 요약 화면에 표시됩니다.

다음 두 Check Setup 오류는 OZero의 네이티브 로드 앵커를 지킵니다(클라이언트 보호의 한계). OZero native plugins are not set to Load on startup: Auto Setup이 OZero 네이티브 플러그인의 Load on startup을 다시 켭니다. 이 설정은 끄지 마세요. Android boot provider library is missing: Mono 백엔드 Android 빌드에 필요한 라이브러리입니다. OZero Security 패키지를 다시 임포트해 Assets/OZeroSecurity/Plugins/Android/OZeroSecurityBoot.androidlib를 복구하세요.

온라인 애드온이 있으면 Auto Setup이 고객만 알 수 있는 값도 입력받아 설정합니다. 이런 단계에는 입력 칸이 나오며, 값을 입력하고 Apply를 누르면 됩니다. 값은 저장하기 전에 확인하며 Console이나 로그에 남기지 않습니다. 확인에 실패하면 단계에 이유가 표시되고 아무것도 저장하지 않으니, 값을 고쳐 다시 입력하거나 Skip을 누르세요.

  • License Key: Pro license key is empty, Native Variant <NNNN> needs your license key, … is bound to a different license key 항목에 나옵니다. 프로젝트에 라이선스 전용 Native Variant가 있으면 저장하기 전에 그 Variant의 라이선스와 대조하며, 다른 라이선스의 키는 저장하지 않습니다.
  • Server Public Key: Pro server signing public key is not configured 항목에 나옵니다. Customer Portal > Server Key에서 서명 공개키(16진수 64자)를 복사해 넣으세요. 다른 형식은 거부되며, 소문자로 저장됩니다.
  • Application Identifier: Application identifier is not registered for Native Variant <NNNN> 항목에 나옵니다. 입력할 값은 활성 빌드 타깃에 따라 다르며 Customer Portal > Platform settings와 같습니다. Android는 Package Name, iOS는 Bundle Identifier를 입력하며, 입력 칸에는 현재 식별자가 미리 채워져 있습니다. 이 설정이 꺼져 있으면 Unity가 입력한 식별자를 무시하므로, Apply는 Player Settings의 Override Default Package Name / Bundle Identifier도 켭니다. Windows, macOS, Linux는 Customer Portal > Platform settings에 이 플랫폼용으로 등록한 Company Name과 Product Name(포털에는 Company / Product로 표시)을 각각의 칸에 입력합니다. 두 칸에는 현재 값이 미리 채워져 있으며, company.product 또는 com.company.product로 대조합니다. Apply는 Player Settings의 Company Name과 Product Name을 설정하며, Product Name은 빌드된 앱 이름이기도 합니다. WebGL은 포털에 등록한 페이지 origin(도메인)으로 식별하므로 프로젝트에서 바꿀 것이 없고, 이 단계는 사용할 수 없음으로 표시됩니다. 값은 라이선스에 등록된 것이어야 하며, 등록되지 않았으면 Customer Portal에서 등록한 뒤 패키지를 다시 받으라는 안내가 나옵니다.

에디터 Play Mode (개발 단계)

에디터 Play Mode에는 별도 보호 설정이 있어 개발을 방해하지 않습니다. Window > OZero Security > Config & Dashboard > Editor Play Mode > Protection에서 Off 또는 Test를 고르세요.

Protection 에디터 Play Mode 동작 사용 시점
Off아무것도 중단하지 않고, 탐지는 로그로만 남깁니다.기본값. 평소 개발할 때.
Test대응·안내 UI·콜백을 에디터에서 실행하고, 종료 대신 Play만 멈춥니다.Receiver와 안내 UI를 시험할 때.

에디터는 출시 환경이 아니므로 최종 동작은 반드시 플레이어 빌드에서 확인하세요.

자세히: 모드별 동작과 로그

에디터 Play Mode에는 별도의 보호 설정이 있어서 OZero가 개발을 방해하지 않습니다. Window > OZero Security > Config & Dashboard > Editor Play Mode > Protection에서 Off 또는 Test를 고르세요. 설정은 ProjectSettings/OZeroEditorPlayMode.json에 저장되므로 버전 관리로 팀과 공유됩니다. 보안 설정과 분리돼 있어 플레이어 빌드와 보안 설정 해시에는 영향이 없습니다. 바꾼 값은 다음 Play부터 적용되고, Play를 시작할 때 Console에 현재 모드가 한 줄로 기록됩니다([OZero] Editor Play Mode Protection: Off - ...).

Off: 탐지기(속도, 인젝션, 무결성 등)를 켜지 않습니다. 직접 붙인 PhysicsHack 컴포넌트나 SecureVariable 변조가 탐지되면 유형별로 한 번만 경고 로그([OZero] <type> detected in Editor Play Mode - no response because Editor Play Mode Protection is Off)를 남기고 그 밖의 일은 하지 않습니다. 콜백, 안내 UI, 텔레메트리가 없고 Play도 계속됩니다. SecureVariable 암호화 자체는 평소대로 동작합니다. 보안 레벨 거부(예: 보안 설정 해시 허용 목록)도 에디터 Play Mode에서는 로그로만 남습니다.

Test: 빌드 없이 현재 설정으로 탐지기를 켭니다. 이전 빌드에서 남은 StreamingAssets/oz_cfg.bin은 무시합니다. 탐지되면 설정된 대응(콜백, 안내 UI, 텔레메트리)을 실행합니다. 플레이어라면 종료할 상황에서는 같은 유예 시간 뒤 Play Mode만 멈추고 Editor Play Mode will stop in N ms 로그를 남깁니다. 보안 레벨 거부도 설정된 대응(Fail On Security Level Reject)을 실행합니다.

두 모드 모두 SDK가 Unity 에디터 프로세스를 종료하지 않으며, 네이티브 부팅 검사(디버거, 에뮬레이터, Linux 호스트 계층)는 에디터에서 항상 꺼져 있습니다. 에디터는 출시 환경이 아니므로 최종 동작은 항상 플레이어 빌드에서 확인하세요.

온라인 애드온을 쓰면 Pro 활성화와 온라인 기능은 에디터에서도 이전과 같이 동작합니다. 애드온의 서버 루프는 Play Mode를 끝내거나 스크립트를 다시 불러올 때 멈춥니다.

3 모듈 활성화

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

OZero Security page in Edit > Project Settings (Config & Dashboard)

보안 프리셋 선택

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

프리셋 권장 사용처 적용 정책 요약 Pro 서버 검증 (Check Integrity With Server)
Low 프로토타입, 개발 빌드, 초기 QA 핵심 검사만 (테스트용) 끔. 오프라인 실행이 필요한 게임용(서버 빌드 판정과 실행 중 재확인 없음)
Standard 대부분의 출시 게임 핵심 보호 + 시작·런타임 검증 (권장) 켬. OZeroManaged 판정, 시작할 때 온라인 필요
Strict 고위험 라이브 서비스, PvP, 경쟁형 빌드 최대 범위, 충분히 테스트한 뒤 적용 켬. CustomerGameServer 판정. 게임 서버가 challenge를 주면 클라이언트가 OZeroOnlineAttestation.RequestGameServerAttestation을 호출하고, 게임 서버가 /v1/validate로 검증
모듈 기능 설명 권장 여부
Build Integrity Validator 앱 바이너리 변조 여부 탐지 권장
Speed Hack Detector 시간 조작 치트 탐지 권장
Injection Detector 메모리 후킹 툴 모니터링 권장
Install Source Validator 불법 APK 차단 (Android 전용) 선택
씬에 오브젝트를 따로 둘 필요가 없습니다. 원하는 모듈을 켜기만 하면 설정이 Project Settings에 자동 저장되고, 다음 실행부터 모듈이 자동으로 시작됩니다.
자세히: 프리셋이 바꾸는 값

Quick Preset 패널에는 현재 설정과 일치하는 프리셋이 Current: Standard처럼 표시되고, 해당 버튼이 굵은 글씨와 *로 표시됩니다. 적용 후 값을 직접 바꾸면 Current: Custom으로 표시되고 어느 버튼에도 표시가 붙지 않습니다. 권장값은 Standard이며 버튼에 Standard (Recommended)로 표시됩니다. 프리셋 이름은 Low / Standard / Strict 그대로입니다. 각 버튼 아래에 프리셋 설명이 나오고, 버튼의 툴팁에도 같은 설명이 보입니다.

Security Config Quick Preset showing Current: Standard with the Standard button marked

Windows 시스템 모듈 검사(enableWindowsSystemModuleScan): Low OFF / Standard·Strict ON. 라이선스 등급이 아닌 보안 프리셋 기준입니다. 호환성과 관측 정책은 Injection 설정을 확인하세요.

설정 다운로드는 enableRemoteSpeedHackConfig, enableRemoteInjectionConfig, enableRemotePhysicsConfig로 제어합니다. 보안 프리셋은 프로젝트의 라이선스 등급(Online Services 설정)이 Pro일 때만 이 옵션을 켜고, Standard이거나 라이선스 설정이 없으면 Pro 전용 서버 옵션을 모두 끈 상태로 둡니다(등급을 Pro로 바꾼 뒤에는 프리셋을 다시 적용하세요). 이 옵션은 Pro에서만 동작합니다. 원격 설정 옵션 자체는 BIV 서버 검증·설치 출처 동기화·서버 화이트리스트·텔레메트리를 켜지 않으며, 프리셋은 이와 별도로 이 값들을 바꿉니다(위 프리셋 표 참고).

  • Low: 가벼운 핵심 검사만 유지합니다. 테스트 환경이 너무 일찍 차단되지 않도록 플랫폼 네이티브 검사와 강제 종료 정책을 완화합니다.
  • Standard: 핵심 보호 세트, 시작 시 검증, 런타임 재검증, 에뮬레이터 검사, 권장 IL2CPP 파일 커버리지를 켭니다. 호환성과 보호 수준의 균형을 맞춘 구성입니다.
  • Strict: 가장 넓은 커버리지를 적용하고 더 많은 실패를 치명적 위반으로 처리합니다. 플랫폼, 서명, 스토어 배포 흐름을 충분히 테스트한 뒤 적용하세요.

라이선스 모델 — Standard / Pro

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

아래 매트릭스는 개발자 관점에서 SDK가 런타임에 티어별로 실제 어떻게 동작하는지를 정리한 것입니다. 전체 기능 비교는 홈페이지의 라이선스 모드 비교표에서 확인하세요.
항목 Standard Pro
라이선스 키 — (없음) OZ-PRO-XXXX ×6
10개 보호 모듈 10개 보호 모듈 전체 활성화 10개 전체 (Standard와 동일)
고객별 독립 네이티브 보안 모듈 미제공 제공
클라우드 텔레메트리 없음 활성화
원격 설정 없음 있음
빌드 무결성 서버 검증 없음 있음
서명된 시간 (시계 조작 방지) 꺼짐 — WebTime은 HTTPS HEAD만 사용 켜짐 — 서명된 /v1/time 응답 사용
Standard에서 Pro로 확장할 때 게임플레이 코드를 유지할 수 있습니다. 독립적인 네이티브 보안 모듈을 적용하고 Online Services 설정에서 Pro 서버 기능을 설정하세요.
앱 개인정보처리방침에 넣을 문구

Pro 서버 기능을 켠 앱은 OZero SDK가 처리하는 정보를 최종 이용자에게 알려야 합니다. 아래 문구를 앱 개인정보처리방침에 붙여 넣고, 대괄호 부분은 켠 기능에 맞게 남기거나 지우세요. 전체 항목은 OZeroSecurity 개인정보처리방침을 참고하세요.

한국어

이 앱은 부정 행위와 변조를 방지하기 위해 OZero Security SDK(오제로시큐리티 주식회사)를 사용합니다. 보안 검사는 기기 안에서 이루어지며, [Pro 서버 기능이 켜진 경우] 보안 이벤트가 발생하면 다음 정보가 OZero 서버로 전송됩니다: 앱별로 해시된 기기 식별자, 플랫폼·운영체제, 앱 버전, 탐지된 보안 이벤트의 종류와 결과, 탐지에 관련된 외부 프로그램 모듈의 파일명·서명자·해시, 접속 국가. [물리 검증을 켠 경우: 캐릭터 위치·속도·씬 이름.] [Steam 확인을 켠 경우: Steam 인증 티켓과 SteamID(해시로 저장).] 기기의 원문 식별자, IP 주소, 채팅·닉네임·결제 정보는 전송되지 않습니다. 전송된 기록은 90일 후 삭제되며, 외부 프로그램 모듈 정보는 개인·기기와 연결되지 않는 형태로 OZero의 부정 프로그램 판별 기준을 개선하는 데 사용됩니다. 자세한 내용: https://www.ozerosecurity.com/privacy.html

English

This app uses the OZero Security SDK (OZero Security Inc.) to prevent cheating and tampering. Security checks run on the device. [When Pro server features are enabled] the following is sent to OZero servers when a security event occurs: an app-specific hashed device identifier, platform and OS, app version, the type and result of the security event, the file name, signer and hash of third-party modules involved in the detection, and the country of connection. [Physics validation: character position, velocity and scene name.] [Steam verification: the Steam auth ticket and SteamID, stored hashed.] The raw device identifier, IP address, chat, nicknames and payment data are not sent. Records are deleted after 90 days. Module information is used, in a form not linked to any person or device, to improve OZero's cheat-program classification. Details: https://www.ozerosecurity.com/privacy.html

日本語

本アプリは不正行為と改ざんを防ぐために OZero Security SDK(OZero Security Inc.)を使用しています。セキュリティ検査は端末内で行われ、[Pro サーバー機能が有効な場合] セキュリティイベント発生時に次の情報が OZero サーバーへ送信されます:アプリごとにハッシュ化した端末識別子、プラットフォーム・OS、アプリのバージョン、検知したイベントの種類と結果、検知に関係した外部モジュールのファイル名・署名者・ハッシュ、接続国。[物理検証:キャラクターの位置・速度・シーン名。][Steam 確認:Steam 認証チケットと SteamID(ハッシュで保存)。] 端末の生の識別子、IP アドレス、チャット・ニックネーム・決済情報は送信されません。記録は 90 日後に削除され、モジュール情報は個人や端末と結び付かない形で OZero の不正プログラム判定基準の改善に使われます。詳細:https://www.ozerosecurity.com/privacy.html

中文(简体)

本应用使用 OZero Security SDK(OZero Security Inc.)防止作弊与篡改。安全检查在设备内进行,[启用 Pro 服务器功能时] 发生安全事件时会向 OZero 服务器发送以下信息:按应用哈希处理的设备标识符、平台与操作系统、应用版本、安全事件的类型与结果、与检测相关的第三方模块的文件名·签名者·哈希、连接国家。[物理验证:角色位置、速度与场景名。][Steam 验证:Steam 认证票据与 SteamID(以哈希保存)。] 不会发送设备原始标识符、IP 地址、聊天、昵称与支付信息。记录在 90 天后删除;模块信息以不关联个人或设备的形式用于改进 OZero 的作弊程序判定标准。详情:https://www.ozerosecurity.com/privacy.html

Online Services 설정

Security Config는 애드온 설정을 두 곳에 보여 줍니다. Protection Modules 그룹 끝, 즉 페이지 맨 아래(Steam Anti-Piracy (PC Only) 다음)의 Pro - License & Online Services 카드는 License(티어, 라이선스 키)로 시작합니다. Tier가 Pro이면 Server Connection, Offline & Device Policy, Security Level Enforcement(Security Config Hash (build allowlist) 목록 포함), Telemetry, Online Verification UI, Validate License Now도 표시되고, 각 탐지 모듈 섹션(Build Integrity, Install Source (Android Only), Device Binding, Speed & Time Hack, Injection & Hooking, Steam Anti-Piracy (PC Only))에 그 모듈의 온라인 설정을 담은 Pro - Online 영역이 생깁니다. 모듈의 Activate가 꺼져 있으면 이 영역은 비활성입니다.

Pro - License & Online Services card at the bottom of the OZero Security page in Project Settings

값은 ProjectSettings/OZeroLicenseSettings.asset에 저장되며, 이 파일을 버전 관리에 커밋합니다. Project 창에는 설정 에셋이 없습니다. 같은 설정을 독립 화면에서도 편집할 수 있습니다. Security Config와 Welcome 창의 Online Services 버튼은 Window > OZero Security > Online Services 창을 열고(설정이 없으면 만들고), 이 창에는 Select Settings Asset과 Validate License Now 버튼이 있습니다. 창 위쪽의 Security Config와 Check Setup 버튼으로 해당 화면으로 이동할 수 있습니다. 이 절의 나머지는 독립 화면 기준이며, 항목 이름은 Security Config에서도 같습니다.

티어는 License 섹션에서 고릅니다. License Key 칸은 두 티어 모두에 표시됩니다. Standard를 고르면 License 섹션만 보이고, Standard는 서버 없이 동작하므로 온라인 서비스가 꺼져 있고 OZero 서버로 아무것도 보내지 않는다는 안내가 나옵니다. License Key는 Native Variant가 포함된 패키지를 쓸 때만 입력하며, 로컬에서만 확인합니다. Pro를 고르면 서버 연결과 온라인 서비스 설정이 나타납니다.

설정은 Security Config처럼 구성되며, 순서는 Quick Preset, License, Server Connection, Offline & Device Policy, Security Level Enforcement(헤더의 Enable로 켬), 모듈 섹션 Build Integrity, Install Source, Device Binding, Speed & Time Hack, Injection & Hooking, Steam Anti-Piracy, 그리고 Telemetry, Online Verification UI입니다.

프리셋은 SDK 전체에서 하나입니다. 여기의 Low / Standard / Strict 버튼은 Security Config와 같은 프리셋을 적용하므로 어느 쪽에서 적용해도 결과가 같습니다. 여기서도 Standard가 권장값이며 Standard (Recommended)로 표시되고, 각 버튼 아래에 설명이 나옵니다. 프리셋을 적용하면 탐지 모듈, 온라인 서비스 기본값, Declared Security Level이 함께 바뀌고 라이선스·서버 URL·키는 유지됩니다. 패널에는 Current: Standard 같은 현재 상태가 표시되고 해당 버튼이 굵은 글씨와 *로 표시되며, 적용 후 온라인 설정을 직접 바꾸면 Current: Custom이 되고 어느 버튼에도 표시가 붙지 않습니다. Declared Security Level이 적용한 프리셋과 다르면 Security Level Enforcement 섹션에 경고가 나옵니다.

모듈 섹션은 Security Config와 이름·순서가 같습니다. 헤더에는 Security Config의 모듈 상태(Active / Inactive, 읽기 전용)가 보이고, 섹션 맨 위에 온라인 서비스가 그 모듈에 더하는 것을 한 줄로 설명합니다. 서버 스위치는 섹션의 첫 줄에 있습니다.

Security Config에서는 모듈이 꺼져 있으면 그 모듈의 Pro - Online 영역이 Activate this module to use its online settings. 안내와 함께 비활성으로 표시됩니다. 독립 화면에서는 비활성 모듈의 섹션에 모듈을 켜기 전까지 이 온라인 설정은 효과가 없다는 안내와 Open Security Config 버튼이 나옵니다.

Telemetry와 Online Verification UI는 온라인 전용입니다. Telemetry는 Customer Portal로 보내는 보안 이벤트와 원격 물리 설정을 다룹니다. Online Verification UI는 서버 검증 중에 나오는 화면(검증 중, 재시도, 온라인 필요, 시간 초과)의 동작을 정합니다. 설정 항목은 UI 정책, 재시도·온라인 필요 시간, 시간 초과 동작, 네트워크 복구 시 자동 재시도입니다. 대화상자 디자인과 언어는 Security Config > Player Notice (Managed UI)를 따릅니다. Check Integrity With Server와 Check Steam DRM With Server가 모두 꺼져 있으면 사용되지 않는다는 안내가 나옵니다.

애드온에는 별도 로그 스위치가 없으며, 로그는 Security Config의 Enable Debug Logs를 따릅니다.

Pro 패키지와 Native Variant

Pro 고객은 Customer Portal 다운로드에서 Pro 통합 패키지를 받습니다. 라이선스용으로 만든 .unitypackage 하나에 Standard SDK, 온라인 애드온, 모든 플랫폼의 Native Variant가 들어 있습니다. 카드에는 두 가지 옵션이 있습니다.

Variant를 교체(회전)할 때: 교체해도 이전 Variant로 만든 기존 빌드는 계속 동작합니다. 새 Variant는 교체 후 새로 다운로드한 패키지로 만든 빌드부터 적용됩니다. 교체하면 회전 횟수를 하나 사용하며, 교체 후에는 환불을 받을 수 없습니다.

Native Variant가 포함된 패키지를 Tier Standard로 쓰려면 Tier는 Standard 그대로 두고 Security Config > Pro - License & Online Services > License에 License Key만 입력하면 됩니다. 빌드와 플레이어는 키를 로컬에서만 확인하며, Tier Standard는 OZero 서버와 통신하지 않습니다. 이 동작은 기본으로 켜져 있는 Allow Standard Build With Premium License를 따릅니다. 꺼져 있으면 Check Setup이 이 옵션을 켜거나 Tier를 Pro로 바꾸라고 안내합니다.

라이선스 키 없이 빌드하려면 Standard variant(0000)가 포함된 패키지를 받으세요.

Unity Asset Store에서 SDK를 구매했다면 SDK는 Asset Store에서 설치하고, Customer Portal에서는 Pro 확장 패키지만 받습니다. 카드에서 고른 SDK 버전에 맞춘 온라인 애드온과 배정된 Native Variant가 들어 있으며, SDK 자체는 들어 있지 않습니다.

Check Setup의 Native Variant 항목

패키지에 Native Variant가 들어 있으면 Check Setup이 빌드 전에 프로젝트가 맞는지 알려 줍니다. 플레이어 빌드도 같은 이유로 중단됩니다.

Check Setup 항목 해결 방법
Native Variant <NNNN> needs your license keySecurity Config > Pro - License & Online Services > License에 License Key를 입력합니다(Tier는 Standard 그대로 둬도 됩니다). 또는 Standard variant(0000)가 포함된 패키지를 받습니다. Check Setup > Auto Setup에서 입력할 수도 있습니다.
Native Variant <NNNN> is bound to a different license key패키지를 받은 라이선스의 키를 입력하거나, 이 라이선스로 로그인한 상태에서 패키지를 다시 받습니다. Check Setup > Auto Setup에서 입력할 수도 있습니다.
Application identifier is not registered for Native Variant <NNNN>빌드 타깃에 맞게 Customer Portal > Platform settings에 등록된 값으로 맞춥니다. Android / iOS: Player Settings > Other Settings > Override Default Package Name / Bundle Identifier를 켜고 등록된 Package Name / Bundle Identifier를 입력합니다. Windows / macOS / Linux: Company Name과 Product Name을 등록된 값으로 설정합니다. WebGL: 등록된 페이지 origin으로 식별하므로 프로젝트에서 바꿀 것이 없습니다. 또는 Customer Portal에서 라이선스에 식별자를 등록한 뒤 패키지를 다시 받습니다. Check Setup > Auto Setup에서 입력할 수도 있습니다(설정하기 전에 Native Variant와 대조합니다).
Native Variant <NNNN> is license-bound, but Tier is StandardAllow Standard Build With Premium License를 켜거나 Tier를 Pro로 바꿉니다.
Native Variant <NNNN> matches this project할 일이 없습니다. Tier Standard에서는 키를 로컬에서만 확인합니다.
Native Variant: Standard public (0000)할 일이 없습니다. Standard variant(0000)는 라이선스 키가 필요 없습니다.

게임 서버 연동: 기기 ID와 사용자 UUID

Pro에서는 게임 서버가 OZero가 쓰는 ID와 함께 동작할 수 있습니다. OZero 기기 ID를 읽어 플레이어 계정과 함께 저장하고, 게임의 사용자 UUID(계정 ID)를 연결하면 Customer Portal에서 그 사용자가 쓴 기기를 찾고 사용자를 차단할 수 있습니다.

기기 ID 읽기

OZeroLicenseRuntime.CurrentDeviceId를 읽으세요. 기본 설정에서는 Customer Portal과 텔레메트리에 보이는 값과 같습니다. 계정을 연결하려고 DeviceIdProvider를 바꾸지 마세요. 서버가 아는 기기 ID가 바뀝니다. 대신 아래의 사용자 UUID API를 쓰면 기기 ID는 그대로 유지됩니다.

사용자 UUID 연결

사용자 차단

Customer Portal에서 사용자 차단 규칙은 두 가지 동작 중 하나를 가집니다.

사용자 차단과 기기 차단은 OZeroLicenseRuntime.LastServerBlockCode로 구분합니다. 사용자는 "USER_BLOCKED", 기기는 "DEVICE_BLOCKED"입니다. 판정은 SetExternalUserId 호출 뒤 약 5초 안에 실행되며(기기 정책 heartbeat가 꺼져 있어도), 최소 30초 간격입니다. 네트워크가 실패하면 세션을 끝내지 않고 다음 확인에서 판정합니다.

Customer Portal: 사용자 UUID 페이지

Customer Portal에서 운영 정보 > 사용자 UUID를 엽니다(Pro). 페이지에는 탭이 세 개 있습니다.

salt 재발급(설정 탭)을 하면 기존 연결이 끊깁니다. 이전 salt로 만든 사용자 연결과 차단 규칙은 더 이상 사용자와 일치하지 않고, 기존 규칙은 옛 키로 남습니다. 게임은 다음 활성화 때 새 salt를 받으며, 고객 서버도 새 salt로 h1을 다시 계산해야 합니다.

차단 규칙 저장·해제와 salt 재발급은 운영 이력의 사용자 UUID 분류에 기록됩니다.

Customer Portal은 단일 페이지 앱입니다. 업데이트 뒤 화면이 예전 그대로이거나 이상하면 페이지를 새로고침하세요.

설정 탭과 같은 예제입니다. API 레퍼런스(OZeroLicenseRuntime)의 테스트 벡터로 구현을 확인하세요.

// C#
using System.Security.Cryptography;
using System.Text;

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();
}

// Node.js
const crypto = require("crypto");

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");
}

# Python
import hashlib

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()

예제: 로그인, 로그아웃, policy 콜백

using OZeroSDK.Security;
using OZeroSDK.Security.License;
using UnityEngine;

public sealed class OZeroAccountLink : MonoBehaviour
{
    private void OnEnable()
        => OZeroSecurityManager.Instance.RegisterUserPolicyActionCallback(OnPolicyAction);

    private void OnDisable()
        => OZeroSecurityManager.Instance.UnregisterUserPolicyActionCallback(OnPolicyAction);

    // Call after your sign-in succeeds.
    public void OnSignedIn(string accountUuid)
    {
        if (!OZeroLicenseRuntime.SetExternalUserId(accountUuid))
            Debug.LogWarning("OZero: user id rejected (empty, too long, or control characters).");

        // Optional: keep the OZero device id with the account on your server.
        string deviceId = OZeroLicenseRuntime.CurrentDeviceId;
        MyGameServer.ReportDevice(accountUuid, deviceId);
    }

    // Call on sign-out.
    public void OnSignedOut() => OZeroLicenseRuntime.ClearExternalUserId();

    private void OnPolicyAction(OZeroPolicyActionEvent evt)
    {
        if (evt.Module != "user_policy")
            return;

        if (evt.ActionId == "user_blocked")
        {
            // Terminate rule: the SDK shows the termination notice and ends the session next.
            MyGame.SaveQuickly();
            return;
        }

        // Callback-only rule: the game decides what to do.
        Debug.LogWarning($"User block rule {evt.PolicyId}: {evt.Reason}");
        MyGame.RestrictAccount(evt.Reason);
    }
}

개인정보와 스토어 신고

보안 설정 해시 허용 목록

빌드마다 보안 설정(OZeroSecurityConfig, 온라인 애드온 설정 포함)을 봉인하고 그 SHA-256 해시를 계산합니다. 배포하는 빌드의 해시를 승인하면 서버의 보안 레벨 확인은 그 빌드만 통과시킵니다. 따라서 설정을 낮춘 변조 빌드가 높은 보안 레벨을 선언해도 통과할 수 없습니다.

Pro 전용입니다. Security Config > Pro - License & Online Services > Security Level Enforcement가 켜져 있어야(헤더의 Enable, enableSecurityLevelCheck) 동작합니다. 꺼져 있으면 빌드가 해시를 보내지 않아 허용 목록이 효과가 없으며, 이 섹션에는 해시 목록 대신 경고 하나만 표시됩니다.

빌드 해시 확인

보안 설정을 하나라도 바꾸면(온라인 애드온 설정 포함) 해시가 바뀝니다. 릴리스 빌드마다 새 해시를 다시 승인하세요. 적용 모드에서는 승인되지 않은 해시의 빌드가 거부됩니다.

Customer Portal에서 해시 승인

정책 설정 > 보안 설정 해시를 엽니다(Pro).

승인과 해제는 운영 이력에 기록됩니다. CI에서 해시를 등록하려면 API 레퍼런스의 보안 설정 해시(CI)를 보세요.

Pro 라이선스 키 등록

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

1. 설정 에셋 생성

온라인 애드온은 import 뒤 에디터가 한가해질 때 라이선스 설정을 ProjectSettings/OZeroLicenseSettings.asset에 자동으로 만듭니다(옛 OZeroLicenseConfig 에셋이 Assets/ 아래 남아 있으면 만들지 않습니다). 라이선스는 Security Config 맨 아래 Pro - License & Online Services 카드에서 입력합니다. License에서 티어를 고르고 라이선스 키를 입력하며(Standard에서는 Native Variant 패키지를 쓸 때만), 서버 URL과 키는 Server Connection에 있습니다. Security Config나 Welcome 창의 Online Services를 누르면 같은 설정이 있는 Window > OZero Security > Online Services 창이 열리고, 설정이 없으면 만들어 줍니다. ProjectSettings/OZeroLicenseSettings.asset는 버전 관리에 커밋하세요.

라이선스 설정은 Project 창의 에셋이 아니라 ProjectSettings/OZeroLicenseSettings.asset에 저장됩니다. Online Services 버튼(Security Config 또는 Welcome 창)이나 Window > OZero Security > Online Services에서 여세요.

2. Inspector 필드 채우기

필드 필수 여부 설명
tier 모든 티어 이 빌드에 적용할 라이선스 등급을 선택합니다. Standard는 공통 네이티브 모듈만 사용하고 서버와 통신하지 않습니다. Pro는 프로젝트 전용 Native Variant와 manifest 검증에 더해 서버 활성화, 텔레메트리, 원격 정책 같은 운영 기능을 사용합니다.
licenseKey Pro 프로젝트에 발급된 OZ-PRO-... 형식의 라이선스 키입니다. Native Variant의 프로젝트 바인딩을 확인하고 런타임 활성화와 Pro 서버 기능을 사용합니다.
appIdentifier 자동 전송 Pro 활성화 요청을 보낼 때 SDK가 Unity의 Application.identifier 값을 함께 전송합니다. 고객 포털에 등록된 Bundle ID 또는 Package Name과 이 값이 다르면 활성화가 거부될 수 있으므로, Unity Player Settings의 Identifier를 먼저 맞춰 주세요.
serverBaseUrl Pro 런타임 Pro 기능이 OZero 서버와 통신할 때 사용하는 기본 주소입니다. 활성화, 텔레메트리, signed time, attestation, 서버 정책 확인에 사용됩니다. Standard 런타임은 이 주소를 호출하지 않습니다. OZero 지원팀이 별도 주소를 안내하지 않았다면 기본값 https://api.ozerosecurity.com을 그대로 두세요.
serverPublicKeyHex Pro 런타임 고객 포털 > Server Key 메뉴에 표시되는 서명용 공개키입니다. Pro 런타임은 이 값으로 activation, signed time, attestation, offline policy 응답이 OZero 서버에서 온 것인지 확인합니다. Standard 런타임은 이 값을 사용하지 않습니다. Pro Variant manifest는 SDK에 내장된 OZero Variant signing key로 따로 검증됩니다.
previousServerPublicKeyHex Pro 선택 Pro 서버 서명 키를 교체하는 짧은 기간에만 사용하는 이전 공개키입니다. OZero 지원팀이 키 교체를 안내한 경우에만 채우고, 평소에는 비워 두세요. Standard 런타임은 이 값을 사용하지 않습니다.
offlineProPolicyMode Pro 기기가 오프라인일 때 로컬 Build Integrity가 서명된 포털 차단 정책을 어떻게 사용할지 정하는 Pro 옵션입니다. ApplyCachedBlockPolicies는 유효한 캐시 정책이 있을 때만 적용하는 fail-open 기본값이며 대부분의 라이브 게임에 권장합니다. RequireFreshPolicy는 사용할 수 있는 서명 정책이 없고 서버 Integrity가 켜져 있을 때 fail-closed로 처리합니다. IgnoreCachedBlockPolicies는 테스트나 마이그레이션을 위해 캐시 정책을 우회합니다. 값별 의미는 OZeroOfflineProPolicyMode에서 확인할 수 있습니다.
activationTimeoutSeconds Pro 런타임 SDK가 Pro 활성화 응답을 기다리는 시간이며 기본값은 15초입니다. 서버가 응답하지 않으면 저장된 Pro 정보가 유효하고 포털 정책에서 오프라인 사용을 허용할 때만 해당 정보를 사용합니다. 온라인 확인이 필요한 정책에서는 서버에 연결될 때까지 앱 시작을 중단합니다.
enableLog 선택 인스펙터에 더 이상 표시되지 않습니다. 라이선스 캐시 사용, 활성화 성공, 타임아웃, 서명 불일치 같은 흐름 로그는 Security Config의 Enable Debug Logs를 따릅니다.
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. Check Integrity With Server 확인

빌드하기 전에 Security Config > Build Integrity > Pro - Online > Check Integrity With Server가 원하는 값인지 확인하세요. Standard·Strict 프리셋은 켜고 Low 프리셋은 끄며, 새로 만든 Online 설정은 Standard 값(켬)으로 시작합니다. 켜 두면 서버 빌드 판정과 실행 중 재확인이 더해지고, Standard에서는 시작할 때 온라인이 필요합니다. 포털의 기기·Variant 차단은 이 값과 관계없이 적용됩니다. 기본 Built-In 확인 UI를 쓰면 TMP dialog 패키지가 필요하며 Auto Setup이 가져옵니다. 오프라인 실행이 필요하면 오프라인 동작을 참고하세요.

4. 빌드 & 검증

별도 코드를 추가할 필요는 없습니다. 앱이 시작되면 SDK가 빌드 때 암호화 리소스로 플레이어에 넣은 라이선스 설정을 자동으로 읽습니다. 첫 실행 후 Player 로그에서 [OZeroLicense] activated; tier=pro caps=5 같은 줄을 확인하세요. Pro로 설정했는데 Standard 모드로 실행된다는 로그가 보이면 먼저 ProjectSettings/OZeroLicenseSettings.asset가 있고 빌드 전에 저장됐는지, Check Setup에 Old OZeroLicenseConfig asset found가 없는지 확인하세요.

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

오프라인 동작

Standard는 서버 없이 실행됩니다. Pro는 저장된 활성화 정보를 이용해 오프라인으로 시작할 수 있지만, 라이선스 정책에서 실행할 때마다 온라인 확인을 요구하도록 설정한 경우에는 서버 연결이 필요합니다.

Standard — 인터넷 없이 바로 실행

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

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

선택형 Pro 서버 기능을 켠 경우 온라인 상태에서 기기와 라이선스를 확인합니다. 선택한 서버 기능은 제한된 시간 동안 일시적인 연결 끊김을 견딜 수 있습니다. 이 확인은 이미 취득한 Native Variant의 빌드나 로컬 보호를 제한하지 않습니다.

오프라인 실행이 필요한 게임: Standard·Strict 프리셋은 Check Integrity With Server를 켜므로 서버 검증을 위해 온라인 연결이 필요합니다. 오프라인으로 실행해야 하면 Low 프리셋을 적용하거나 Security Config > Build Integrity > Pro - Online > Check Integrity With Server를 끄세요. 이 경우 서버 빌드 판정과 실행 중 재확인이 빠집니다.

저장된 Pro 활성화 정보는 서버가 서명한 활성화 토큰이 만료될 때까지 유효합니다. 만료 시점은 OZero 서버가 토큰을 발급할 때 정하며(현재 활성화 성공 후 7일), SDK에서 늘릴 수 없습니다. 이 기간이 지나면 다시 활성화에 성공해야 Pro 서버 기능을 사용할 수 있습니다. 이미 적용한 Native Variant와 로컬 탐지 설정은 바뀌지 않습니다. 이 유효 기간은 아래의 로컬 시계 정책과 별도로 적용됩니다.

Pro — 기기 시계 사용 방식 선택

고객 포털 > 라이선스 > 로컬 시계에서 저장된 Pro 활성화 정보를 오프라인 시작에 사용할 수 있는지 설정합니다. 온라인 활성화에는 서버 시간이 사용되므로, 기기의 날짜나 시간이 실제와 다르다는 이유만으로 온라인 Pro 기능이 낮아지지 않습니다.

설정 앱을 시작할 때의 동작
로컬 시계 사용 기기 시간이 선택한 허용 범위 안에 있으면 저장된 Pro 활성화 정보로 오프라인 시작이 가능합니다. 기본 허용 범위는 10분이며, 3분·1시간·3시간·6시간·12시간·24시간도 선택할 수 있습니다.
로컬 시계 사용 안 함 기기 시계를 확인하지 않는 대신 앱을 시작할 때마다 라이선스 서버에 연결해야 합니다. 항상 온라인으로 시작하는 서비스에 적합합니다.
온라인 확인이 필요한 상태에서 서버에 연결할 수 없으면, Pro 보호 수준을 낮춰 계속 실행하지 않고 앱 시작을 중단합니다. 설정 변경은 다음 활성화부터 적용되며 Native Variant를 새로 배정받거나 교체할 필요는 없습니다.

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

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

이 동작은 Security Config의 Pro - License & Online Services 카드에 있는 Offline & Device Policy 섹션의 Offline Pro Policy Mode(OZeroLicenseConfig.offlineProPolicyMode, Online Services 창에도 있음)로 설정합니다. 기기가 서버에 연결할 수 없을 때 로컬 Build Integrity가 마지막으로 받은 서명된 Pro 포털 차단 정책을 어떻게 다룰지 결정합니다.

모드 사용 기준
ApplyCachedBlockPolicies 대부분의 라이브 게임에 권장하는 기본값입니다. 유효한 서명 정책 캐시가 있으면 포털에서 차단한 빌드나 버전을 오프라인에서도 계속 차단합니다. 반대로 캐시가 없거나 삭제됐거나 만료된 상태에서 기기가 오프라인이면, 이 정책 게이트는 빌드를 실패시키지 않고 통과합니다.
RequireFreshPolicy 온라인 중심 게임을 위한 strict 모드입니다. 첫 실행, 재설치, 캐시 삭제, 캐시 만료처럼 사용할 수 있는 서명 정책이 없으면 오래되었거나 없는 정책을 신뢰하지 않고 Build Integrity를 실패 처리합니다. 이 fail-closed 동작은 서버 Integrity 옵션이 활성화되어 있어야 적용됩니다.
IgnoreCachedBlockPolicies 테스트나 마이그레이션을 위한 bypass 모드입니다. 오프라인 중에는 캐시된 포털 차단 정책을 읽지 않으므로 이 게이트에서 포털 차단 규칙이 적용되지 않습니다. 라이브 서비스 빌드에는 권장하지 않습니다.

요약하면 ApplyCachedBlockPolicies는 운영 기본값인 fail-open 모드, RequireFreshPolicy는 fail-closed 정책 모드, IgnoreCachedBlockPolicies는 테스트용 bypass입니다. 매 검증마다 온라인 서버 확인을 반드시 요구해야 한다면 이 옵션만으로 판단하지 말고 서버 Integrity 또는 Managed Verification 필수 옵션도 함께 구성하세요.

ApplyCachedBlockPolicies에서는 완전한 오프라인 첫 실행, 재설치, 캐시 삭제, 캐시 만료 상태에서 포털 차단 정책을 적용할 수 없습니다. 이런 경우에도 유효한 서명 정책을 반드시 요구해야 한다면 RequireFreshPolicy와 서버 Integrity를 함께 사용하세요.
상태 디텍터 텔레메트리 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를 쓰려는 설정이면 정상입니다. ProjectSettings/OZeroLicenseSettings.asset가 없으면 빌드는 Standard(서버 미사용)로 동작합니다. Pro를 기대했다면 이 파일이 있는지(Security Config나 Welcome 창의 Online Services 버튼이 만듭니다), tier=Pro와 licenseKey가 채워져 있는지, Check Setup에 Old OZeroLicenseConfig asset found가 없는지 확인하세요.
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·macOS·Linux는 Player Settings의 Company Name과 Product Name, WebGL은 실행 origin을 확인하세요. Unity 기본값(DefaultCompany, My project, New Unity Project, 빈 값)은 등록될 수 없으므로, Pro에서 데스크톱 빌드 대상이면 Check Setup이 Company Name is still Unity's default(또는 Product Name is still Unity's default) 경고를 표시하고, 그대로 두면 활성화가 403 DESKTOP_COMPANY_NAME_MISMATCH / DESKTOP_PRODUCT_NAME_MISMATCH로 거부되어 Standard로 동작합니다. 포털에 등록된 값과 다르면 /v1/activate가 거부됩니다. 값을 바꾼 뒤에는 새 빌드로 다시 테스트하세요.
activation token signature verification failed 서버 응답은 왔지만, SDK가 이 응답을 신뢰할 수 없다고 판단했습니다. 이 항목은 Pro 활성화에서만 확인합니다. 고객 포털 > Server Key의 Active publicKey를 Window > OZero Security > Online Services > Server Connection > Server Public Key Hex에 넣으세요. 포털에 kid가 함께 보이더라도 Unity에는 64자리 hex publicKey만 넣어야 합니다. 값이 정확한데도 계속 실패하면 회사 프록시/MITM 장비를 거치지 않는 직접 네트워크에서 한 번 더 테스트하세요. Check Setup에 Pro server signing public key is not configured가 표시되면 Check Setup > Auto Setup에서 붙여 넣을 수도 있습니다.
private native variant manifest signature is invalid Variant 패키지의 manifest가 없거나 수정되었거나, 신뢰된 OZero Variant signing key로 서명되지 않았습니다. 이 문제는 Server Public Key Hex를 채워서 해결하는 문제가 아닙니다. Pro Variant 패키지는 배정된 패키지를 다시 다운로드하고, manifest와 native plugin 파일을 같은 패키지에서 가져온 상태로 유지하세요. 새로 받은 패키지도 실패하면 manifest 파일과 Unity 빌드 로그를 OZero 지원팀에 전달하세요.
DEVICE_BLOCKED 이 기기는 고객 포털에서 차단된 상태입니다. 포털에서 해당 deviceId의 보안 이벤트를 확인하세요. 실제 공격이나 정책 위반이면 그대로 두고, QA 기기나 오탐이면 포털에서 차단을 해제하세요. SDK는 이 응답을 받으면 저장된 Pro 권한을 지우고 차단 흐름을 실행합니다.
/v1/activate timed out / network error 서버에 연결하지 못했거나, 응답이 너무 늦었습니다. 기기가 https://api.ozerosecurity.com/health에 접속할 수 있는지 확인하세요. 회사 방화벽, 프록시, DNS 문제도 함께 확인합니다. 지연이 큰 지역에서 운영한다면 activationTimeoutSeconds를 조금 늘려 테스트하세요. 이미 저장된 Pro 정보가 있으면 SDK는 그 정보를 먼저 사용합니다.
cached entitlement past TTL; clearing. 저장된 Pro 정보의 유효 기간이 지났습니다. 기기가 활성화 토큰 만료(마지막 활성화 성공 후 7일)보다 오래 오프라인이면 저장된 Pro 정보를 더 이상 신뢰하지 않습니다. 게임은 Standard 수준으로 계속 실행되고, 다음 온라인 활성화가 성공하면 Pro 기능이 다시 켜집니다. 이 기간은 OZero 서버가 정하며 SDK에서 늘릴 수 없습니다.
SERVER_NOT_CONFIGURED / SIGN_FAILED 서버가 활성화 토큰에 서명하지 못했습니다. 대부분 클라이언트 설정 문제가 아니라 서버의 Server Key 또는 암호화 키 상태 문제입니다. 고객 포털의 Server Key 상태를 확인하세요. 운영 서버에서 발생했다면 로그 시간, 일부 licenseKey, 발생 플랫폼을 OZero 지원팀에 전달하세요.
출시 빌드에서는 Security Config의 Enable Debug Logs를 꺼 두는 것을 권장합니다. 라이선스 흐름 로그도 이 설정을 따릅니다. QA에는 유용하지만, 일반 사용자에게 배포되는 로그에 티어, 기능 상태, 탐지 흐름이 불필요하게 남을 수 있습니다.

프로젝트 설정

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

최소 빌드 타겟

플랫폼 최소 타겟 설명
Unity Editor 2022.3 LTS+ 2022.3 LTS 이상이 필요하며 Unity 6도 지원합니다.
iOS 14.0+ Player Settings의 Target minimum iOS Version을 14.0 이상으로 설정하세요.
macOS Apple Silicon Apple Silicon(arm64) Mac만 지원합니다. IL2CPP를 권장합니다.
Android API 24+ Minimum API Level을 24(Android 7.0) 이상으로 설정하세요. 출시 빌드는 IL2CPP·ARM64를 권장합니다.
자세히: 플랫폼별 요구 사항
  • Unity Editor: OZero Security SDK는 Unity 2022.3 LTS 이상이 필요하며 Unity 6도 검증되었습니다. 2021.3 프로젝트는 패키지를 가져오기 전에 업그레이드해야 합니다.
  • iOS: iOS 빌드에서는 Project Settings > Player > iOS > Target minimum iOS Version을 14.0 이상으로 설정하세요. ABI 21 Injection 탐지기가 Apple 시스템 이미지와 주입된 코드를 구분하는 데 iOS 14의 shared-cache API를 쓰기 때문이며, 그보다 낮으면 Editor preflight가 실패합니다.
  • macOS: macOS 빌드는 Apple Silicon(arm64) Mac을 지원합니다. Intel Mac은 지원하지 않습니다. IL2CPP를 권장합니다. Scripting Backend가 Mono이면 Check Setup이 IL2CPP 권장 경고를 표시하고, 기본값인 Fail Build on Setup Warnings 설정 때문에 빌드가 실패합니다. Mono를 의도적으로 사용하려면 이 설정을 끄고 macOS 서명 절차를 따르세요.
Apple 개인정보 매니페스트

Apple privacy manifest: SDK에는 PrivacyInfo.xcprivacy가 포함되며, Unity 후처리가 앱의 manifest를 iOS Xcode 앱 target과 macOS의 Contents/Resources에 씁니다. Standard 빌드는 OZero가 앱 내부 SDK 상태에 Unity PlayerPrefs를 쓰기 때문에 UserDefaults CA92.1 사유만 선언하고, 수집 데이터는 선언하지 않습니다. OZero 서버 기능을 쓰는 Pro 라이선스 빌드는 PrivacyInfo.ServerFeatures.plist의 수집 데이터 4종(Device ID, User ID, Gameplay Content, Other Diagnostic Data)을 함께 선언합니다. 모두 사용자와 연결되고, 추적에 쓰지 않으며, 앱 기능 목적입니다. 앱의 같은 위치(iOS Xcode 프로젝트 루트, macOS의 Contents/Resources)에 이미 PrivacyInfo.xcprivacy가 있으면 덮어쓰지 않고 SDK 선언을 병합합니다. 접근 API 사유와 수집 데이터는 유형별로 합치고, 앱의 NSPrivacyTracking과 NSPrivacyTrackingDomains는 그대로 둡니다. 최종 앱에서 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 단계부터 시작하세요. Mono 백엔드에서는 Managed Stripping Level을 Disabled로 두세요. 켜면 Unity가 managed .pdb 파일(<Product>_Data/Managed/*.pdb)을 함께 넣고, OZero 릴리스 빌드 검사가 이를 디버그 심볼로 보고 빌드를 막습니다.

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

클라이언트 보호의 한계

Mono 빌드는 일반 도구로 디컴파일하고 고칠 수 있어, 수정된 빌드가 클라이언트 검사를 건너뛸 수 있습니다. IL2CPP는 수정 비용을 크게 높이지만 완전한 방지는 아닙니다. 진짜 대책은 서버 검증, 그리고 게임 코드가 Secure Variables 같은 OZero API에 의존하게 하는 것입니다.

우회 피해가 큰 게임에는 다음 구성을 권장합니다.

자세히: 부팅 파일 편집과 로드 앵커

플레이어 시작 파일 편집. Unity는 RuntimeInitializeOnLoads.json, ScriptingAssemblies.json, globalgamemanagers.assets의 스크립트 표로 OZero의 시작 지점을 찾습니다. 이 파일을 지우거나 고친 빌드에서는 OZero가 아예 시작되지 않을 수 있습니다. SDK 1.2.0은 다음 세 가지로 대응합니다.

  • 네이티브 로드 앵커: 스크립트가 실행되기 전에 OZero 네이티브 코드가 먼저 시작됩니다. Windows·macOS·Linux는 플러그인의 Load on startup 설정으로 플레이어가 로드합니다. iOS와 IL2CPP Android는 네이티브 코어가 앱 바이너리에 링크되어(Android는 Plugins/Android/<abi>/libOZeroSecurity.a가 libil2cpp.so 안에) 정적 초기화로 시작하므로 manifest나 시작 JSON 파일을 고쳐도 영향이 없고, 우회하려면 libil2cpp.so 바이너리를 패치해야 합니다. Mono Android는 정적 링크가 불가능해 OZeroSecurityBoot.androidlib의 ContentProvider가 libOZeroSecurity.so를 로드합니다. 90초 안에 OZero 부트스트랩이 시작되지 않으면 네이티브 코드가 프로세스를 종료합니다(abort reason 0x0BADB008).
  • 해시 범위: 이 시작 파일들은 Build Integrity 통합 해시(oz_ahash)에 항상 포함됩니다. Windows·macOS·Linux(Mono·IL2CPP)는 네 파일 모두, Android는 RuntimeInitializeOnLoads.json와 ScriptingAssemblies.json가 포함되므로, OZero가 시작된 뒤에는 일부만 고친 편집도 탐지됩니다.
  • manifest 서명: 이제 Standard 프리셋도 Strict처럼 서명된 manifest를 요구합니다(Require Manifest Signature 켬).
Development 빌드 디버깅: Wait For Managed Debugger를 켠 Development 빌드는 디버거가 붙을 때까지 첫 씬을 로드하기 전에 멈춥니다. 90초 안에 디버거를 붙이지 않으면 로드 앵커가 플레이어를 종료합니다(abort reason 0x0BADB008). 개발용 예외 스위치는 공격자에게도 스위치가 되므로 두지 않았습니다.
SDK 혼자서는 막을 수 없는 경우: 공격자가 globalgamemanagers의 preload 목록에서 OZero를 빼거나(데스크톱) 앱 바이너리를 패치한 뒤(iOS, IL2CPP Android) 빌드를 다시 서명하면 여전히 OZero가 시작되지 않게 할 수 있습니다. Mono Android는 훨씬 쉽습니다. manifest에서 provider를 지우고 APK를 다시 서명하기만 하면 앵커가 사라지므로 Android 출시 빌드는 IL2CPP를 쓰세요. 근본 대책은 게임 코드가 Secure Variables(OZeroSV) 같은 OZero API에 실제로 의존하게 해서, OZero가 빠진 빌드로는 게임을 진행할 수 없게 만드는 것입니다.
OZero Windows 네이티브 플러그인은 C/C++ 런타임을 정적으로 포함하므로, 플레이어 PC에 Visual C++ 재배포 패키지가 없어도 동작합니다.

macOS: Developer ID 서명과 공증

Unity가 만든 macOS 빌드는 ad-hoc 서명이라, Build Integrity가 켜져 있으면 실행 약 10초 뒤 종료됩니다. 배포하려면 Developer ID Application 인증서로 서명해야 합니다. 자동 서명을 켜 두면 빌드가 끝날 때 OZero가 서명합니다.

자동 서명 켜기 (권장)

  1. Window > OZero Security > macOS Distribution Signing을 열고 Sign macOS builds를 켜세요. 빌드 대상이 macOS면 Check Setup의 Open signing settings로도 열 수 있습니다.
  2. Certificate에서 Developer ID Application 인증서를 고르세요. 키체인에 하나뿐이면 Automatic으로 두면 됩니다.
  3. 빌드 중에 공증까지 하려면 Notarize를 켜고, xcrun notarytool store-credentials로 한 번 만들어 둔 Notary keychain profile 이름을 입력하세요.
  4. Save를 누르세요. 이후 macOS 빌드가 끝날 때마다 자동으로 서명되며, Console에 macOS distribution signing complete가 나오면 바로 배포할 수 있습니다.
주의: codesign은 잠금 해제된 키체인이 필요하므로 로그인된 Mac 세션에서 빌드하세요. 서명된 앱을 나중에 다른 도구로 다시 서명하면 무결성 파일이 맞지 않아 실행 시 종료되니, 다시 서명하지 마세요.
자세히: 자동 서명 설정과 CI

준비물: Xcode command line tools가 설치된 Mac, 로그인한 사용자의 키체인에 있는 Developer ID Application 인증서. 공증까지 하려면 xcrun notarytool store-credentials <프로필 이름>으로 공증 자격 증명을 한 번 저장해 두세요.

  • Certificate 목록에서 Developer ID Application 인증서를 고릅니다. 키체인에 하나뿐이면 Automatic으로 두면 됩니다. Refresh를 누르면 목록을 다시 읽습니다. 인증서가 CI 키체인처럼 키체인 검색 목록 밖에 있을 때만 Advanced (CI or a separate keychain)에서 Keychain path를 지정하고 Refresh를 누르세요. 그 밖에는 Advanced를 비워 둡니다.
  • Save를 누릅니다. 이후 macOS 빌드가 끝날 때마다 OZero가 자동으로 서명합니다(중첩 코드 → OZero 무결성 재생성 → 앱 서명 → 검증 → 선택 시 공증). Console에 macOS distribution signing complete (TeamIdentifier=…)가 나오면 바로 배포할 수 있습니다. 설정은 ProjectSettings/OZeroMacOSSigning.json에 저장되며 비밀값은 저장하지 않습니다.
  • 키체인에 Developer ID Application 인증서가 정확히 하나 있으면 Auto Setup에서 바로 켤 수 있습니다(macOS distribution signing: Off -> On 단계).
설정은 ProjectSettings/OZeroMacOSSigning.json에 저장되어 버전 관리로 팀과 공유됩니다. 비밀번호는 저장하지 않습니다. 빌드는 Mac에 로그인한 세션에서 하세요. codesign은 잠금 해제된 키체인이 필요해서 SSH나 잠긴 CI 세션에서는 실패합니다. 서명이 실패하면 빌드도 실패합니다. Valve의 libsteam_api는 서명을 자동으로 유지하고, 다른 벤더 서명 plugin은 Keep vendor signature에 적으세요. 자동 서명이 꺼져 있으면(기본값) 빌드 로그에 서명 안내가 남고 앱은 ad-hoc 상태로 남습니다. Windows 에디터는 서명할 수 없어서 이 설정이 켜진 채로 macOS 빌드를 하면 실패합니다. 그 PC에서는 OZERO_MACOS_AUTOSIGN=0을 설정하고 Mac에서 스크립트로 서명하세요. 서명 로그는 빌드 폴더가 아니라 프로젝트의 Library/OZeroMacSign-logs/<앱 이름>에 남습니다.
CI: 환경 변수가 저장된 설정보다 우선합니다. OZERO_MACOS_AUTOSIGN(1 또는 0), OZERO_MACOS_SIGN_IDENTITY, OZERO_MACOS_SIGN_KEYCHAIN, OZERO_MACOS_KEEP_SIGNED, OZERO_MACOS_NOTARY_PROFILE(값이 있으면 공증을 켭니다). 빌드 전에 작업 안에서 서명용 키체인 잠금을 풀어 두세요.
Windows에서 빌드했거나 기존 .app에 서명할 때: 서명 스크립트

OZero에 포함된 스크립트를 Mac에서 실행합니다. .app과 그 앱을 빌드한 Unity 프로젝트를 지정하고, 실행 전에 에디터에서 그 프로젝트를 닫아 두세요.

bash "/path/to/YourProject/Assets/OZeroSecurity/Tools/macOS/OZeroMacSign.sh" \
  --app "/path/to/YourGame.app" \
  --project "/path/to/YourProject" \
  --notary-profile "YourNotaryProfile"

스크립트가 done을 출력하면 바로 배포할 수 있습니다. 이후에는 다시 서명하지 마세요. 다시 서명해야 한다면 그 앱에 스크립트를 다시 실행하세요. 스크립트가 중간에 멈추면 마지막 줄에 실패한 단계와 해결 방법이 나오고, 로그는 현재 폴더의 OZeroMacSign-logs에 남습니다(--log-dir로 바꿀 수 있습니다).

옵션: 키체인에 Developer ID 인증서가 여러 개 있으면 security find-identity -v -p codesigning에 나오는 인증서 SHA-1을 --identity로 지정하세요. 공증을 직접 하려면 --notary-profile을 빼면 됩니다. Steamworks(libsteam_api)는 Valve 서명을 자동으로 유지하고, 다른 벤더 서명 plugin은 --keep-signed <파일 이름>으로 지정합니다. 스크립트는 프로젝트의 에디터 버전을 Unity Hub 폴더와 Spotlight에서 찾습니다. 찾지 못하면 --unity로 Unity 실행 파일을 지정하거나 OZERO_UNITY_PATH 환경 변수를 설정하세요. 전체 옵션은 --help로 확인할 수 있습니다.
Entitlement

자동 서명과 스크립트가 알아서 정합니다. --entitlements로 직접 만든 파일을 넘기거나 OZero 도구 없이 서명할 때 이 표를 따르세요.

Entitlement IL2CPP (권장) Mono Steamworks 등 벤더 서명 plugin을 함께 쓸 때
com.apple.security.cs.allow-jit—필요—
com.apple.security.cs.disable-library-validation——필요
com.apple.security.cs.allow-dyld-environment-variables넣지 마세요넣지 마세요넣지 마세요
com.apple.security.get-task-allow넣지 마세요넣지 마세요넣지 마세요
넣지 마세요 항목이 있으면 OZero가 앱을 종료합니다. allow-dyld-environment-variables는 macos_unsafe_dyld_state로, 로컬의 어떤 프로세스든 디버거를 붙일 수 있게 하는 get-task-allow는 macos_debuggable_signature로 판정합니다. disable-library-validation이 있으면 OZero는 차단하지 않고 경고 로그만 남깁니다. 범위가 더 넓은 disable-executable-page-protection은 필요하지 않습니다.
그래도 앱이 종료될 때: 메시지별 원인과 해결
메시지 원인 해결
macos_unsafe_dyld_state앱이 hardened runtime 없이 실행됐습니다(예: Unity 빌드를 그대로 배포). 또는 allow-dyld-environment-variables가 들어 있습니다.자동 서명을 켜거나 스크립트로 서명하세요. 직접 만든 entitlement 파일을 쓴다면 해당 항목을 빼세요.
macos_debuggable_signatureget-task-allow가 들어 있습니다.entitlement 파일에서 빼고 다시 서명하세요.
macos_outer_signer_mismatch무결성 파일을 다시 만든 뒤 바깥 .app을 다른 인증서나 ad-hoc으로 다시 서명했습니다.자동 서명으로 다시 빌드하거나 그 앱에 스크립트를 다시 실행하세요. 바깥 .app은 같은 Developer ID로 한 번만 서명합니다.
aggregate hash mismatch무결성 파일을 다시 만든 뒤 파일을 서명하거나 수정했습니다. 또는 재생성을 건너뛰었습니다.자동 서명으로 다시 빌드하거나 그 앱에 스크립트를 다시 실행하세요. 이후에는 앱 안의 파일을 바꾸지 마세요.
Library not loaded … different Team IDsdisable-library-validation이 없어 macOS가 Steamworks 같은 벤더 서명 plugin을 불러오지 않았습니다. OZero가 시작되기 전에 일어납니다.자동 서명이나 스크립트를 쓰세요. 둘 다 벤더 서명을 유지하면 이 entitlement를 자동으로 넣습니다. 직접 서명한다면 entitlement 파일에 추가하세요.
macOS nested signing failed (OZeroMacSign.sh exit …)codesign이 키체인을 쓰지 못했습니다. SSH나 잠긴 CI 세션에서 빌드했거나 Developer ID Application 인증서가 설치돼 있지 않습니다. Console에 스크립트 출력이 나옵니다.Mac에 로그인한 세션에서 빌드하거나, CI 작업 안에서 키체인 잠금을 푸세요(security unlock-keychain). 인증서는 security find-identity -v -p codesigning으로 확인합니다.
Notarize is on but no notary keychain profile is set서명 설정에서 Notarize를 켰지만 공증 키체인 프로필이 비어 있습니다. 빌드가 서명 전에 멈춥니다.xcrun notarytool store-credentials <이름>으로 프로필을 만들고 Notary keychain profile에 이름을 입력하거나, Notarize를 끄세요.
more than one identity matches키체인에 조건에 맞는 인증서가 여러 개 있습니다.Certificate 목록에서 인증서 하나를 고르거나, 출력된 목록의 SHA-1 하나를 Advanced > Identity (SHA-1 or name)에 입력하세요(스크립트는 --identity).
스크립트 없이 직접 서명하기 (CI 또는 자체 파이프라인)

자동 서명과 스크립트는 아래 세 단계를 이 순서대로 실행합니다. 자체 파이프라인에서 서명할 때도 순서를 지키세요. 3단계 뒤에 무엇이든 다시 서명하면 세 단계를 처음부터 다시 해야 합니다.

  1. Contents 안의 모든 .dylib, .bundle, .framework를 codesign --force --options runtime --timestamp로 서명합니다. Valve의 libsteam_api는 Valve 서명을 그대로 둡니다.
  2. OZero 무결성 파일을 다시 생성합니다. 서명하면 OZero가 빌드 때 hash한 plugin 파일이 바뀌므로, 이 단계를 빼면 앱이 0x0A로 종료됩니다. 빌드를 만든 프로젝트로 Unity를 batch mode로 실행하고 OZERO_MACOS_SIGNED_APP_PATH에 .app 경로를 지정합니다(아래 명령). IL2CPP와 Mono 빌드 모두 같은 절차입니다. 재생성은 서명한 내부 코드(GameAssembly.dylib, Mono는 UnityPlayer.dylib)의 Team ID도 기록합니다.
  3. 바깥 .app을 --deep 없이, 같은 Developer ID와 아래 entitlement로 한 번만 서명한 뒤 공증과 staple을 진행합니다. 바깥 서명의 Team ID가 기록값과 다르거나 ad-hoc이면 OZero가 macos_outer_signer_mismatch(0x0A)로 앱을 종료합니다. 배포본을 복사해 바깥 서명만 바꾸고 권한을 추가하거나 라이브러리를 끼워 넣은 사본을 막기 위한 검사입니다.
OZERO_MACOS_SIGNED_APP_PATH="/path/to/YourGame.app" \
  "/path/to/Unity.app/Contents/MacOS/Unity" -batchmode -nographics -quit \
  -projectPath "/path/to/YourProject" \
  -executeMethod OZeroSDK.Security.Editor.OZeroMacOSPostSignIntegrityRebaker.RebakeAfterNestedSigning \
  -logFile rebake.log

Windows / Linux: 코드 서명과 무결성 파일 재생성

OZero DLL의 코드 서명: OZero Windows 네이티브 DLL(OZeroSecurity.dll, OZeroSecurity64.dll, OZeroPCStoreBridge.dll, OZeroPCStoreBridge64.dll, Pro 패키지 포함)에는 OZero Security Inc 명의의 코드 서명과 타임스탬프가 들어 있습니다. 파일을 마우스 오른쪽 버튼으로 눌러 속성 > 디지털 서명에서 서명자가 OZero Security Inc이고 타임스탬프가 있는지, 서명 세부 정보에서 서명이 유효한지 확인하세요. 서명이 없거나 다른 게시자로 표시되면 정품 패키지가 아니므로 고객 포털이나 구매한 스토어에서 다시 받으세요.

Windows나 Linux 빌드에 직접 코드 서명을 하지 않는다면 아래 단계는 건너뛰세요. 빌드 뒤 실행 파일이나 네이티브 모듈에 서명했다면, 서명한 다음에 OZero 무결성 파일을 다시 생성해야 합니다. 그러지 않으면 시작 시 무결성 오류(0x0A)로 종료됩니다.

  1. 평소처럼 빌드한 뒤 실행 파일과 직접 만든 DLL·.so에 서명하세요. OZero DLL은 이미 서명돼 있으니 다시 서명하지 않아도 됩니다. 출시 빌드에는 OS가 신뢰하는 인증서가 필요하며, 자체 서명 테스트 인증서로는 통과하지 않습니다.
  2. 아래 명령으로 서명한 산출물 기준의 무결성 파일을 다시 생성하세요. 에디터에서는 Tools > OZero Security > Re-bake Integrity After Code Signing (Windows or Linux player)...을 써도 됩니다.
  3. 그대로 패키징해 배포하세요. 무결성 파일은 <Game>_Data/StreamingAssets 아래에 있어 서명 대상이 아닙니다. 서버 검증을 쓴다면 재생성이 출력한 manifest 해시를 등록하세요.
set OZERO_SIGNED_PLAYER_PATH=C:\dist\YourGame.exe
"C:\Program Files\Unity\Hub\Editor\<version>\Editor\Unity.exe" -batchmode -nographics -quit ^
  -projectPath "C:\path\to\YourProject" ^
  -executeMethod OZeroSDK.Security.Editor.OZeroStandalonePostSignIntegrityRebaker.RebakeAfterSigning ^
  -logFile rebake.log
OZero DLL에도 서명했다면: 정책상 출력 폴더의 모든 DLL에 서명해야 해서 OZero DLL에도 서명했다면, 서명이 바뀌면서 파일 내용도 달라집니다. 이때도 위 2단계 재생성을 하면 무결성 파일이 새 파일에 맞춰집니다. 다만 서명자는 OZero Security Inc가 아니라 귀사 인증서로 표시됩니다. 프로젝트 안의 원본 DLL(Assets/OZeroSecurity/Plugins)에는 서명하지 마세요. 빌드할 때 OZero가 서명한 Variant 매니페스트의 해시와 대조하므로, 원본이 바뀌면 빌드가 멈춥니다.
고객 빌드 스크립트: 보안 설정 읽기와 변경 (고급)

OZero는 빌드 시작 단계(콜백 순서 50)에서 보안 설정을 oz_cfg.bin으로 굽습니다. 설정은 Assets/ 밖의 Project Settings에 있으므로 빌드 중에 아무것도 옮기거나 되돌리지 않고, 평문 설정 에셋이 플레이어에 들어가지 않습니다. OZeroSecurityConfig.Instance는 굽기 전후 어느 콜백 순서에서든 프로젝트 설정을 돌려줍니다. WebGL 빌드는 평문 에셋 대신 빌드 때 만든 리소스를 쓰며, 이 리소스는 빌드 뒤 지워집니다.

  • 에디터 코드에서는 OZeroSecurityConfig.Instance로 값을 읽습니다. callbackOrder에 제한이 없습니다.
  • 스크립트(CI, 빌드 파이프라인)에서 설정을 바꾸려면 OZeroSDK.Security.OZeroSecurityProjectSettings를 쓰세요. Load()는 설정이 없으면 null을 돌려주고, LoadOrCreate()는 없을 때 Standard 프리셋으로 만들어 돌려주며, Save()가 저장합니다. 돌려받은 객체를 new SerializedObject(...) 등으로 고치면 되고, 저장하기 전이라도 빌드는 메모리의 값을 굽습니다.
  • DeveloperSecret과 매니페스트 서명 개인키 경로는 UserSettings/OZeroSecuritySecrets.json에 있는 사용자별 값입니다. 빌드 머신에서는 OZERO_DEVELOPER_SECRET와 OZERO_MANIFEST_SIGNING_PRIVATE_KEY_PATH를 설정하세요. 설정된 환경변수는 저장된 값보다 우선하며 파일에 다시 쓰지 않습니다.
  • SDK 1.1 이하의 OZeroSecurityConfig 에셋이 Assets/ 아래에 남아 있으면 빌드가 멈춥니다. Check Setup(Import 또는 Auto Setup)으로 가져오거나, 배치 모드에서는 -executeMethod OZeroSDK.Security.Editor.OZeroLegacyConfigImporter.ImportForBatchMode를 쓰세요.
  • 온라인 애드온의 라이선스 설정도 같은 방식입니다. ProjectSettings/OZeroLicenseSettings.asset에 저장되며(파일이 없으면 Standard, 서버 미사용 빌드), 스크립트에서는 OZeroSDK.Security.License.OZeroLicenseProjectSettings(Load, LoadOrCreate, Save, Reload)로 바꿉니다. 플레이어에는 빌드 때 만든 암호화 리소스로 들어가고, 이 리소스는 빌드 뒤 지워집니다. Assets/ 아래 옛 OZeroLicenseConfig 에셋이 남아 있어도 빌드가 멈추므로, Check Setup(Old OZeroLicenseConfig asset found)이나 배치 모드의 -executeMethod OZeroSDK.Online.Editor.OZeroLegacyLicenseImporter.ImportForBatchMode로 가져오세요.

Android ProGuard / R8 설정

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.** { *; }
자세히: 규칙이 필요한 이유와 Android 네이티브 구성

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

  • Minify를 켠 뒤에만 Android 로그에서 JNI 조회 실패, 설치 출처 감지 실패, APK 서명 검사 결과 비어 있음이 보인다면 먼저 커스텀 브리지 keep 규칙을 확인하세요.
  • 스토어 배포용 빌드는 IL2CPP, 릴리스 서명, Android Sha Keys에 등록된 예상 Android SHA-256 지문, 그리고 최소 한 대 이상의 실제 기기 클린 실행 테스트를 기준으로 검증하세요.

네이티브 코어와 부트 provider: 패키지에는 ABI별로 libOZeroSecurity.so와 정적 코어 libOZeroSecurity.a가 모두 들어 있고, 빌드할 때 OZero가 Scripting Backend에 맞춰 하나를 고르므로 플러그인 임포트 설정을 손댈 필요가 없습니다. IL2CPP는 libOZeroSecurity.a를 libil2cpp.so에 링크하고 .so와 부트 provider는 빼므로, APK에 libOZeroSecurity.so가 없고 OZero 코드는 libil2cpp.so 해시에 포함됩니다. Mono는 libOZeroSecurity.so와 Plugins/Android/OZeroSecurityBoot.androidlib를 포함합니다. Unity는 이 라이브러리를 내보낸 Gradle 프로젝트에 서브프로젝트로 넣고, 병합된 manifest에 provider com.ozerosecurity.boot.OZeroBootProvider(exported=false, authority <applicationId>.ozerosecurity.boot)가 추가되며, 이 provider가 앱 프로세스가 시작될 때 .so를 로드합니다. 커스텀 main manifest나 Gradle 템플릿을 쓰더라도 이 라이브러리와 provider를 빼지 마세요. Mono 빌드가 네이티브 라이브러리를 로드하지 못하면(기기 ABI용 .so가 빠진 경우 포함) 앱이 시작 직후 종료됩니다.

5 보안 설정 (OZeroSecurityConfig)

보안 설정은 패키지를 import하면 Standard 프리셋으로 만들어집니다. Edit > Project Settings > OZero Security에서 편집하며, Window > OZero Security > Config & Dashboard도 같은 페이지를 엽니다. 공유 값은 ProjectSettings/OZeroSecuritySettings.asset에 저장되고 비밀값이 없으므로 버전 관리에 커밋합니다. DeveloperSecret과 매니페스트 서명 개인키 경로는 사용자별 값으로 UserSettings/OZeroSecuritySecrets.json에 저장되며 버전 관리에 넣지 않습니다. 런타임과 에디터 스크립트에서는 OZeroSecurityConfig.Instance로 읽고, 스크립트에서 값을 바꾸려면 고객 빌드 스크립트를 참고하세요. 페이지는 Quick Preset, Project & Advanced(Common Settings, Editor Play Mode), Detection Response(Global Threat Response, Player Notice (Managed UI)), Protection Modules 순서입니다. 모든 섹션은 접힌 상태로 시작하므로 제목을 눌러 펼친 뒤 설정하세요. Security Termination Notice 정책은 Detection Response의 Player Notice (Managed UI)에 있습니다.

OZero Security page in Edit > Project Settings (Config & Dashboard)

공통 설정

모든 보안 모듈에 공통으로 적용되는 최상위 설정입니다. 이 표의 기본값은 코드에 정의된 직렬화 기본값을 기준으로 합니다. Security Config에서는 Project & Advanced 그룹의 Common Settings에 있으며, Keys & Storage, Build Checks (Preflight), Logs & Diagnostics로 나뉩니다.

필드 Type 기본값 설명
developerSecret string "" OZeroSV_File과 OZeroSafePlayerPrefs 데이터를 보호할 때 사용하는 프로젝트별 secret입니다. 직접 임의 문자열을 입력하기보다 Config Dashboard의 Generate Secure Secret 버튼으로 생성하세요. 이 값은 게임마다 다르게 사용하고 문서, 로그, 공개 저장소에 노출하지 마세요. 출시 후 이 값을 변경하면 기존 버전에서 플레이어 기기에 저장한 보호 데이터를 새 버전에서 복호화할 수 없습니다. 사용자별로 UserSettings/OZeroSecuritySecrets.json에 저장되며 버전 관리에 들어가지 않습니다. 프로젝트를 빌드하는 모든 머신이 같은 값을 써야 하므로 비밀번호 관리자로 공유하거나 빌드 머신에서는 OZERO_DEVELOPER_SECRET를 설정하세요.
enableLog bool true 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을 생성하세요. 이 값은 버전 관리가 아니라 사용자별로 UserSettings/OZeroSecuritySecrets.json에 저장되며, 프로젝트를 빌드하는 모든 머신이 같은 값을 써야 합니다. 비밀번호 관리자로 공유하거나 빌드 머신에서는 OZERO_DEVELOPER_SECRET 환경변수를 설정하세요(설정하면 저장된 값보다 우선합니다). 출시 후에는 변경하지 마세요. 이 값을 변경하면 기존 사용자의 OZeroSV_File 파일은 읽을 수 없게 되어 null로 돌아오고(키 변경으로 기록되며 변조로 판정하지는 않습니다), OZeroSafePlayerPrefs 값은 기본값으로 돌아옵니다. 게임이 저장 데이터를 마이그레이션하거나 초기화하지 않으면 사용자는 저장 데이터를 잃습니다.

Player Notice (Managed UI) (모든 라이선스)

Security Termination Notice, 공통 다이얼로그 프리팹, 로컬라이징은 Standard와 Pro에서 모두 사용할 수 있습니다. Pro에는 Build Integrity와 Steam DRM용 Managed Verification 재시도·timeout·provider 설정이 추가됩니다. 선택형 package를 import하기 전에 Package Manager에서 Unity UI와 TextMeshPro를 설치하세요. 안내 UI는 이미 예약된 종료를 설명할 뿐 종료를 취소하거나 native deadline을 연장하지 않습니다.

Build preflight: 모든 라이선스에서 Security Termination Notice가 OZeroBuiltIn이거나 Pro Managed Verification이 Built-In이면 optional TMP package가 필요합니다. 준비 항목이 누락되면 빌드를 중단합니다. Dedicated Server에서는 Built-In UI를 사용할 수 없으므로 Disabled 또는 custom callback/logging을 선택하세요. 그래픽 프로젝트는 Window > OZero Security > Check Setup의 Import Package로 복구합니다.
로컬라이징: EN·KO·JA·ZH-CN·ZH-TW 안전 문구를 제공합니다. securityUiLanguageCode, 설정 fallback, 영어 순서로 선택합니다. Managed Verification 서버 Reason 원문은 표시하지 않습니다. 확장은 Tools > OZero Security > Localization > Managed Verification Text를 사용하세요.
즉시 fail-close 경계: Bootstrap/config/trust/초기 variant·assembly 검증 및 watchdog start 실패는 팝업 없이 즉시 종료합니다. Boot-ACK timeout, watchdog 종료, 실제 crash, OS kill, headless 실행도 UI를 보장하지 않습니다. 안내 dispatch는 RuntimeNoticeReady 이후에만 적용합니다. 안전한 OZ-SEC-* 코드만 표시하고 내부 탐지 원문은 노출하지 않습니다.
네이티브 릴리스 계약: Windows, macOS, Linux, Android, iOS Player에는 일치하는 ABI 21 네이티브 variant, 유효한 OZero 서명 Variant Manifest V3, 보호된 설정의 bootstrap policy field와 일치하는 정확한 8바이트 oz_boot.bin이 필요합니다. 누락, 형식 오류, 미서명, 불일치 또는 이전 ABI 산출물은 빌드/런타임 실패입니다. oz_boot.bin은 평문 환경 플래그만 담으며 변조 인증 산출물이 아닙니다.
폰트 범위: 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 기본값 설명
securityTerminationNoticePolicyenumDisabled모든 라이선스. 런타임 fatal event에서 안내 없음, OZero Built-In, custom callback 중 하나를 선택합니다. Low·Standard 보안 preset은 Built-In, Strict는 Disabled입니다. 초기 fail-close 경로에는 안내가 표시되지 않습니다.
securityUiDialogPrefabResourcePathstring""모든 라이선스. 프로젝트 소유 security dialog prefab의 Resources 경로입니다. 비우면 optional TMP package 기본값을 사용합니다. 기존 managedVerificationDialogPrefabResourcePath 값은 자동 migration됩니다.
securityUiLanguageCodestringauto모든 라이선스. 종료 안내와 Managed Verification UI 언어입니다. auto는 Application.systemLanguage를 따릅니다. 기존 managedVerificationLanguageCode 값은 자동 migration됩니다.
securityUiFallbackLanguageCodestringen모든 라이선스. 기본 영어 안전 문구 전에 사용할 fallback 언어입니다. 기존 managedVerificationFallbackLanguageCode 값은 자동 migration됩니다.
Security Config > Pro - License & Online Services > Online Verification UI > Ui Policy (Pro)enumBuiltInBlockingDialogPro Only 기능입니다. OZero Managed Build Integrity와 Steam DRM 상태를 어떤 사용자 UI 흐름으로 보여 줄지 선택합니다. 기본 차단형/안내형 다이얼로그는 TextMeshPro와 optional OZeroSecurity_BuiltInDialog_TMP.unitypackage import가 필요합니다. 프로젝트가 이 패키지를 import하지 않거나 자체 UI로 대체하려면 Custom UI / Callback Only를 사용하세요.
Security Config > Pro - License & Online Services > Online Verification UI > Retry Timeout Seconds (Pro)int15사용자가 Retry를 누른 뒤 retry-timeout 상태로 전환되기 전까지 기다릴 최대 시간입니다. 플레이어가 무기한 검증 대기 상태에 남지 않도록 설정합니다.
Security Config > Pro - License & Online Services > Online Verification UI > Online Required Timeout Seconds (Pro)int120온라인 필요 다이얼로그가 표시된 뒤 설정된 timeout action을 적용하기 전까지 기다릴 최대 시간입니다.
Security Config > Pro - License & Online Services > Online Verification UI > Timeout Action (Pro)enumBlockSession검증이 제한 시간 안에 복구되지 않을 때의 동작을 정합니다. 다이얼로그 유지, 보호 세션 차단, 앱 종료, 또는 커스텀 흐름을 위한 콜백 호출만 선택할 수 있습니다.
Security Config > Pro - License & Online Services > Online Verification UI > Auto Retry When Network Restored (Pro)boolfalse네트워크 연결이 복구되면 Managed Verification을 자동으로 다시 시도합니다. 명시적인 플레이어 조작 없이 재시도해도 안전한 게임 흐름에서만 켜세요.

Global Threat Response

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

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

연동 문제 자가진단 가이드

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

종료 안내에 OZ-SEC-* 지원 코드가 표시되면 문의에 포함하세요. 팝업이 없다고 OZero와 무관한 것은 아닙니다. 초기 fail-close, Boot-ACK·watchdog 종료, 실제 crash, OS kill은 UI 전에 종료될 수 있습니다. ozero_abort.txt, Player log, Check Setup, build preflight 결과를 확인하세요. Strict preset의 안내 정책 기본값은 Disabled입니다.
Enable Failure Diagnostics는 QA나 고객 지원 중 원인을 확인할 때만 켜세요. 진단 파일에는 모듈명, 해시, 기기 상태, 설치 패키지명, 런타임 설정 일부가 들어갈 수 있습니다. 공개 릴리스 빌드에는 켜둔 채로 배포하지 마세요.

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

순서 할 일 확인 방법
1Unity에서 Edit > Project Settings > OZero Security(또는 Window > OZero Security > Config & Dashboard)를 열고 Common Settings > Logs & Diagnostics > Enable Failure Diagnostics를 켭니다.QA 또는 고객 지원 중 원인을 확인할 때만 사용하고, 공개 릴리스에는 켜둔 채로 배포하지 마세요.
2설정을 더 바꾸기 전에 같은 빌드로 문제를 한 번 재현합니다.재현 전에 여러 설정을 한꺼번에 바꾸면 원인 추적이 어려워집니다. 먼저 현재 상태의 진단 파일을 확보하세요.
3Application.persistentDataPath에서 가장 최근의 ozero_*_failure.log 파일을 엽니다.Build Integrity는 ozero_integrity_failure.log와 ozero_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가 반응한 이유를 사람이 읽을 수 있게 요약한 내용입니다.이 문장은 최종 판정이 아니라 힌트입니다. module과 subCode를 먼저 보고, 그 다음에 아래 증상 표에서 같은 키워드가 있는 행을 확인하세요.
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_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에만 최소로 사용하세요.
긴 로딩 화면에서 앱이 종료됩니다.메인 스레드가 아주 오래 막혀 있었습니다(Watchdog은 막힌 스레드는 기다리지만, 5분 넘게 멈춘 프로세스는 종료합니다).신뢰할 수 있는 긴 로딩 구간만 OZeroWatchdog.BeginLoadingGrace로 감싸세요. 일반 게임플레이 멈춤을 숨기는 용도로 쓰면 안 됩니다.
Android에서 Minify/ProGuard/R8을 켠 뒤 앱이 꺼집니다.Unity bridge 또는 프로젝트의 custom Android bridge class가 제거되거나 이름이 바뀌었을 수 있습니다.Android ProGuard / R8 keep rule을 적용하세요. Unity bridge class와 프로젝트에서 호출하는 Java/Kotlin bridge class를 보존해야 합니다.
macOS 빌드가 실행 약 10초 뒤 BuildIntegrity(0x0A)로 종료됩니다.앱이 ad-hoc 서명이거나, hardened runtime이 없거나, allow-dyld-environment-variables나 get-task-allow가 있거나, 재서명 후 무결성 파일을 다시 생성하지 않았거나, 바깥 .app을 내부 코드와 다른 인증서로 서명했습니다.macOS: Developer ID 서명과 공증에 따라 자동 서명이나 스크립트로 서명하세요. Player 로그에 원인(macos_unsafe_dyld_state, macos_debuggable_signature, macos_outer_signer_mismatch 또는 aggregate hash mismatch)이 나오고, 그 절 끝의 표에 원인별 해결 방법이 있습니다.
깨끗한 Windows PC에서 Standalone 빌드가 바로 종료됩니다.OZero 네이티브 플러그인이 빌드 산출물에 없거나 불러오지 못했을 수 있습니다.OZero 네이티브 플러그인(OZeroSecurity64.dll 등)이 빌드 산출물에 포함되었는지 확인하고, Player.log에서 DllNotFoundException이 가리키는 파일을 확인하세요. OZero 플러그인에는 VC++ 런타임이 필요 없지만, 게임의 다른 플러그인은 필요로 할 수 있습니다.
빌드가 OZeroSecurityConfig asset(s) found in the project와 함께 멈춥니다.SDK 1.1 이하의 OZeroSecurityConfig 에셋이 Assets/ 아래에 남아 있습니다. 평문으로 배포될 수 있어 빌드를 멈춥니다.Window > OZero Security > Check Setup을 열고 Old OZeroSecurityConfig asset found의 Import를 누르세요(Auto Setup으로도 됩니다). 값이 Project Settings로 옮겨지고 에셋은 삭제됩니다. 고객 빌드 스크립트를 참고하세요.
빌드가 OZeroLicenseConfig asset(s) found in the project와 함께 멈춥니다.SDK 1.1 이하의 OZeroLicenseConfig 에셋이 Assets/ 아래에 남아 있습니다. 평문으로 배포될 수 있어 빌드를 멈춥니다.Window > OZero Security > Check Setup을 열고 Old OZeroLicenseConfig asset found의 Import를 누르세요(Auto Setup으로도 됩니다). 라이선스 설정이 ProjectSettings/OZeroLicenseSettings.asset로 옮겨지고 에셋은 삭제됩니다.
증분 빌드(예: Android)가 OZero could not confirm its startup sentinel in these scenes, which Unity reused from the build cache 메시지로 멈춥니다.Unity가 바뀌지 않은 씬을 처리하지 않고 빌드 캐시에서 재사용했는데, OZero가 그 씬 내용에 시작 sentinel을 넣은 기록이 없습니다(예: SDK 업데이트 후 첫 증분 빌드).Clean Build로 한 번 빌드하세요(Build Settings > Clean Build 또는 BuildOptions.CleanBuildCache). OZero가 처리한 씬을 Library/OZeroSecurity/StartupSentinelScenes.txt에 기록하므로 이후 증분 빌드는 통과합니다. 메시지가 could not add its startup sentinel to any built scene이면 빌드에 활성화된 씬을 하나 이상 추가하세요.
에디터에서 탐지가 돼도 아무 반응이 없거나, Play Mode가 저절로 멈춥니다.Editor Play Mode Protection 설정(Off 또는 Test).Off(기본값)는 에디터에서 탐지를 로그로만 남깁니다. 설정된 대응을 실행하려면 Test로 바꾸세요. Test에서는 플레이어라면 종료할 탐지가 유예 시간 뒤 Play Mode를 멈춥니다(Editor Play Mode will stop in N ms 로그). 에디터 Play Mode를 참고하세요.
정상 overlay나 녹화 툴을 켰는데 Injection 이벤트가 나옵니다.Injection/Hooking 모듈이 로드된 모듈을 관측한 상황일 수 있습니다.먼저 Failure Diagnostics나 Pro 포털에서 탐지된 모듈 이름, 해시, 서명자를 확인하세요. 정상 프로그램이면 Pro 고객은 포털(인젭션 → 관측된 모듈)에서 해당 행의 허용을 누르면 다음 목록 갱신 때 모든 기기에 반영됩니다. Standard/Pro 고객은 해시를 injectionWhitelistEntries에 넣고 새 빌드를 배포합니다. 이름을 댈 수 없는 모듈은 허용하지 마세요.
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 에디터에서도 검증을 실행합니다. 에디터에서는 Editor Play Mode Protection이 Off(기본값)이면 탐지기를 켜지 않으므로 Test일 때만 적용됩니다. 테스트용으로 유용하지만 일반 개발 중에는 끄는 것을 권장합니다.
enablePeriodicValidationbooltrue게임 실행 중에도 무결성 검증을 반복합니다. 시작 시 1회 검증만으로 충분한 빌드가 아니라면 켜두는 것을 권장합니다.
periodicCheckIntervalfloat300 s게임 실행 중 빌드 무결성 재검증을 반복하는 기준 간격(초)입니다. 0 이하로 설정하면 주기 검사를 끕니다.
periodicCheckJitterPercentfloat35%검증 주기에 무작위 변화를 더해 검사 타이밍을 예측하기 어렵게 합니다. 구체적인 주기와 범위는 공개 문서에서 안내하지 않으며, 기본값 사용을 권장합니다.
timingAnomalyConsecutiveRequiredint7타이밍 기반 이상 신호가 몇 번 누적되어야 디버거 타이밍 드리프트로 판단할지 정합니다. 강한 디버거 신호는 즉시 실패할 수 있습니다.
timingAnomalyWindowSecondsfloat900 s타이밍 기반 이상 신호를 누적하는 시간 창입니다. 길수록 관대하고, Strict는 더 짧은 창을 사용합니다.
timingAnomalyFrameHitchSuppressionSecondsfloat20 s씬 로딩, 셰이더 컴파일, GC, OS 스케줄링 지연처럼 큰 프레임 히치가 발생한 뒤 일정 시간 동안 타이밍 기반 디버거 검사를 완화합니다.
checkAssemblyHashbooltrue빌드 시 생성된 manifest를 기준으로 컴파일된 어셈블리의 해시를 검증합니다(앱 시작 시와 주기 재검사의 런타임 검사). 네이티브 기동 집계 검사는 이 옵션으로 꺼지지 않습니다. 데스크톱 플레이어에서는 설정을 읽기도 전에 배포된 바이너리를 항상 검증하므로, 어셈블리가 변조되면 여전히 기동 단계에서 안전하게 종료됩니다. 그 집계가 어떤 IL2CPP 파일을 포함할지는 il2cppHash* 필드가 정합니다.
checkDebuggerbooltrue디버거 연결과 디버거처럼 보이는 런타임 타이밍 이상을 감지합니다.
failOnDebugBuildboolfalseUnity 디버그 빌드를 위반으로 간주합니다(릴리스 빌드에서 권장).
checkPlatformNativebooltrue플랫폼별 네이티브 검사를 실행합니다. 플랫폼에 따라 루팅/탈옥, APK 서명, 실행 환경, 프록시 또는 분석 도구 신호를 확인합니다.
failIfManifestMissingboolfalse*Inspector 기본값은 개발 편의를 위해 꺼져 있습니다. 하지만 development가 아닌 플레이어 빌드에서는 자동으로 켜지며, oz_manifest.ozero 파일이 없거나 읽을 수 없으면 위반으로 처리합니다.
failIfAssemblyHashBlobMissingboolfalse*Inspector 기본값은 개발 편의를 위해 꺼져 있습니다. 하지만 development가 아닌 플레이어 빌드에서는 자동으로 켜지며, 생성된 어셈블리 해시 블롭이 삭제되어도 해시 검증을 우회할 수 없게 합니다.
requireCodeSignature (Windows)boolfalse메인 실행 파일에 유효한 코드 서명이 있어야 합니다. Windows 전용. 서명하면 실행 파일의 해시가 바뀌므로 서명 뒤 무결성 파일을 다시 생성해야 합니다(Windows / Linux: 코드 서명과 무결성 파일 재생성 참고). 건너뛰면 Unexpected or modified native module로 종료됩니다.
blockVirtualMachine (Windows)boolfalse가상 머신 내부에서 게임 실행을 차단합니다. Windows 전용.
blockHyperV (Windows)boolfalseHyper-V VMBus 신호를 차단합니다. 이 검사는 Hyper-V 게스트와 Hyper-V 루트 파티션으로 동작하는 호스트 PC를 구분하지 못하므로, WSL2·Docker Desktop·Windows Sandbox·코어 격리(VBS)·Hyper-V 역할이 켜진 사용자도 함께 막힙니다. 일반 배포에서는 꺼 두고, 통제된 환경에서만 사용하세요.
blockNetworkProxies (Windows)boolfalse네트워크 프록시, 패킷 검사, 트래픽 분석 도구로 의심되는 실행 신호를 감지합니다. 경쟁형 빌드에서 사용하되 오탐 가능성을 테스트하세요.
blockReverseEngineeringTools (Windows)boolfalse역공학 또는 디버깅 도구로 의심되는 실행 신호를 감지합니다. 개발/QA 환경과 라이브 환경을 분리해 검증하세요.
blockSystemMonitorTools (Windows)boolfalse프로세스/시스템 모니터링 도구로 의심되는 실행 신호를 감지합니다. 일반 사용자 환경에서 오탐 가능성을 고려하세요.
il2cppHashGameAssemblybooltrueIL2CPP 데스크톱 빌드(Windows·macOS·Linux)의 GameAssembly 바이너리(.dll/.dylib/.so)를 해시 검증합니다. IL2CPP 파일 보호의 최소 권장 항목입니다.
il2cppHashGlobalGameManagersboolfalseIL2CPP 데스크톱 파일 해시 범위에 globalgamemanagers를 추가합니다. 코드 기본값은 꺼짐이지만 Standard와 Strict 프리셋은 이 값을 켭니다. SDK 1.2.0부터 Windows·macOS·Linux에서는 globalgamemanagers와 globalgamemanagers.assets가 이 옵션과 관계없이 플레이어 시작 파일로 항상 해시됩니다.
il2cppHashSharedAssetsboolfalseIL2CPP 데스크톱 파일 해시 범위에 sharedassets* 파일을 추가합니다. 코드 기본값은 꺼짐이지만 Standard와 Strict 프리셋은 이 값을 켭니다.
il2cppHashSceneFilesboolfalseIL2CPP 데스크톱 파일 해시 범위에 level* 같은 Unity 씬 파일을 추가합니다. 코드 기본값은 꺼짐이지만 Standard와 Strict 프리셋은 이 값을 켭니다.
il2cppHashResourcesAssetsboolfalseIL2CPP 검증 범위에 resources.assets를 추가합니다. Strict 모드에 유용하지만 패치 흐름을 먼저 테스트하세요.
il2cppAdditionalWatchedFilesList<string>—프로젝트가 별도 네이티브 페이로드를 포함하는 경우, 추가로 감시할 IL2CPP 데스크톱 출력 파일을 _Data 폴더(macOS는 Contents/Resources/Data) 기준 상대 경로로 지정합니다.
blockEmulator (Android)booltrueAndroid 전용입니다. 에뮬레이터 또는 미지원 런타임 신호를 무결성 위반으로 취급합니다. QA/에뮬레이터 테스트 중에는 완화하고, 프로덕션 빌드에서는 더 엄격한 정책을 권장합니다.
blockSystemRwMount (Android)booltrueAndroid 전용입니다. 시스템 파티션이 쓰기 가능하거나 root 계열 mount 상태가 보이면 무결성 위반으로 처리합니다. Standard 프리셋은 unlocked/rooted QA 기기 오탐을 줄이기 위해 이 값을 완화합니다.
linuxHostLayerPolicy (Linux)enumMonitorLD_PRELOAD, LD_AUDIT, WINEDLLOVERRIDES 같은 Linux 호스트 계층이 게임과 OS 사이에 끼어 있을 때의 반응입니다. Off는 무시, Monitor(Strict 포함 모든 프리셋의 기본값)는 게임을 종료하지 않고 기록만, Block은 위반으로 처리합니다. 정상적인 Steam Linux·Proton 플레이어도 이 계층을 쓰므로 Block은 플레이어 환경을 검증한 게임에서만 쓰세요.
protonPolicy (Linux / Proton)enumMonitorWindows 빌드가 Proton이나 Wine 위에서 실행될 때의 정책으로, 값은 위와 같습니다. 기본값 Monitor는 Steam Deck·Proton 플레이어의 플레이를 막지 않으면서 환경을 텔레메트리에 기록합니다.
acknowledgeHostLayerBlockboolfalseBlock 호스트 계층·Proton 정책을 쓰려면 먼저 켜야 하는 확인 스위치입니다. 이 확인 없이 Block을 설정하면 빌드 preflight가 실패하므로, 정상 Linux 플레이어의 게임을 실수로 종료시키는 빌드가 나가지 않습니다.
nativeModuleInventoryPolicyOff | Monitor | BlockBlock (Low: Monitor)Windows·Linux 플레이어 전용입니다. 빌드가 게임 폴더 아래 모든 native 모듈(*.dll / *.so)을 서명된 무결성 manifest에 기록합니다. 시작 시 Build Integrity가 폴더를 열거하고 기록된 파일을 해시해, 목록에 없는 native 파일이나 해시가 바뀐 파일을 위반으로 봅니다. Block(기본)은 종료, Monitor는 기록 후 계속, Off는 검사 생략입니다. Injection 탐지기도 같은 목록으로 게임 자신의 플러그인을 알아봅니다. 플레이어가 게임 폴더에 native 모드 로더를 넣는 것을 허용한다면 Monitor를 선택하세요. Low 프리셋은 Monitor입니다.
androidShaKeysList<string>—기대되는 APK 서명 인증서의 SHA-256 지문 목록입니다. 설치된 APK가 이 키 중 하나로 서명되지 않았다면 검증에 실패합니다(Android 전용). checkPlatformNative가 켜진 출시 빌드는 이 목록이 비어 있으면 빌드가 거부되며, 서명 검사를 건너뛰는 경우는 checkPlatformNative를 껐거나 개발 빌드일 때뿐입니다. Android에서는 씬·에셋 파일(globalgamemanagers, sharedassets*, level*, resources.assets)을 해시하지 않고 네이티브 라이브러리와 IL2CPP 메타데이터만 해시하므로, 에셋을 바꿔 다시 패키징한 APK를 막는 것은 이 서명 검사입니다. 출시 빌드에는 반드시 설정하세요. Google Play 앱 서명을 쓰면 빌드는 업로드 키로 서명되고, Play가 사용자에게 설치하는 APK는 앱 서명 키로 다시 서명됩니다. 두 키의 SHA-256을 모두 넣으세요(Play Console › 설정 › 앱 서명). 업로드 키만 넣으면 빌드는 통과하지만 Play에서 설치한 모든 사용자가 서명 검사에 실패합니다.
expectedBundleIds (iOS)List<string>—허용할 iOS Bundle ID 목록입니다. 비워두면 Bundle ID 검사를 건너뜁니다.
excludedAssembliesList<string>—해시 검증에서 제외할 어셈블리 이름 목록(.dll 확장자 제외). 런타임에 변경되는 어셈블리(예: 생성된 코드)에 사용하세요.
Security Config > Build Integrity > Pro - Online > Check Integrity With Server (Pro)boolfalsePro의 nonce → attest 흐름을 켭니다. 로컬 Build Integrity가 통과하면 SDK가 빌드 무결성 증거를 OZero에 제출합니다. attestationVerificationMode=OZeroManaged, optional OZero TMP dialog package, 기본 Managed Verification UI를 함께 쓰면 사용자 인증 흐름을 위한 게임 코드를 작성하지 않아도 됩니다. 프리셋 값: Low는 끔, Standard는 켬(OZeroManaged), Strict는 켬(CustomerGameServer). 새로 만드는 Online 설정은 Standard 프리셋 값으로 시작하며, 필드 기본값 자체는 꺼짐입니다. 켜면 서버가 빌드를 판정하고, 주입·후킹이 탐지되면 검증을 거부하며, 게임 서버용 OZA 토큰을 발급하고, 실행 중에도 다시 확인합니다. 꺼도 포털의 기기·Variant 차단은 적용되고, Build Integrity가 켜져 있으면 서명된 빌드·버전 차단 목록도 실행할 때 적용됩니다. 다만 새로 건 빌드·버전 차단은 다음 실행부터 적용됩니다. Pro에서 꺼져 있으면 Check Setup에 Info Pro server attestation is off가 표시되며, 빌드는 막지 않습니다. 켜고 기본 Built-In 확인 UI를 쓰면 TMP dialog 패키지가 필요하며, Auto Setup이 가져옵니다.
Security Config > Build Integrity > Pro - Online > Attestation Verification Mode (Pro)enumOZeroManaged최종 검증을 맡을 주체를 선택합니다. 기본값과 Low·Standard 보안 프리셋은 OZeroManaged로, SDK가 OZero 서버에 검증을 요청합니다. Strict 프리셋은 CustomerGameServer로, 게임 서버가 발급한 정보로 클라이언트에서 RequestGameServerAttestation(...)을 호출한 뒤 게임 서버가 OZA 토큰을 검증합니다. 이 값은 checkIntegrityWithServer가 켜져 있을 때만 쓰이며, Standard·Strict 프리셋은 그 값을 켜고 Low 프리셋은 끕니다. 라이선스 등급이 Pro가 아니면 어떤 프리셋을 적용해도 OZeroManaged로 유지됩니다.
Security Config > Build Integrity > Pro - Online > Attestation Network Policy (Pro)enumRequireOnlineRevalidation지원하는 정책은 RequireOnlineRevalidation 하나입니다. 서버 검증을 완료하거나 갱신할 수 없으면 OnlineRequired 상태로 전환하여 온라인 재검증을 요구합니다. 검증되지 않은 오프라인 세션을 허용하는 옵션은 없습니다.
manifestSigningPublicKeystring""서명된 Build Integrity manifest를 검증하는 Base64 RSA-2048 공개키입니다. Window > OZero Security > Config & Dashboard의 Generate Key Pair로 생성하세요. Online Services > Server Connection > Server Public Key Hex에 넣는 고객 포털 서버 키와는 다른 키입니다.
requireManifestSignatureboolfalse*Standard와 Strict 프리셋에서 켜집니다(Standard는 SDK 1.2.0부터). 릴리스 플레이어 빌드에서는 manifest 서명 검증이 강제로 켜지므로 출시 전에 키 쌍을 생성하세요.
manifestSigningPrivateKeyPathstring""빌드 시 manifest 서명에 사용할 private key PEM 경로입니다. 비워 두면 [ProjectRoot]/OZeroSigningKeys/manifest_private_key.pem을 사용합니다. Editor 전용이며 플레이어 빌드에는 포함되지 않습니다. 사용자별 값으로 UserSettings/OZeroSecuritySecrets.json에 저장되며 버전 관리에 들어가지 않습니다. 빌드 머신에서는 OZERO_MANIFEST_SIGNING_PRIVATE_KEY_PATH 환경변수로 지정할 수 있습니다.

플랫폼별 참고 (포털 차단): Android 빌드는 manifest hash를 만들지 않으므로 빌드 해시 차단은 적용되지 않고 버전 차단만 적용됩니다. WebGL은 서명된 차단 목록을 로컬에서 검사하지 않습니다. 버전 차단은 SDK 버전과 앱 버전을 같은 목록으로 비교하며 플랫폼을 구분하지 않으므로, 차단한 버전은 모든 플랫폼에 적용됩니다.

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

코드 없이 쓰는 흐름은 Security Config > Build Integrity > Pro - Online > Check Integrity With Server를 켜고, Attestation Verification Mode를 OZeroManaged로 설정한 뒤, optional OZero TMP dialog package를 import하고 Security Config > Pro - License & Online Services > Online Verification UI > Ui Policy를 기본 다이얼로그로 유지하면 됩니다. SDK가 nonce를 받고, attest 증거를 제출하고, OZero 관리형 세션 판정을 받은 뒤, 네트워크 필요, 재시도, timeout, 경고, 차단 상태를 기본 다이얼로그로 표시합니다. 기본 UI를 대체하거나 TextMeshPro를 쓰지 않거나 별도 telemetry/session gate를 붙일 때는 RegisterUserManagedVerificationStateCallback을 등록하세요.

CustomerGameServer에서는 게임 서버가 서버 API 키로 POST /v1/validate를 호출해 토큰을 확인합니다. 전체 흐름과 예제는 API 레퍼런스의 서버 API 키로 게임 서버 연동하기를 보세요.

💡 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)은 아직 진행 중일 수 있습니다.
OZeroOnlineAttestation.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 서명 키 쌍을 생성하세요. SDK 1.2.0부터 통합 해시(oz_ahash)에는 모든 프리셋에서 플레이어 시작 파일이 항상 포함됩니다. Windows·macOS·Linux(Mono·IL2CPP)는 RuntimeInitializeOnLoads.json, ScriptingAssemblies.json, globalgamemanagers, globalgamemanagers.assets, Android는 네이티브 라이브러리와 global-metadata.dat에 더해 RuntimeInitializeOnLoads.json, ScriptingAssemblies.json입니다. 데스크톱에서 Compression Method가 LZ4/LZ4HC이면 globalgamemanagers, 씬, 에셋이 data.unity3d 한 파일로 묶이며, 이때는 개별 파일 대신 이 파일이 있으면 항상 해시됩니다. 파일이 커도 SHA-256 계산은 시작 시 GB당 1초 안팎입니다.

Manifest 서명이 필요한 이유

빌드 시점에 OZero는 개인 키로 manifest에 서명합니다. 플레이어 빌드에는 개인 키가 포함되지 않고, 보안 설정에 저장된 공개 키만 포함됩니다. 런타임에는 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용 키 쌍을 생성합니다. 공개 키는 보안 설정(Project Settings)에 저장되고, 개인 키는 기본적으로 다음 경로에 저장됩니다(이 경로 자체는 User Settings에 사용자별로 저장됩니다):
    [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을 사용해야 합니다.

기존 키 쌍 유효성 검사

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

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

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

듀얼 핑거프린트 키 회전

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

현재 키와 이전 키

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

안전한 키 회전 절차

  1. 현재 fingerprint를 백업합니다. Assets/OZeroSecurity/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, 보안 설정의 공개 키, OZeroManifestTrustAnchor의 fingerprint, 생성된 manifest/signature 파일 중 하나가 서로 맞지 않을 때 발생합니다.

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

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

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

Project Settings > OZero Security > Build Integrity > Manifest Signing Public Key가 비어 있을 때 발생합니다. 해결: 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"

보안 설정의 공개 키와 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를 한 번 실행해 확인하는 것을 권장합니다.

Install Source (Android Only) 설정

게임이 승인된 스토어 또는 경로에서 설치된 경우에만 실행되도록 제한합니다. 사이드로드 또는 재패키징된 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를 허용합니다. 지역 스토어 배포가 필요할 때만 실제 기기에서 확인한 뒤 사용하세요.
Security Config > Install Source (Android Only) > Pro - Online > Check Installer With Server (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 (PC Only) 설정

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

꼭 넣을 것(3가지): Activate, Expected Steam App Id(0이면 데스크톱 빌드가 실패합니다), SDK Folder(Steamworks SDK 폴더, 빌드 서버에서는 OZERO_STEAMWORKS_SDK 환경 변수).
선택: Required DLC App Ids는 특정 DLC가 없으면 게임을 할 수 없는 경우에만 넣습니다.
고급(대부분 비워 둠): Validate Steam Api Dll Hash와 Expected Steam Api Dll Sha256 Hashes는 모든 프리셋에서 꺼져 있고, 꺼져 있으면 해시 목록은 쓰이지 않습니다. 해시를 넣지 않아도 빌드 때 복사된 steam_api 해시가 서명된 빌드 무결성 정보에 자동으로 들어가 Build Integrity가 교체를 탐지하고, Require Valve Signed Steam Api(Standard·Strict 프리셋에서 켜짐)가 Windows·macOS에서 Valve 서명을 확인합니다. 나머지 항목은 프리셋 기본값 그대로 두면 됩니다. Security Config의 이 섹션 맨 위 안내 상자에도 같은 내용이 나오며, 필드에 마우스를 올리면 값을 바꿀 때 일어나는 일을 볼 수 있습니다.

Steamworks SDK 폴더와 라이브러리 자동 복사

Project Settings > OZero Security > Steam Anti-Piracy (PC Only) > SDK Folder(또는 Check Setup의 Steam 설정 칸)에서 Steamworks SDK 폴더(sdk 폴더 또는 그 상위 폴더)를 고르세요. 빌드할 때 OZero가 대상 플랫폼에 맞는 Steam API 라이브러리를 빌드 산출물에 자동으로 복사하므로 직접 복사할 필요가 없습니다.

플랫폼 SDK 폴더의 파일 복사 위치
Windows x64redistributable_bin/win64/steam_api64.dllsteam_api64.dll (실행 파일 옆), <Game>_Data/Plugins/x86_64/
Windows x86redistributable_bin/steam_api.dllsteam_api.dll (실행 파일 옆), <Game>_Data/Plugins/x86/
Linux x64redistributable_bin/linux64/libsteam_api.solibsteam_api.so (실행 파일 옆), <Game>_Data/Plugins/x86_64/
macOSredistributable_bin/osx/libsteam_api.dylib<Game>.app/Contents/MacOS/libsteam_api.dylib

빌드를 시작하기 전에 OZero가 라이브러리의 파일 형식·아키텍처와 지원하는 Steamworks 버전(SteamApps v008–v009, SteamUtils v009–v010, SteamUser v022–v023)을 검사하며, 맞지 않으면 빌드가 멈춥니다. 복사는 무결성 manifest를 만들기 전에 일어나므로 Steam 라이브러리도 무결성 검사 대상에 들어갑니다. macOS 빌드에 서명할 때 libsteam_api.dylib의 Valve 서명은 그대로 유지됩니다(macOS 서명).

다른 PC와 CI: 고른 폴더는 그 PC의 Editor 설정에만 저장되고 프로젝트 파일에는 남지 않습니다. 다른 PC나 CI에서는 폴더를 다시 고르거나 환경 변수 OZERO_STEAMWORKS_SDK에 SDK 폴더 경로를 설정하세요. 둘 다 없으면 빌드가 안내 메시지와 함께 실패합니다.

데스크톱 전용: Steam Anti-Piracy는 Windows, macOS, Linux 플레이어에서만 동작합니다. 켠 채로 Android, iOS, WebGL을 빌드해도 빌드는 경고만 남기고 진행되며, 그 앱에서는 Steam 검사가 실행되지 않고 Steam 라이브러리도 포함되지 않습니다. 이때 Check Setup에는 Info 항목 Steam Anti-Piracy is skipped for this build target가 표시됩니다.

Steamworks.NET 등 래퍼와 함께 쓸 때: 래퍼 플러그인이 빌드 산출물에 넣은 steam_api 라이브러리가 선택한 SDK의 것과 다르면 OZero가 SDK의 라이브러리로 교체하고 빌드 로그에 경고를 남깁니다. 래퍼가 빌드된 것과 같은 Steamworks SDK 버전을 SDK Folder로 지정하세요.

필드 Type 기본값 설명
Activate Steam Anti-PiracycheckboxOffSteam Anti-Piracy 활성화 체크박스입니다. Steam App ID와 배포 방식이 프로젝트마다 다르므로 기본값은 Off입니다. 먼저 관찰/QA 모드에서 상태를 확인한 뒤 출시 빌드 정책을 강화하세요.
expectedSteamAppIdint0프로젝트의 Steam App ID입니다. 개발 확인용 AppID 480을 사용했다면 출시 전 반드시 실제 App ID로 바꾸세요.
requireSteamLaunchbooltrue게임이 실행 파일 직접 실행이 아니라 Steam을 통해 시작되었는지 확인합니다. 개발 중 직접 실행 테스트와 릴리스 정책을 구분하세요. 직접 실행돼 Steam에 재실행을 요청한 경우 이 인스턴스는 조용히 종료하고, Steam이 띄운 인스턴스가 실행을 이어 받습니다.
requireSteamApiInitbooltrueSteam API 초기화 성공 여부를 확인합니다. 로컬 개발 실행에서는 Steamworks 설정에 따라 실패할 수 있으므로, 최종 판정은 실제 Steam 배포 경로에서 확인하세요.
requireSubscribedCurrentAppbooltrue현재 Steam 계정이 앱을 소유하거나 사용 권한을 가지고 있는지 확인합니다. 이 항목은 로컬 검증이며, 서버 측 소유권 검증은 Pro에서 수행합니다.
requiredDlcAppIdsList<int>—필수 DLC의 Steam App ID 목록입니다(최대 10개, 초과하면 빌드가 실패합니다). 목록의 DLC를 하나라도 소유하지 않은 계정은 본편 미구독과 같은 등급의 인증 실패(required_dlc_missing)로 처리합니다. 설치 여부가 아니라 소유 여부(Steam BIsSubscribedApp) 기준입니다. Steamworks 파트너(개발사) 계정은 자사 DLC를 소유한 것으로 보고될 수 있으니, 미소유 테스트는 DLC가 없는 일반 계정으로 하세요. 콘텐츠 잠금 해제용 선택형 DLC는 이 목록에 넣지 말고 QueryDlcOwnership으로 확인하세요.
blockSteamAppIdTxtInReleasebooltruesteam_appid.txt는 Steam 없이 로컬 실행 테스트를 할 때만 쓰는 개발용 파일입니다. 릴리스 빌드에 남아 있으면 Steam 우회 실행으로 이어질 수 있어 OZero가 위반으로 처리합니다. 배포 전에 프로젝트 루트와 빌드 출력물에서 제거하세요.
validateSteamApiDllHashboolfalse선택·고급, 기본 꺼짐(모든 프리셋). 켜면 Steam API 파일 해시를 expectedSteamApiDllSha256Hashes 목록과 비교해 목록에 있는 파일만 허용합니다. 목록이 비어 있으면 빌드가 실패하고, Steamworks SDK를 올릴 때마다 목록을 갱신해야 합니다. 대부분의 게임은 꺼 둔 채로 두면 됩니다.
allowFamilySharing / allowFreeWeekend / allowTimedTrialbooltrueSteam 가족 공유, 무료 주말, 시간 제한 체험 권한 상태를 허용할지 제어합니다. 참고: 현재 SDK release 빌드에서는 이 정책 검사가 적용되지 않습니다. 값을 설정해 두어도 오류는 나지 않습니다.
requireSteamBuildIdNonZeroboolfalseSteam이 Build ID를 0으로 보내거나 Build ID를 확인할 수 없으면 위반으로 처리합니다. Steam depot/build 배포 흐름에서 Build ID가 정상적으로 잡히는 것을 확인한 뒤 켜세요.
expectedSteamApiDllSha256HashesList<string>—선택·고급. validateSteamApiDllHash를 켰을 때만 쓰는 Steam API 파일의 허용 SHA-256 목록이며, 꺼져 있으면(기본값) 쓰이지 않습니다. 켰다면 배포하는 Steamworks SDK 버전과 플랫폼별 해시를 넣고, SDK를 올릴 때마다 갱신하세요.
requireValveSignedSteamApibooltrueSteam API 재배포 파일이 Valve 플랫폼 신원과 일치하는지 확인합니다. Windows는 코드 서명자를, macOS는 Valve Team ID를 확인합니다. Linux처럼 서명을 확인할 수 없는 플랫폼에서는 경고만 남기고 통과하며, 이때도 Build Integrity가 빌드 무결성 정보에 들어간 steam_api 해시로 교체를 탐지합니다. SHA-256 목록으로 더 고정하려면 고급 항목 validateSteamApiDllHash를 쓸 수 있습니다.
detectKnownSteamEmulatorsbooltrue실행 파일 주변에서 Goldberg, CreamAPI, SmartSteamEmu, ColdClientLoader 같은 Steam 에뮬레이터가 남기는 파일이나 폴더를 찾습니다. 발견되면 정상 Steam 실행이 아닐 가능성이 있는 것으로 봅니다.
requireSteamEnvironmentConsistencybooltrue릴리스 빌드에서 Steam이 정상적인 개인 SteamID와 0이 아닌 Build ID를 돌려주는지 확인합니다. Steam 클라이언트/라이브러리 경로 사전 점검은 경고 로그로 남기고, SteamID나 Build ID가 잘못되면 위반으로 처리합니다.
observeSteamAuthTicketHeuristicbooltrue로컬 Steam 인증 티켓의 크기와 기본 형식만 참고 신호로 확인합니다. 티켓 내용은 로그로 남기지 않으며, 이 검사 하나만으로 게임을 차단하지 않습니다.
detectionActionOZeroPCStoreDetectionActionCallbackSteam 검증이 실패했을 때 SDK가 로컬에서 어느 단계까지 처리할지 정합니다. Observe는 로그와 진단만 남기고, Callback은 게임 콜백을 호출하며, Block은 위반을 차단 정책으로 올립니다. 실제 앱 종료 여부는 Global Threat Response 설정을 따릅니다.
reportViolationToCallbackbooltrueCallback 모드에서 Steam 검증이 실패했을 때 일반 OZero 보안 콜백으로 전달할지 정합니다. 로그만 보고 게임 콜백은 막고 싶은 QA 빌드에서만 끄세요.
forceSteamAntiPiracyObserveOnlyboolfalseSteam 검증 실패를 임시로 로그와 진단 기록만 남기는 관찰 모드로 처리합니다. 특이한 Steam 실행 환경을 테스트할 때만 사용하고, 릴리스 전에는 다시 끄세요.
Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Check Steam DRM With Server (Pro)boolfalsePro 전용입니다. 서버 근거를 사용한 Steam DRM 검증을 켭니다. 로컬 Steamworks 상태만 보지 않고 Steam 소유권을 서버 검증으로 확인해야 할 때 사용하세요.
Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Steam Drm Verification Mode (Pro)OZeroPCStoreDrmVerificationModeOZero ManagedSteam DRM 검증 주체를 선택합니다. OZeroManaged는 SDK가 OZero /v1/steam/attest를 직접 호출하고, activation cache를 관리하며, optional OZero TMP dialog package를 import한 뒤 사용자 표시 상태를 기본 Managed Verification UI로 전달합니다. CustomerGameServer는 고객 서버가 OZA 토큰과 Steam 티켓을 받아 OZero 서버 API를 호출하는 구조입니다.
Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Steam Drm Network Policy (Pro)OZeroPCStoreDrmNetworkPolicyRequireOnlineRevalidation최신 서버 재검증이 필요한 시점에 네트워크 또는 서버를 사용할 수 없으면 온라인 필요 상태로 처리합니다. OZeroManaged와 import된 기본 UI package를 함께 쓰면 재시도 흐름을 자동으로 표시합니다.
Standard Steam Anti-Piracy 출시 빌드는 Unity가 지원하는 대상에서 IL2CPP를 사용하고, Build Integrity를 Standard 또는 Strict 프리셋으로 켠 상태를 유지하세요. manifest trust-anchor fingerprint가 고정되어 있는지 확인하고, Steamworks SDK 파일을 교체할 때마다 Tools > OZero Security > Setup > PC Store > Scan Redistributable을 다시 실행하세요. unsupported accessor, 비표준 library path, verification-unavailable 같은 compatibility 진단은 단독 불법복제 증거가 아니라 먼저 QA 신호로 다루세요.

Steam 보호 집행 수준

Steam 위반이 게임 종료로 이어지는 것은 Detection Action이 Callback 또는 Block이고 Response > Force Quit On Detection이 켜져 있을 때입니다. 이 집행 수준은 빌드할 때 boot 메타데이터에 봉인되고, 종료는 네이티브 코드가 직접 집행합니다. 빌드 출력의 봉인 값이 보안 설정과 다르면 빌드 preflight가 실패하니 다시 빌드하세요.

Observe·Off이거나 Force Quit On Detection이 꺼져 있거나, forceSteamAntiPiracyObserveOnly가 켜져 있거나, requireSteamApiInit이 꺼져 있거나, Low 프리셋이면 위반은 기록만 되고 게임은 종료되지 않습니다. 이런 구성도 허용되지만 Check Setup의 Steam enforcement 항목에 경고로 표시되고, 빌드할 때 콘솔에도 경고가 남습니다. 종료가 일어나는 구성이면 같은 항목에 "terminates on detection"으로 표시됩니다.

온라인 애드온에서 Check Steam DRM With Server로 서버가 Steam 소유를 판정하는 구성은 네이티브 종료 대신 서버 판정 흐름을 따르며, 로컬 Steam 위반은 관찰로 기록됩니다.

Steam Activation / DRM이 OZeroManaged를 사용하면 보호 대상 첫 실행은 OZero 서버를 통해 유효한 activation token을 온라인에서 받습니다. 이후에는 유효한 cache token과 오프라인 유예 시간이 남아 있으면 오프라인 실행을 허용할 수 있습니다. 토큰이 만료되었거나 유예 시간이 끝났거나 재검증이 필요한 시점에 사용자가 오프라인이라면 OZeroSecurity_BuiltInDialog_TMP.unitypackage를 import한 기본 Managed Verification UI가 온라인 필요 안내, Retry 버튼, retry timeout, 차단 상태를 자동으로 처리합니다.

OZero 서버 직접 Steam DRM 인증

Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Check Steam DRM With Server를 켜고, Steam Drm Verification Mode를 OZeroManaged로 설정한 뒤, OZeroSecurity_BuiltInDialog_TMP.unitypackage를 import하고 Security Config > Pro - License & Online Services > Online Verification UI > Ui Policy를 기본 다이얼로그로 유지합니다. SDK가 Steam 인증 티켓을 OZero 서버로 보내고 activation token cache를 관리하며, 온라인 필요, 재시도, timeout, 경고, 차단 상태를 기본 UI로 표시합니다. 기본 UI를 그대로 쓰면 callback이나 retry 코드를 작성할 필요가 없습니다. Pro 활성화 자체가 거부되면(예: Product Name이 등록되지 않음) Steam 메시지가 Pro activation was not granted … Cause: <활성화 오류 코드>로 원인을 보여 줍니다. 그 활성화 오류부터 해결하세요(라이선스 오류 해결 참고).

고객 서버에서 Steam 인증

Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Check Steam DRM With Server를 켜고 Steam Drm Verification Mode를 CustomerGameServer로 설정합니다. 고객 서버가 audience, 256-bit hex challenge, session ID를 발급하고, 로컬 검증 후 클라이언트가 RequestGameServerAttestation(...)을 호출해 callback의 OZA 토큰·동일 binding 값·Steam Auth Ticket을 고객 서버로 보냅니다. 고객 서버는 /v1/validate와 /v1/steam/attest를 호출하며 두 서버 API Key는 서버에만 보관합니다.

디바이스 바인딩 설정

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

운영에서 사용하는 방식

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

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

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

정상적인 기기 변경 지원

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

게임에서 바인딩 해제하기

플레이어가 정당하게 기기를 바꿨다면 게임에서 RequestReset으로 저장된 지문을 지울 수 있습니다. 플레이어 빌드에서는 OZeroDeviceBindingDetector.SetResetAuthorizer로 등록한 인가 콜백에 묻고, 콜백이 true를 돌려줄 때만 해제합니다. 해제한 뒤에는 같은 세션에서 현재 기기를 곧바로 다시 등록하므로 재시작할 필요가 없습니다.

using OZeroSDK.Security;

// Register once at startup.
OZeroDeviceBindingDetector.SetResetAuthorizer(request =>
{
    // Approve only after your own check succeeds, for example a recent
    // account re-authentication or a support code your server verified.
    return MyAccountService.HasVerifiedResetFor(request.Reason);
});

// Later, from your account recovery or support screen:
bool cleared = OZeroDeviceBindingDetector.Instance?.RequestReset("ticket-1234") ?? false;
모든 요청을 승인하지 마세요. 항상 true를 돌려주는 콜백은 프로세스 안의 어떤 코드든(주입된 코드 포함) 바인딩을 지울 수 있게 만듭니다. 계정 재인증이나 서버가 확인한 지원 코드처럼 실제 확인을 거친 뒤에만 true를 돌려주세요. Demo_DeviceBinding과 Demo_OZeroSecurity 샘플은 시연을 위해 모든 요청을 승인합니다. 그 콜백을 게임에 그대로 옮기지 마세요.

플레이어 빌드에서 인가 콜백도 없고 유효한 서버 Reset Token도 없으면 해제를 거부하고 오류 로그를 남깁니다. 에디터에서는 항상 해제됩니다. RequestReset과 인가 콜백은 호출한 스레드에서 동기로 실행됩니다. ClearStoredFingerprint(token)도 계속 동작합니다. Pro 라이선스와 서버 설정(서버 URL, 라이선스 키)이 있으면 비어 있지 않은 토큰을 백그라운드에서 서버 Reset Token으로 검증하므로, 해제는 호출이 반환된 뒤에 일어납니다. 결과가 필요하면 메인 스레드에서 ClearStoredFingerprintAsync(token)을 await하세요(아래 흐름). 그 밖의 경우에는 인가 콜백이 판단하며 토큰은 request.AuthorizationToken으로 전달됩니다.

Standard 게임의 CS 처리 가이드

Standard에는 OZero 서버가 없어서 포털 Reset Token을 쓸 수 없습니다. 대신 게임의 CS 확인 절차를 인가 콜백(SetResetAuthorizer)에 직접 연결하세요. 아래 방법 중 하나를 고르되, 어떤 방법이든 위 경고처럼 확인이 통과한 뒤에만 콜백에서 true를 돌려주세요.

A. 게임 자체 서버(계정 시스템)가 있는 경우 — 권장

  1. CS가 백오피스에서 플레이어 계정에 1회용 지원 코드를 발급합니다.
  2. 플레이어가 게임 화면에 코드를 입력합니다.
  3. 게임이 자체 서버에서 코드를 먼저 비동기로 검증합니다.
  4. 통과하면 게임이 RequestReset을 호출하고, 콜백은 방금 검증이 통과했는지만 확인합니다.

플레이어가 다시 로그인(플랫폼 재인증)하면 해제를 허용하는 방식도 같은 구조입니다. 인가 콜백은 동기로 실행되므로 비동기 검증을 먼저 끝낸 뒤 RequestReset을 호출하세요.

bool _supportCodeVerified;

void Start()
{
    // The authorizer runs synchronously: it only reports whether the check just passed.
    OZeroDeviceBindingDetector.SetResetAuthorizer(_ => _supportCodeVerified);
}

async void OnSubmitSupportCode(string code)
{
    // Verify the one-time code issued by your support team on your own backend.
    _supportCodeVerified = await MyBackend.VerifyResetCodeAsync(accountId, code);
    try
    {
        bool cleared = OZeroDeviceBindingDetector.Instance.RequestReset("cs-support");
        ShowResetResult(cleared);
    }
    finally
    {
        _supportCodeVerified = false;
    }
}

B. 서버가 없는 오프라인 게임 — 서명 코드 방식

  1. 게임이 요청 코드를 보여 줍니다. 예를 들어 1회용 난수와 게임 자체의 세이브·플레이어 ID를 짧게 인코딩한 값입니다.
  2. CS가 개발사만 가진 개인키로 서명한 응답 코드를 돌려줍니다.
  3. 게임은 내장된 공개키로 응답을 검증하고, 서명이 유효할 때만 콜백이 해제를 허용합니다. 같은 응답을 다시 쓰지 못하도록 사용한 난수는 버리세요.
게임에 비밀값을 넣는 HMAC 방식은 빌드에서 비밀값을 추출할 수 있어 권장하지 않습니다. Ed25519나 ECDSA 같은 공개키 서명을 쓰세요. 서명 도구와 키 관리는 개발사 책임입니다. 요청 코드에 GetCurrentFingerprintHash()를 넣지 마세요. 디버그용 도우미라 출시 코드에서 화면에 보여 주거나 전송하면 안 됩니다.
// Pseudocode. Game: build a request code that the player sends to support.
nonce        = RandomBytes(8)          // keep until the reset is done, then discard
requestCode  = ShortEncode(nonce + yourSaveOrPlayerId)

// Support tool on your side. The private key never ships with the game.
responseCode = Sign(studioPrivateKey, requestCode)          // Ed25519 or ECDSA

// Game: verify with the public key built into the game, then reset.
OZeroDeviceBindingDetector.SetResetAuthorizer(_ => _signedCodeVerified)
_signedCodeVerified = Verify(embeddedPublicKey, requestCode, responseCode)
OZeroDeviceBindingDetector.Instance.RequestReset("cs-offline")
_signedCodeVerified = false
Discard(nonce)

C. 앱 데이터 삭제 또는 재설치

바인딩 기록은 앱 로컬 저장소(PlayerPrefs)에 있습니다. 플레이어가 앱 데이터를 지우거나 앱을 재설치하면 다음 실행 때 새 기기로 등록됩니다. 로컬 세이브도 사라질 수 있으니 서버·클라우드 세이브가 있는 게임에서만 CS 답변으로 쓰세요.

D. 문의 줄이기

hardwareChangeTolerance를 1 이상으로 두면(Standard 프리셋은 1, Strict는 0) OS 업데이트나 개인정보 설정 변경으로 식별값 하나가 바뀌어도 차단하지 않고 기기를 다시 등록합니다.

Pro: 포털 Reset Token과 ClearStoredFingerprintAsync(token)을 쓰세요. 아래 Reset Token 지원 흐름을 참고하세요.

Reset Token 지원 흐름 (Pro)

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

Speed & Time Hack 설정

Unity 시간, 네이티브 기준 시계, 백그라운드 타이머, 웹/Pro 서버 시간을 서로 비교해 게임 속도를 빠르게 또는 느리게 바꾸는 조작과 기기 시계 변경을 감지합니다. WebGL에서는 네이티브 시계 비교를 쓸 수 없습니다. 시작 로그에 이 내용이 표시되며, 브라우저 시계 변조(Date.now / performance.now)는 Injection & Hooking의 WebGL 런타임 검사가 대신 탐지합니다.

필드 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 시간 이상 징후를 지켜보는 동안 웹/서버 시간과 다시 비교하는 주기입니다. 너무 짧게 잡으면 네트워크 요청이 늘어납니다.
webSyncIdleInterval float 60 s 최근 시간 이상 징후가 없을 때의 웹 시간 비교 주기입니다. 로컬 시간 이상 징후나 웹 시간 확인 실패 뒤 5분 동안은 webSyncInterval을 씁니다. 0이면 항상 webSyncInterval을 씁니다. Strict preset은 15초입니다.
webRatioTolerance float 0.15 기기 시간과 웹/서버 시간이 이 비율만큼 차이 나는 것은 정상으로 봅니다. 지역별 네트워크 지연을 고려해 실제 기기에서 확인하세요.
timeOffsetTolerance float 60 s 실행 중 기기 시계가 이 초 단위보다 크게 뒤로 가거나, 웹/서버 시간과의 차이가 첫 측정 이후 이만큼 달라지면 시간 조작으로 봅니다. 처음부터 틀려 있던 기기 시계는 조작으로 보지 않습니다. 판정 기준은 실행 중인 프로세스 단위라, 시계를 바꾸려고 앱이 다시 시작되는 모바일(예: iOS 설정 앱)이나 -batchmode 헤드리스 플레이어에서는 로컬 역행 판정이 동작하지 않습니다. 시간 기반 보상은 서버 시간(OZeroTime 서명 시간)을 기준으로 처리하세요.
focusIgnoreTime float 4 s 앱이 백그라운드에서 돌아온 직후 이 시간 동안 감지 결과를 무시합니다. OS가 앱을 멈췄다가 재개할 때 생기는 오탐을 줄입니다.
loadingGraceTime float 6 s 앱 시작이나 씬 로딩 직후 이 시간 동안 감지 결과를 무시합니다. 무거운 로딩 구간의 오탐을 줄입니다.
lagSpikeIgnore float 0.5 s 프레임 시간이 이 값을 넘는 샘플은 지연으로 보고 버립니다. 실제 렉을 시간 조작으로 오해하지 않기 위한 설정입니다.
buildFailIfTimeScaleTamperedbooltrue보호 대상 코드가 정책 밖에서 Time.timeScale을 직접 바꾸는 것으로 보이면 빌드/검증 단계에서 실패하게 합니다.
timeScaleTamperExemptionsList<string>—Time.timeScale을 직접 바꿔도 되는 스크립트/메서드 이름 패턴입니다. 일시정지, 불릿타임, 컷신처럼 이미 검토한 코드만 최소한으로 등록하세요.
webTimeUrlsstring[]—웹/서버 시간 확인에 사용할 HTTPS 주소 목록입니다. 하나의 주소가 막혀도 검사가 멈추지 않도록, 직접 운영하거나 신뢰할 수 있는 주소를 2개 이상 넣는 것을 권장합니다. Standard와 Strict 프리셋은 제3자 호스트인 https://www.apple.com/과 https://www.bing.com/을 채웁니다(OZero 서버 아님).
minSuccessfulEndpointsint2한 번 검사할 때 최소 몇 개의 주소가 정상적인 Date 헤더를 돌려줘야 시간을 신뢰할지 정합니다. 등록한 주소 수보다 크게 잡지 마세요.
maxConsecutiveFailuresint6웹/서버 시간 확인이 연속으로 이 횟수만큼 실패하면 onWebTimeUnavailable 정책을 실행합니다. 일시적인 네트워크 오류까지 바로 위반으로 보지 않기 위한 완충값입니다.
onWebTimeUnavailableWebTimeUnavailablePolicyWarnOnly웹/서버 시간을 계속 확인하지 못할 때 어떻게 처리할지 정합니다. WarnOnly는 경고만 남기고 계속 실행합니다. Strict는 반복 실패를 의심 상황으로 보고 SpeedHack 콜백을 발생시킵니다. Silent는 로그도 남기지 않으므로 특수 테스트가 아니면 사용하지 마세요.
webSyncJitterPercentfloat20%웹/서버 시간 확인 시간이 모든 기기에서 한꺼번에 겹치지 않도록, 설정한 주기 주변에서 호출 시점을 조금씩 분산합니다. 예: 20%이면 15초 주기가 대략 12~18초 사이에서 실행됩니다.
sustainedLagThresholdfloat0.15최근 프레임 평균이 이 값보다 느리면 SDK는 먼저 실제 렉 상황으로 보고, 그동안의 시간 검사 결과를 바로 위반으로 확정하지 않습니다. 기본 0.15초는 평균 약 6~7 FPS 수준입니다.
sustainedLagGraceDurationfloat3 s렉으로 판단된 뒤 시간 검사를 잠시 보류하는 시간입니다. 렉이 이어지는 동안에는 이 보류 시간이 계속 연장되고, 런타임에서는 최대 10초 안에서 제한됩니다.
overloadStrictMultiplierint3onWebTimeUnavailable이 Strict일 때, 실패가 서버의 명확한 거부가 아니라 단순 타임아웃처럼 보이면 몇 번 더 기다릴지 정하는 배수입니다. 기본 3은 maxConsecutiveFailures의 3배까지 기다린 뒤 콜백을 발생시킨다는 뜻입니다.
Security Config > Speed & Time Hack > Pro - Online > Enable Remote Speed Hack Config (Pro)booltruePro에서 서버의 Speed & Time Hack 설정을 받아 적용합니다. 새 설정의 기본값은 true이며, 보안 프리셋은 라이선스 등급이 Pro일 때만 켜고 Standard에서는 끕니다. 포털에서 값을 지정한 항목만 로컬 값을 덮으며, 임계값뿐 아니라 detectSlowHack·enableTimeScaleDetection·enableThreadTimerCheck·enableSignedServerTime 같은 탐지 켜기/끄기도 포함됩니다. 포털에서 비워 둔 항목은 로컬 값을 유지합니다. 끄거나 서버에 연결할 수 없으면 로컬 설정만 사용합니다.
remoteSpeedHackConfigIntervalfloat300 sPro 서버에서 Speed & Time Hack 원격 설정을 다시 가져오는 주기입니다. 0이면 앱 시작 시 한 번만 가져오고, 양수이면 주기적으로 새 정책을 확인합니다.
remoteSpeedHackConfigJitterPercentfloat20%Pro 원격 설정을 여러 기기가 동시에 요청하지 않도록, 설정 갱신 시간을 조금씩 분산합니다.
Security Config > Speed & Time Hack > Pro - Online > Enable Signed Server Time (Pro)boolfalsePro 활성화가 가능할 때 서명된 /v1/time 응답을 우선 신뢰 시간으로 사용합니다. 서버 키와 시간 엔드포인트를 설정하고 테스트한 뒤 켜세요.
웹 시간 검증: Speed & Time Hack의 웹 시간 검증(useWebTimeValidation)을 켜면 SDK가 webTimeUrls의 주소로 HTTPS HEAD 요청을 보내고, 시간 비교를 위해 Date 헤더만 읽습니다. Standard와 Strict 프리셋은 제3자 호스트인 https://www.apple.com/과 https://www.bing.com/을 채웁니다. 이 호스트는 OZero 서버가 아니며, OZero는 요청에 개인 데이터를 넣지 않습니다. 게임이 다른 호스트에 접속하면 안 된다면 직접 운영하는 주소로 바꾸거나 웹 시간 검증을 끄세요.

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 > Diagnostics > Check Time.timeScale Usage를 실행하고, 가능한 모든 직접 수정 코드를 OZeroTime.timeScale로 교체하세요.

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

OZero의 네이티브 Watchdog은 Unity 메인 스레드가 계속 실행되는지 주기적인 heartbeat로 확인합니다. 릴리스 빌드의 heartbeat 한도는 모든 플랫폼에서 약 10초입니다(시작 유예 이후). 한도가 지나면 Watchdog은 먼저 메인 스레드가 무엇을 하고 있는지 봅니다. 스레드가 막혀 CPU를 쓰지 않는 상태(긴 동기 로딩, 로그 flush, 디스크 I/O, 가상 머신의 일시 정지)는 stall로 보고 기록만 남기며, 게임이 계속 돌아가는데 heartbeat만 오지 않는 경우는 변조로 보고 설정된 대응 정책에 따라 종료합니다. 5분 넘게 멈춘 프로세스는 응답 없음으로 종료합니다.

대형 씬 로딩, 동기식 에셋 압축 해제, 셰이더 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 원격 설정 다운로드, 텔레메트리 전송을 설정합니다. 이동 속도, 거리 허용치, 벽 통과 검사, 가속도 검사는 각 OZeroPhysicsHackDetector 컴포넌트에서 설정합니다. 플레이어 프리팹에 컴포넌트를 붙이고, 캐릭터나 차량의 실제 이동 규칙에 맞게 값을 조정한 뒤, 스폰 로직에서 Initialize(playerId)를 호출하세요.
필드 Type 기본값 설명
physicsHack.useGlobalPhysicsHackbooltrue모든 Physics Hack 컴포넌트에 적용되는 전체 켜기/끄기 스위치입니다. 모든 물리 이동 검사를 의도적으로 멈춰야 할 때만 끄세요.
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 수를 제한합니다. 기준값을 잘못 잡았을 때 서버로 이벤트가 과도하게 몰리는 것을 막습니다.
필드 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 메모리, 인라인 후킹, 원격 스레드형 주입, 고위험 프로세스 핸들, WebGL 런타임 변조가 대상입니다. 정상 오버레이나 캡처 도구도 같은 방식으로 게임에 들어오므로, 탐지기는 모든 모듈을 파일 해시와 서명자로 식별하고 모듈별로 판정할 수 있게 합니다.

기본 설정으로 시작하세요.
정상 오버레이, 녹화 프로그램, 파트너 SDK, 우리 게임에 포함된 모듈이 탐지된 경우에만 허용하세요. 먼저 Failure Diagnostics나 Pro 포털에서 모듈 이름, 파일 해시, 서명자를 확인하세요. 자주 보인다는 이유만으로 허용하지 마세요. 치트 도구도 흔합니다.
필드 Type 기본값 설명
Activate Injection & HookingcheckboxOnConfig Dashboard의 Injection & Hooking 켜기/끄기 항목입니다. 내부적으로는 useInjection에 연결됩니다. 플레이어 빌드에서는 네이티브 Injection 검사를 실행하고, 개발 빌드에서는 원인 확인을 위해 완화된 진단 흐름으로 동작합니다. 에디터 Play Mode에서는 Editor Play Mode Protection이 Test일 때만 검사합니다.
injectionWhitelistEntriesOZeroInjectionWhitelistEntry[]empty모든 티어에서 사용할 수 있는 로컬 신뢰 모듈 목록입니다. 각 항목은 모듈 파일의 SHA-256 hash와 선택 signer fingerprint로 특정 파일 하나를 허용합니다. 게임과 함께 배포하거나 QA, 진단 파일, Pro telemetry, OZero 지원팀 안내로 검증한 모듈만 등록하세요.
Security Config > Injection & Hooking > Pro - Online > Enable Server Whitelist (Pro)boolfalsePro에서 서명된 모듈 허용·차단 목록을 받습니다. enableServerWhitelist 하나로 수신을 제어하며, 승인 목록과 차단 목록의 유효기간은 각각 관리됩니다. 허용된 모듈은 무음 처리하고 명시적으로 차단한 모듈에는 차단 정책을 적용합니다. 검증된 목록만 캐시에 저장하며, 오프라인에서는 유효기간이 남은 결정만 사용합니다. 기본값은 false입니다.
Security Config > Injection & Hooking > Pro - Online > Enable Injection Scoped Exact (Pro)boolfalsePro에서 관측된 모듈을 전체 목록을 받는 대신 해시로 하나씩 서버에 조회합니다. 아직 제공되지 않습니다: OZero 서버에서 이 조회 기능이 켜져 있지 않아, 켜도 효과가 없고 SDK는 기존 서버 화이트리스트를 그대로 사용합니다. 꺼 두세요.
Security Config > Injection & Hooking > Pro - Online > Enable Injection Bloom Advisory (Pro)boolfalse서명된 전역 모듈 인덱스로 조회 우선순위를 정합니다(참고용). 아직 제공되지 않습니다: 전역 인덱스 루트 키가 공개되지 않아, enableInjectionScopedExact와 함께 켜면 root_unconfigured로 빌드가 실패합니다. 꺼 두세요.
serverWhitelistRefreshIntervalfloat0 s실행 중 서버 목록을 정기적으로 다시 받는 간격입니다. 0이면 정기 조회만 중지하며, 시작 시 수신과 목록 만료 전 자동 갱신은 계속 수행합니다.
requireSignerForNativeWhitelistboolfalseWindows PE와 Apple Mach-O whitelist 항목에 파일 hash뿐 아니라 signer fingerprint도 요구합니다. 더 강한 방식이지만 정확한 signer 값을 수집할 수 있을 때만 켜세요. Android/Linux 공유 라이브러리는 보통 hash-only로 처리합니다.
Security Config > Injection & Hooking > Pro - Online > Enable Remote Injection Config (Pro)booltruePro에서 서버의 Injection 설정을 받아 적용합니다. 새 설정의 기본값은 true이며, 보안 프리셋은 라이선스 등급이 Pro일 때만 켜고 Standard에서는 끕니다. 서버에서 지정한 항목만 덮어쓰고, 끄면 로컬 설정만 사용합니다. 서버 화이트리스트 수신은 enableServerWhitelist로 별도 설정합니다.
remoteInjectionConfigIntervalfloat300 sPro 원격 Injection 설정을 다시 확인하는 주기입니다. 0이면 앱 시작 시 한 번만 가져옵니다.
enableWindowsModuleIdentityScanbooltrue시작 이후 나타난 모든 모듈을 파일 해시로, Windows/macOS에서는 서명자 지문으로도 식별합니다(필드 이름은 옛 이름 그대로 Windows지만 모든 네이티브 대상에 적용). iOS/macOS는 앱 번들 안의 이미지를 게임 코드로 보고, Android/Linux는 공유 라이브러리를 해시로 식별합니다. 모듈 식별 방식을 참고하세요. macOS는 앱과 다른 서명자로 서명된 번들 내부 이미지를 인젝션 후보로 봅니다.
enableWindowsSystemModuleScanbooltrueWindows의 System32, SysWOW64, WinSxS에서 게임에 로드된 모듈을 검사합니다. 켜면 시스템 폴더에 있다는 이유만으로 모듈이나 후킹 대상을 신뢰하지 않습니다. Low: false, Standard/Strict: true. 모듈 식별에는 enableWindowsModuleIdentityScan도 켜져 있어야 합니다. 인라인 후킹 검사는 별도 옵션으로 설정합니다.
scanIntervalSecondsfloat2 sInjection 검사를 실행하는 기본 주기입니다. 값을 낮추면 더 빨리 감지하지만 런타임 비용이 늘 수 있습니다. 실제 적용값은 안전한 범위로 제한됩니다.
windowsModuleIdentityScanIntervalSecondsfloat5 s상대적으로 무거운 네이티브 모듈 식별 검사의 별도 주기입니다. 모듈 열거, hash 계산, signer 확인이 대상 기기에서 부담된다면 이 값을 늘려보세요.
scanJitterPercentfloat20%주기 검사 시간이 항상 정확히 같은 초에 실행되지 않도록, 설정한 주기 주변에서 실행 시점을 조금 분산합니다.
enableExecutablePrivateMemoryScanbooltrue정상 모듈로 로드되지 않은 실행 가능 private 메모리를 탐지합니다. 셸코드형 페이로드를 잡지만, 실행 가능 영역만으로는 증거를 누적할 뿐 단독으로 게임을 종료하지 않습니다. Mono 스크립팅 백엔드 빌드에서는 모든 플랫폼에서 자동으로 꺼집니다. Mono JIT가 바로 이런 메모리를 만들기 때문입니다.
enableInlineHookScanboolfalse중요 네이티브 API 진입점이 패치된 흔적이 있는지 확인합니다. 강한 신호지만 overlay나 보안 소프트웨어도 API를 hook할 수 있으므로, Strict 성격의 빌드에서 호환성 테스트 후 켜는 것을 권장합니다.
enableThreadStartAddressScanbooltrue네이티브 thread가 신뢰 모듈 밖의 의심스러운 메모리에서 시작했는지 확인합니다. 지원 플랫폼에서 remote-thread 방식의 주입을 잡는 데 도움이 됩니다.
enableExternalProcessHandleScanbooltrue디버거, 메모리 에디터처럼 외부 프로세스가 게임 프로세스에 위험한 handle을 들고 있는지 확인합니다. 주로 Windows에서 사용됩니다.
detectionConfidenceThresholdint70강한 Injection 신호를 즉시 위반으로 처리하는 최소 신뢰도입니다. 그보다 낮은 신호는 연속·구간 판정으로 계속 다룹니다. 60~100으로 제한되며, 60 아래로 내리면 프로세스 안의 모든 JIT 엔진(WebView, Lua, Mono)이 위반처럼 보입니다.
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 검사를 각각 켜고 끄는 스위치입니다.

처음 보는 시스템 모듈이라고 해서 바로 게임을 종료하지는 않습니다. 우선 관측 대상으로 기록하며, 반복해서 발견되거나 파일에 서명이 없다는 이유만으로 차단하지 않습니다. 기존 허용 규칙은 유지되고, 해시로 명시적으로 차단한 모듈에는 차단 정책이 적용됩니다. 이 옵션은 Unity의 Injection 설정에서 조정할 수 있으며, Pro 포털에서는 원격으로 변경할 수 없습니다. 다른 플랫폼에는 영향을 주지 않습니다.

탐지기가 모듈을 식별하는 방식

탐지기는 몇 초마다 게임 프로세스에 로드된 모듈 전체를 훑어 시작 시점의 목록과 비교합니다. 새 모듈은 파일 해시로, Windows·macOS에서는 서명 인증서 지문으로도 식별합니다. 규칙은 모듈 자체만 보고 설치 폴더 이름은 보지 않으므로, 정상 프로그램이 설치 경로 때문에 오탐되는 일은 없습니다.

플랫폼 게임 자체 코드로 보는 기준
Windows게임 자체 native 모듈은 폴더 위치가 아닌 서명된 빌드 무결성 해시 목록으로 확인합니다. enableWindowsSystemModuleScan=false이면 System32·SysWOW64·WinSxS의 모듈은 식별 검사에서 제외합니다. 켜면 게임 시작부터 로드되어 있던 시스템 모듈도 검사합니다. 알 수 없는 시스템 모듈은 우선 관측 대상으로 기록하되, 해시로 명시적으로 차단한 모듈에는 차단 정책을 적용합니다. 기존 해시·서명자 허용 규칙과 검증된 벤더 모듈의 무음 처리는 유지됩니다. 게임 폴더의 서명 목록에 없는 native 파일은 시작 시 Build Integrity 위반으로 처리합니다.
iOS / macOS앱 자신의 .app 번들에 속한 이미지와 Apple 플랫폼 이미지입니다. 빌드 시 별도 산출물은 필요 없습니다. iOS 14.0 이상이 필요하며 그보다 낮으면 Editor preflight가 실패합니다. 단, macOS에서는 앱과 같은 서명자(GameAssembly.dylib·UnityPlayer.dylib의 서명)로 서명된 번들 내부 이미지만 게임 코드로 봅니다. ad-hoc·미서명이거나 다른 서명자의 이미지는 번들 안에 있어도 인젝션 후보(bundle_signer_mismatch)로 처리하며, Valve 서명 libsteam_api.dylib는 예외입니다. 앱 자체가 ad-hoc 서명이면 비교하지 않습니다.
Android / Linux시작 시점의 공유 라이브러리 중 런타임과 같은 규칙을 통과한 것, 시스템·엔진 런타임 라이브러리, Steam 오버레이, 그리고 앱 자신의 라이브러리입니다. Linux는 빌드가 서명한 인벤토리로(폴더 위치는 보지 않음), Android는 OS만 쓸 수 있는 설치 패키지 디렉터리로 앱 자신의 라이브러리를 알아봅니다. Android 시스템 WebView 제공 앱도 플랫폼 구성요소로 봅니다. 어떤 파일에도 속하지 않는 실행 가능 메모리도 같은 검사에서 보고되지만 누적 판정만 하며 단독으로 종료하지 않습니다. Linux 호스트 계층과 Proton은 위 Build Integrity 정책(기본 Monitor)을 따릅니다.

검사는 탐지 계열마다 탐지, 검사 후 정상, 아직 검사 시점 아님 세 가지 중 하나로 답합니다. 실제 표본만 누적치를 바꾸며, 실행되지 않은 검사는 이전 탐지를 지우지 않고, 아예 실행할 수 없었던 검사는 정상이 아니라 스캐너 상태 문제로 보고됩니다.

확정 탐지 뒤의 동작. Force Quit On Detection이 켜져 있으면(Standard·Strict 프리셋 기본값) 확정된 주입 모듈은 콜백 유예 시간이 지난 뒤 게임을 종료합니다. OZeroSecurityReceiver나 콜백에서 로그만 남겨도 마찬가지이며, Windows에서는 네이티브 모듈 감시가 직접 종료합니다. 인젝션을 게임 코드에서만 처리하려면 Force Quit On Detection을 끄고(Low 프리셋) Receiver에서 대응하세요.

Pro 빌드. Pro 빌드에 서명 없는 모듈이 주입되면 라이선스 서버가 다음 온라인 확인(약 4분 주기)을 거부합니다. 이 거부는 Build Integrity 실패라서 Force Quit On Detection 설정이나 콜백과 관계없이 항상 게임을 종료합니다. 정상 모듈이면 포털에서 허용하세요.

Pro: 포털에서 관측 모듈 허용/차단

enableServerWhitelist를 켜면 Pro 빌드는 포털의 프로젝트별 결정과 OZero의 전역 결정을 합친 서명 목록을 받습니다. 시작 시 수신하고, 설정한 주기로 다시 받으며, 승인·차단 목록이 만료되기 전에도 자동으로 갱신합니다.

판정 게임의 동작
Allow해당 모듈(해시+서명자 일치)이 네이티브 신뢰 목록에 올라가 프로젝트의 모든 기기에서 위협으로 취급되지 않습니다.
Block이 모듈이 로드된 게임은 반복 탐지를 기다리지 않고 즉시 종료됩니다. 치트 도구가 확실한 경우에만 쓰세요.
Watch게임에 전달되지 않습니다. 기록만 계속 쌓여 나중에 더 많은 근거로 결정할 수 있습니다.

스캐너 사용 불가 (abort code 0x15)

네이티브 스캐너 자체가 동작할 수 없으면(설정 적용 실패, 반복되는 네이티브 스캔 오류, managed/native 계약 불일치) SDK는 ModulationType.InjectionScannerUnavailable(값 10, abort code 0x15, message key injection_scanner_unavailable)을 보고하고 ForceQuitOnDetection이 꺼져 있어도 게임을 종료합니다. 공격 탐지가 아니라, 조용히 고장 난 스캐너가 게임을 무방비로 두는 것을 막기 위한 동작입니다. 플레이어에게는 OZ-SEC-15 같은 지원 코드를 보여 주고 로그에는 abort code를 남기세요.

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을 변경하지 마세요. developerSecret은 OZeroSafePlayerPrefs와 OZeroSV_File 데이터를 보호하는 암호화 키의 기준값입니다. 출시 후 이 값을 바꾸면 기존 버전에서 저장한 보호 데이터를 새 버전에서 읽을 수 없습니다. OZeroSV_File은 키 변경을 감지해 null을 돌려주고 OZeroSV_File.OnKeyMismatch 이벤트를 발생시키며(보안 위반으로 처리하지 않음), OZeroSafePlayerPrefs는 기본값을 돌려줍니다. 사용자 데이터를 지키려면 게임에서 마이그레이션 또는 초기화 처리가 필요합니다.

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

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

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 레퍼런스에서 모든 클래스, 메서드, 설정 옵션을 자세히 확인해 보세요.

문제 해결 레퍼런스

OZero 다이얼로그가 표시되었거나 앱이 보안 사유로 종료된 경우, 먼저 화면이나 로그에 표시된 코드를 확인하세요. 지원 코드를 기준으로 대략적인 원인을 찾고, 필요한 경우 아래 문제 해결 순서에 따라 로그와 설정을 점검하면 됩니다.

지원 코드

지원 코드는 OZero 다이얼로그와 로그에 표시되는 짧은 확인 코드입니다. 플레이어가 이 코드를 고객 지원팀에 전달하면, 개발팀이나 QA 담당자가 어느 영역을 먼저 확인해야 하는지 빠르게 파악할 수 있습니다. OZ-SEC-*는 보안 이벤트가 보고되었음을 의미하고, OZ-MV-*는 온라인 검증이나 관리형 검증 상태를 확인해야 함을 의미합니다.

코드 분류 확인할 항목
OZ-SEC-01메모리 보호보호된 값이 지원되지 않는 경로, 테스트 도구, 또는 잘못된 Secure Types 연동으로 변경되었는지 확인합니다.
OZ-SEC-02인젝션 또는 후킹오버레이, 디버거, 모딩 도구, 외부 프로그램이 앱에 연결되어 있었는지 확인합니다. 예외 처리는 QA에서 신뢰 가능한 프로그램임을 확인한 뒤 적용하세요.
OZ-SEC-0A빌드 무결성 / 실행 환경 변조안내 라벨로 구분합니다. 빌드 무결성: 릴리스 빌드를 만든 뒤 빌드 파일, manifest, 서명 정보, native variant, 패키지 구성이 바뀌지 않았는지 확인합니다. 실행 환경 변조: 디버거, 치트·리버싱 도구 프로세스, VM·에뮬레이터, 루트·탈옥이 탐지된 경우입니다. 기기에서 무엇이 실행 중이었는지 확인합니다.
OZ-SEC-0C속도 또는 시간 변경스피드핵 도구, 기기 시간 변경, 직접적인 Time.timeScale 변경, 정상적인 일시정지/슬로모션 연출을 함께 확인합니다.
OZ-SEC-0E기기 또는 설치 정책설치 출처, 스토어 패키지 정보, 에뮬레이터/root/jailbreak 정책, 기기 바인딩 지원 절차를 확인합니다.
OZ-SEC-0F물리 검증실제 게임 규칙에 맞게 이동 한계, 충돌 레이어, dash, knockback, 차량 이동 등이 설정되어 있는지 확인합니다.
OZ-SEC-10실행 환경지원되는 기기, 브라우저, 에뮬레이터, 디버거, 플랫폼 테스트 환경에서 실행 중인지 확인합니다.
OZ-SEC-13Steam 검증Steam 실행 상태, AppID, 소유권 검증, 필요한 Steamworks 파일, 네트워크 연결을 확인합니다.
OZ-SEC-15인젭션 스캐너 사용 불가공격이 아닙니다. 네이티브 인젭션 스캐너가 동작하지 못한 상태입니다(설정 미적용, 반복 스캔 실패, managed/native 계약 불일치). SDK와 네이티브 바이너리가 같은 패키지에서 왔는지 확인하고 게임을 재설치한 뒤에도 계속되면 문의하세요.
OZ-SEC-1F알 수 없는 분류플랫폼, SDK 패키지 버전, native ABI, Player log, ozero_abort.txt, 정확한 재현 절차를 수집합니다.
OZ-MV-LOCAL관리형 검증라이선스 활성화, 서버/네트워크 연결, 등록된 앱 식별자, 플랫폼 서명 정보, 현재 적용된 variant manifest를 확인합니다.

종료 코드

종료 코드는 로그, 콜백, OZ-SEC-* 지원 코드에서 공통으로 사용하는 안정적인 분류입니다. 플레이어에게 세부 근거를 노출하지 않으면서, 개발팀이 어떤 보호 영역에서 반응했는지 확인할 수 있게 해 줍니다.

코드 분류 의미
0x01메모리 보호보호된 메모리 값이 예상하지 못하게 변경되었습니다.
0x02인젝션 또는 후킹예상하지 못한 모듈, hook 또는 런타임 injection 신호가 감지되었습니다.
0x0A빌드 무결성 / 실행 환경 변조빌드 무결성 검증에 실패했거나, 빌드 무결성의 실행 환경 검사(디버거, 치트·리버싱 도구 프로세스, VM·에뮬레이터, 루트·탈옥)가 탐지했습니다.
0x0C속도 또는 시간 변경실행 속도, time scale, 시스템 시간 또는 trusted-time 이상 신호가 감지되었습니다.
0x0E기기 또는 설치 정책Device binding 또는 install-source 정책이 현재 환경을 거부했습니다.
0x0F물리 검증비정상 물리 동작이 설정된 정책을 초과했습니다.
0x10실행 환경지원되지 않거나 안전하지 않은 런타임 환경이 감지되었습니다.
0x13Steam 검증Steam 소유권 또는 ticket 검증에 실패했습니다.
0x15인젭션 스캐너 사용 불가인젭션 스캐너 자체가 동작하지 못했습니다. SDK는 무방비로 실행하는 대신 fail-close로 종료합니다.
0x1F알 수 없는 분류더 구체적인 공개 분류와 매칭되지 않은 보안 이벤트입니다.
0x0BADB007시작 확인Boot-ACK 시간 초과: managed OZero 부트스트랩이 제시간에 시작 완료를 알리지 않았습니다.
0x0BADB008시작 확인로드 앵커 시간 초과: OZero 네이티브 라이브러리는 로드되었지만 90초 안에 OZero 부트스트랩이 시작되지 않았습니다. 빌드의 OZero 시작 항목이 지워졌거나 수정되었을 가능성이 큽니다.

API 필드와 callback 예시는 OZeroAbortCode & Event Messages를 참고하세요.

문제 해결 절차

단계 조치 기대 증거
1화면이나 로그에 표시된 지원 코드와 실행 플랫폼을 기록합니다.스크린샷 또는 티켓 본문에 코드, OS, Unity 버전, SDK 패키지 버전, Development/Release 빌드 여부가 포함되어야 합니다.
2먼저 ozero_abort.txt와 Player log를 확인합니다.팝업이 표시되기 전에 종료된 경우에도 원인을 확인하는 데 도움이 됩니다.
3QA 환경에서 재현 가능한 문제라면 Enable Failure Diagnostics를 켜고 한 번 재현한 뒤 다시 끕니다.최신 ozero_*_failure.log에서 어느 보호 영역이 반응했는지 확인할 수 있습니다. 이 파일은 고객 지원이나 QA 용도로만 사용하세요.
4정책을 바꾸기 전에 Window > OZero Security > Check Setup을 실행합니다.필수 패키지 누락, manifest/서명 불일치, 플랫폼 설정, ProGuard/R8 설정, native plugin, 라이선스 설정 문제를 먼저 확인할 수 있습니다.
5Pro 또는 관리형 검증 문제는 포털 등록 정보와 실제 빌드 정보를 비교합니다.package/app ID, 플랫폼 서명 정보, SDK package, native ABI, active variant manifest, license tier, 서버 연결 상태, downgraded/limited 상태를 확인합니다.
6수정 후 해당 플랫폼 QA 시나리오를 다시 실행합니다.티켓을 닫기 전에 생존 시간, Managed UI 표시 여부, 모의/실제 공격 결과, 팝업 카운트다운, 최종 로그를 캡처합니다.