앱에 PC용 Play Integrity 통합

PC용 Play Integrity를 사용하면 게임 이벤트와 서버 요청이 정품 PC 기기의 정품 PC용 Google Play 게임즈 인스턴스에서 비롯되는지 확인할 수 있습니다. 잠재적으로 위험한 기기와 알 수 없는 에뮬레이터를 감지함으로써 게임의 백엔드 서버는 속임수, 무단 액세스, 허위 트래픽, 악용을 방지하기 위한 적절한 조치를 취할 수 있습니다.

사전 준비 사항

1단계: 게임에서 PC용 Play Integrity를 사용하는 방법 결정하기

PC용 Play Integrity를 호출하여 환경에 관한 무결성 확인 결과를 가져올 시기를 결정합니다. 예를 들어 게임이 열리거나, 플레이어가 로그인하거나, 플레이어가 멀티플레이어 게임에 참여할 때 확인 결과를 요청할 수 있습니다. 그런 다음 다양한 무결성 응답을 처리하는 방법을 결정합니다. 예를 들면 다음과 같은 작업을 할 수 있습니다.

  • 강제 조치 없이 응답을 수집하고 내부적으로 데이터를 분석하여 악용의 유용한 신호인지 파악합니다.
  • 응답을 수집하고 백엔드 서버에 로직을 구현하여 무결성 결과를 통과한 기기는 게임을 정상적으로 플레이할 수 있도록 허용하고, 의심스러운 환경에서 유입되는 트래픽은 차단하거나 액세스를 거부합니다.
  • 응답을 수집하고 백엔드에서 로직을 구현하여 무결성 검사를 통과한 기기의 플레이어를 함께 매칭하는 동시에 의심스러운 환경에서 발생하는 트래픽을 함께 매칭합니다.

2단계: 게임에서 무결성 토큰 요청

PC용 Play Integrity 워밍업

PC용 Play Integrity를 준비합니다. 이렇게 하면 무결성 확인 결과 요청을 할 때 중요한 경로의 지연 시간을 줄이기 위해 Google Play에서 기기의 부분 증명 정보를 스마트하게 캐시할 수 있습니다. 게임이 열리자마자 비동기적으로 이 작업을 실행하여 필요할 때 주문형 무결성 요청을 할 수 있습니다.

void PrepareIntegrityToken(
  const PrepareIntegrityTokenParams & params,
  PrepareIntegrityTokenContinuation continuation
)

성공하면 무결성 토큰을 요청하는 데 사용해야 하는 RequestTokenData가 포함된 PrepareIntegrityTokenResultValue로 연속이 호출됩니다. 이 데이터는 메모리에 캐시되어야 하며 RequestIntegrityToken 호출을 위해 애플리케이션 세션 기간 동안 재사용되어야 합니다.

애플리케이션에서 무결성 확인 결과를 완전히 재평가해야 한다고 판단하는 경우에만 PrepareIntegrityToken을 호출해야 합니다.

세부정보
매개변수 params: Google Cloud 프로젝트 번호가 포함된 매개변수입니다.
continuation: 무결성 토큰 제공자를 반환하는 비동기 콜백입니다.

PrepareIntegrityToken 작업을 호출하는 방법을 보여주는 코드 스니펫은 다음과 같습니다.

google::play::integrity::IntegrityClient client_;

google::play::integrity::PrepareIntegrityTokenResult
IntegrityInterface::PrepareIntegrityToken(int64_t cloud_project_number) {
  google::play::integrity::PrepareIntegrityTokenParams params;
  params.cloud_project_number = cloud_project_number;

  auto promise = std::make_shared<
      std::promise<google::play::integrity::PrepareIntegrityTokenResult>>();
  client_.PrepareIntegrityToken(
      params,
      [promise](
          google::play::integrity::PrepareIntegrityTokenResult result) {
        promise->set_value(std::move(result));
      });

  return promise->get_future().get();
}

무결성 토큰 요청

무결성 토큰은 게임이 기기가 무단 변경되지 않았음을 확인할 수 있는 메커니즘입니다. 게임에서 진짜인지 확인하려는 서버 요청을 실행할 때마다 무결성 토큰을 요청하고 복호화 및 확인을 위해 게임의 백엔드 서버로 전송할 수 있습니다.

PC용 Play Integrity API로 앱에서 사용자 작업을 확인할 때 RequestIntegrityTokenParams::request_hash 필드를 사용하여 조작 공격을 완화할 수 있습니다. 예를 들어 플레이어의 점수를 게임의 백엔드 서버에 보고하려고 할 때 서버는 프록시 서버에서 이 점수가 조작되지 않았는지 확인하려고 합니다. PC용 Play Integrity는 서명된 무결성 응답 내에서 이 필드에 설정된 값을 반환할 수 있습니다. requestHash가 없으면 무결성 토큰은 기기에만 바인딩되고 특정 요청에는 바인딩되지 않으므로 공격이 발생할 수 있습니다.

void RequestIntegrityToken(
  const RequestIntegrityTokenParams & params,
  RequestIntegrityTokenContinuation continuation
)

공격 가능성을 완화하려면 무결성 확인 결과를 요청할 때 다음을 충족해야 합니다.

  • 발생하는 사용자 작업 또는 서버 요청에서 모든 관련 요청 매개변수(예: 안정적인 요청 직렬화의 SHA256)의 다이제스트를 계산합니다.
  • RequestIntegrityTokenParams::request_hash 필드를 다이제스트로 설정합니다.
세부정보
매개변수 params: 준비된 RequestTokenData 및 무결성 확인 요청 해시가 포함된 매개변수입니다.
continuation: 데이터를 반환하는 비동기 콜백입니다.

RequestIntegrityToken 작업을 호출하는 방법을 보여주는 코드 스니펫은 다음과 같습니다.

absl::StatusOr<google::play::integrity::RequestIntegrityTokenResult>
IntegrityInterface::RequestIntegrityToken(
    const google::play::integrity::PrepareIntegrityTokenResult&
        prepare_integrity_token_result,
    const std::string& request_hash) {
  // Check if the prepare_integrity_token_result is OK
  if (!prepare_integrity_token_result.ok()) {
    return absl::FailedPreconditionError(
        absl::StrCat("PrepareIntegrityTokenResult is not OK. Error code: ",
                     prepare_integrity_token_result.error_code));
  }

  google::play::integrity::RequestIntegrityTokenParams params{
      .request_token_data =
          prepare_integrity_token_result.request_token_data,
      .request_hash = request_hash};

  auto promise = std::make_shared<std::promise<
      google::play::integrity::RequestIntegrityTokenResult>>();
  client_.RequestIntegrityToken(
      params,
      [promise](google::play::integrity::RequestIntegrityTokenResult result) {
        promise->set_value(std::move(result));
      });

  return promise->get_future().get();
}

3단계: 게임의 백엔드 서버에서 무결성 토큰 복호화 및 확인

무결성 토큰 복호화

무결성 확인 결과를 요청하면 Play Integrity API가 암호화된 응답 토큰을 제공합니다. 기기 무결성 확인 결과를 가져오려면 Google 서버에서 무결성 토큰을 복호화해야 합니다.

  1. 앱에 연결된 Google Cloud 프로젝트 내에서 서비스 계정을 만듭니다.
  2. 앱 서버에서 playintegrity 범위를 사용하여 서비스 계정 사용자 인증 정보로부터 액세스 토큰을 가져오고 다음과 같이 요청합니다.

    playintegrity.googleapis.com/v1/<var>PACKAGE_NAME</var>:decodePcIntegrityToken -d \
     '{ "integrity_token": "<var>INTEGRITY_TOKEN</var>" }'
    
  3. JSON 응답을 읽습니다.

결과 페이로드는 무결성 확인 결과와 세부정보가 개발자 제공 정보와 함께 포함된 일반 텍스트 토큰입니다. 복호화된 무결성 토큰은 다음과 같습니다.

{
  "requestDetails": {
    "requestPackageName": "com.your.package.name",
    "requestTime": "2025-08-29T13:10:37.285Z",
    "requestHash": "your_request_hash_string"
  },
  "deviceIntegrity": {
    "deviceRecognitionVerdict": [
      "MEETS_PC_INTEGRITY"
    ]
  },
}

무결성 토큰 확인

디코딩된 무결성 토큰의 requestDetails 필드에는 요청에 관한 정보가 포함됩니다. 여기에는 requestHash에 개발자가 제공한 정보가 포함됩니다.

requestHashpackageName 필드는 원래 요청의 필드와 일치해야 합니다. 따라서 requestPackageNamerequestHash가 다음 코드 스니펫과 같이 원래 요청에서 전송된 것과 일치하는지 확인하여 JSON 페이로드의 requestDetails 부분을 확인합니다.

const auto& request_details = json_payload["requestDetails"];

if (request_details.value("requestPackageName", "") != <YOUR_PACKAGE_NAME>) {
  // Don't trust the verdicts.
}

// Check for the existence of the request_hash.
// If you set a request hash in the request and it's not present, you shouldn't
// trust the verdicts.
if (!request_details.contains("requestHash")) {
    // Don't trust the verdicts.
}


// The requestHash from request_details needs to match the request hash your
// app provided.
if (request_details.value("requestHash", "") != <PROVIDED_REQUEST_HASH>) {
    // Don't trust the verdicts.
}

// You can read the rest of payload's fields.

4단계: 무결성 확인 결과에 따라 취할 조치 결정

deviceIntegrity 필드에는 단일 값 deviceRecognitionVerdict가 포함될 수 있습니다. 이 값을 사용하여 게임이 Play 무결성 검사를 통과하는 PC에서 실행되고 있는지 확인할 수 있습니다 (MEETS_PC_INTEGRITY 응답). 게임의 백엔드 서버는 이 정보를 수집하고 이를 사용하여 게임에서 취해야 하는 조치(예: 게임 이벤트 진행 허용 또는 위험한 트래픽에 대한 액세스 거부)를 결정할 수 있습니다.

"deviceIntegrity": {
  "deviceRecognitionVerdict": ["MEETS_PC_INTEGRITY"]
}

deviceRecognitionVerdict가 보유할 수 있는 값은 다음과 같습니다.

MEETS_PC_INTEGRITY
게임이 정품 PC 환경에서 실행되고 있으며 기기 내 조작이 감지되지 않았습니다.
비어 있음(빈 값)
게임이 공격(예: API 후킹)이나 시스템 손상(예: 조작된 Google 데스크톱 서비스 버전을 실행하는 기기) 징후가 있는 기기에서 실행되거나, 앱이 Google Play 무결성 검사를 통과하지 못한 에뮬레이터와 같은 실제 기기에서 실행되지 않습니다.

5단계: 오류 코드 처리

게임에서 PC용 Play Integrity 요청을 하고 호출이 실패하면 게임에 오류 코드가 수신됩니다. 이러한 오류는 네트워크 연결 불량과 같은 환경 문제, API 통합 문제, 악의적인 활동 및 활성 공격 등 다양한 이유로 발생할 수 있습니다.

재시도 가능한 오류 코드

이러한 오류의 원인은 일시적인 조건으로 인해 발생할 수 있으므로 지수 백오프 전략을 사용하여 호출을 다시 시도해야 합니다.

IntegrityError 오류 설명 오류 코드
kNetworkError 기기의 네트워크 연결 문제 5
kTooManyRequests 기기에서 요청한 횟수가 너무 많습니다. 6
kClientTransientError 클라이언트의 일시적인 문제입니다. 7

재시도 전략에 대한 자세한 권장사항은 여기를 참고하세요.

재시도할 수 없는 오류 코드

이 경우에는 자동 재시도가 도움이 되지 않습니다. 단, 사용자가 문제의 원인이 된 조건을 해결한다면 수동 재시도가 도움이 될 수 있습니다.

IntegrityError 오류 설명 오류 코드 권장 작업
kError SDK 작업 중 치명적인 오류가 발생했습니다. 1 다시 시도하기 전에 API 구현을 확인하세요.
kCloudProjectNumberIsInvalid 클라우드 프로젝트 번호가 잘못되었습니다. 2 Google Cloud 콘솔에서 클라우드 프로젝트 번호가 올바르게 구성되어 있고 요청이 올바른 클라우드 프로젝트 번호로 이루어지는지 확인합니다.
kRequestHashTooLong 요청 해시가 너무 깁니다. 3 생성된 요청 해시가 너무 깁니다. 500자(영문 기준) 미만이어야 합니다.
kNoValidPreparedTokenFound 토큰 요청을 하기 전에 준비된 토큰이 없습니다. 4 [RequestIntegrityToken][request-integrity-token] 호출을 하기 전에 [PrepareIntegrityToken][prepare-token] 작업을 호출합니다.
kSdkRuntimeUpdateRequired Play for Native SDK를 업데이트해야 합니다. 8 기기의 Google Play 서비스 클라이언트가 최신 상태이고 최신 버전의 Play for Native PC SDK를 사용하고 있는지 확인합니다.