무결성 확인 결과

이 페이지에서는 반환된 무결성 확인 결과를 해석하고 사용하는 방법을 설명합니다. 표준 API 요청을 하든 기존 API 요청을 하든 무결성 확인 결과는 비슷한 내용과 함께 동일한 형식으로 반환됩니다. 무결성 확인 결과는 기기, 앱, 계정의 유효성에 관한 정보를 전달합니다. 앱 서버는 복호화되어 확인된 결과에서 결과 페이로드를 사용하여 앱에서 특정 작업이나 요청을 처리하는 최선의 방법을 결정할 수 있습니다.

반환된 무결성 확인 결과 형식

페이로드는 일반 텍스트 JSON이고 개발자가 제공한 정보와 함께 무결성 신호를 포함합니다.

일반적인 페이로드 구조는 다음과 같습니다.

{
  requestDetails: { ... }
  appIntegrity: { ... }
  deviceIntegrity: { ... }
  accountDetails: { ... }
  environmentDetails: { ... }
}

각 무결성 확인 결과를 확인하기 전에 먼저 requestDetails 필드의 값이 원래 요청의 값과 일치하는지 확인해야 합니다. 다음 섹션에서는 각 필드를 자세히 설명합니다.

요청 세부정보 필드

requestDetails 필드는 요청에 관한 정보를 포함합니다. 표준 요청의 경우 requestHash에, 기존 요청의 경우 nonce에 개발자가 제공한 정보가 포함됩니다.

표준 API 요청:

requestDetails: {
  // Application package name this attestation was requested for.
  // Note that this field might be spoofed in the middle of the request.
  requestPackageName: "com.package.name"
  // Request hash provided by the developer.
  requestHash: "aGVsbG8gd29scmQgdGhlcmU"
  // The timestamp in milliseconds when the integrity token
  // was requested.
  timestampMillis: "1675655009345"
}

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

Kotlin

val requestDetails = JSONObject(payload).getJSONObject("requestDetails")
val requestPackageName = requestDetails.getString("requestPackageName")
val requestHash = requestDetails.getString("requestHash")
val timestampMillis = requestDetails.getLong("timestampMillis")
val currentTimestampMillis = ...

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request
    || !requestHash.equals(expectedRequestHash)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

Java

RequestDetails requestDetails =
    decodeIntegrityTokenResponse
    .getTokenPayloadExternal()
    .getRequestDetails();
String requestPackageName = requestDetails.getRequestPackageName();
String requestHash = requestDetails.getRequestHash();
long timestampMillis = requestDetails.getTimestampMillis();
long currentTimestampMillis = ...;

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request.
    || !requestHash.equals(expectedRequestHash)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

기존 API 요청:

requestDetails: {
  // Application package name this attestation was requested for.
  // Note that this field might be spoofed in the middle of the
  // request.
  requestPackageName: "com.package.name"
  // base64-encoded URL-safe no-wrap nonce provided by the developer.
  nonce: "aGVsbG8gd29scmQgdGhlcmU"
  // The timestamp in milliseconds when the request was made
  // (computed on the server).
  timestampMillis: "1617893780"
}

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

Kotlin

val requestDetails = JSONObject(payload).getJSONObject("requestDetails")
val requestPackageName = requestDetails.getString("requestPackageName")
val nonce = requestDetails.getString("nonce")
val timestampMillis = requestDetails.getLong("timestampMillis")
val currentTimestampMillis = ...

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request. See 'Generate a nonce'
        // section of the doc on how to store/compute the expected nonce.
    || !nonce.equals(expectedNonce)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

Java

JSONObject requestDetails =
    new JSONObject(payload).getJSONObject("requestDetails");
String requestPackageName = requestDetails.getString("requestPackageName");
String nonce = requestDetails.getString("nonce");
long timestampMillis = requestDetails.getLong("timestampMillis");
long currentTimestampMillis = ...;

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request. See 'Generate a nonce'
        // section of the doc on how to store/compute the expected nonce.
    || !nonce.equals(expectedNonce)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

애플리케이션 무결성 필드

appIntegrity 필드에는 패키지 관련 정보가 포함됩니다.

appIntegrity: {
  // PLAY_RECOGNIZED, UNRECOGNIZED_VERSION, or UNEVALUATED.
  appRecognitionVerdict: "PLAY_RECOGNIZED"
  // The package name of the app.
  // This field is populated iff appRecognitionVerdict != UNEVALUATED.
  packageName: "com.package.name"
  // The sha256 digest of app certificates (base64-encoded URL-safe).
  // This field is populated iff appRecognitionVerdict != UNEVALUATED.
  certificateSha256Digest: ["6a6a1474b5cbbb2b1aa57e0bc3"]
  // The version of the app.
  // This field is populated iff appRecognitionVerdict != UNEVALUATED.
  versionCode: "42"
}

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

PLAY_RECOGNIZED
앱과 인증서가 Google Play에서 배포된 버전과 일치합니다.
UNRECOGNIZED_VERSION
인증서나 패키지 이름이 Google Play 기록과 일치하지 않습니다.
UNEVALUATED
애플리케이션 무결성이 평가되지 않았습니다. 기기를 충분히 신뢰할 수 없는 등 필요한 요구사항을 충족하지 못했습니다.

토큰이 개발자가 만든 앱에서 생성되었는지 확인하려면 다음 코드 스니펫과 같이 애플리케이션 무결성이 예상대로 나타나는지 확인합니다.

Kotlin

val appIntegrity = JSONObject(payload).getJSONObject("appIntegrity")
val appRecognitionVerdict = appIntegrity.getString("appRecognitionVerdict")

if (appRecognitionVerdict == "PLAY_RECOGNIZED") {
    // Looks good!
}

Java

JSONObject appIntegrity =
    new JSONObject(payload).getJSONObject("appIntegrity");
String appRecognitionVerdict =
    appIntegrity.getString("appRecognitionVerdict");

if (appRecognitionVerdict.equals("PLAY_RECOGNIZED")) {
    // Looks good!
}

앱 패키지 이름과 앱 버전, 앱 인증서를 수동으로 확인할 수도 있습니다.

기기 무결성 필드

deviceIntegrity 필드에는 deviceRecognitionVerdict 값 하나만 포함되며, 여기에는 기기가 앱 무결성을 얼마나 잘 시행할 수 있는지 나타내는 라벨이 하나 이상 포함됩니다. 기기가 라벨 기준을 충족하지 않으면 deviceIntegrity 필드는 비어 있습니다.

deviceIntegrity: {
  // "MEETS_DEVICE_INTEGRITY" is one of several possible values.
  deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
}

기본적으로 deviceRecognitionVerdict에는 다음이 포함될 수 있습니다.

MEETS_DEVICE_INTEGRITY
앱이 Google Play 서비스가 설치된 Android 지원 기기에서 실행 중입니다. 기기는 시스템 무결성 검사를 통과하고 Android 호환성 요구사항을 충족합니다.
비어 있음(빈 값)
앱이 공격(예: API 후킹)이나 시스템 손상(예: 루팅됨) 징후가 있는 기기에서 실행되거나, 앱이 Google Play 무결성 검사를 통과하지 못한 에뮬레이터와 같은 실제 기기에서 실행되지 않습니다.

토큰이 신뢰할 수 있는 기기에서 제공되었는지 확인하려면 다음 코드 스니펫과 같이 deviceRecognitionVerdict가 예상대로 표시되는지 확인합니다.

Kotlin

val deviceIntegrity =
    JSONObject(payload).getJSONObject("deviceIntegrity")
val deviceRecognitionVerdict =
    if (deviceIntegrity.has("deviceRecognitionVerdict")) {
        deviceIntegrity.getJSONArray("deviceRecognitionVerdict").toString()
    } else {
        ""
    }

if (deviceRecognitionVerdict.contains("MEETS_DEVICE_INTEGRITY")) {
    // Looks good!
}

Java

JSONObject deviceIntegrity =
    new JSONObject(payload).getJSONObject("deviceIntegrity");
String deviceRecognitionVerdict =
    deviceIntegrity.has("deviceRecognitionVerdict")
    ? deviceIntegrity.getJSONArray("deviceRecognitionVerdict").toString()
    : "";

if (deviceRecognitionVerdict.contains("MEETS_DEVICE_INTEGRITY")) {
    // Looks good!
}

테스트 기기가 기기 무결성을 충족하는 데 문제가 있다면 공장 출고 시 ROM이 설치(예: 기기 재설정)되어 있고 부트로더가 잠겨 있는지 확인하세요. Play Console에서 Play Integrity API 테스트를 만들 수도 있습니다.

조건부 기기 라벨

앱이 PC용 Google Play 게임즈에 출시되는 경우 deviceRecognitionVerdict에는 다음 라벨도 포함될 수 있습니다.

MEETS_VIRTUAL_INTEGRITY
앱이 Google Play 서비스가 설치된 Android 지원 에뮬레이터에서 실행 중입니다. 에뮬레이터는 시스템 무결성 검사를 통과하고 핵심 Android 호환성 요구사항을 충족합니다.

기기 정보(선택사항)

무결성 확인 결과에서 추가 라벨 수신을 선택한 경우 deviceRecognitionVerdict에는 다음 추가 라벨이 포함될 수 있습니다.

MEETS_BASIC_INTEGRITY
앱이 기본 시스템 무결성 검사를 통과한 기기에서 실행되며 Android 13 이상 기기의 경우 Android 플랫폼 키 증명이 필요합니다. 기기가 Android 호환성 요구사항을 충족하지 못할 수 있고 Google Play 서비스 실행이 승인되지 않을 수도 있습니다. 예를 들어 기기가 인식할 수 없는 Android 버전을 실행하거나 잠금 해제된 부트로더, 확인되지 않은 부팅을 보유하거나 제조업체의 인증을 받지 않았을 수 있습니다.
MEETS_STRONG_INTEGRITY
앱이 Google Play 서비스가 적용된 Android 지원 기기에서 실행되며 하드웨어 지원 부팅 무결성 증명과 같은 시스템 무결성을 강력히 보장합니다. Android 13 이상 기기의 경우 지난 1년 이내에 보안 업데이트를 받아야 합니다. 기기는 시스템 무결성 검사를 통과하고 Android 호환성 요구사항을 충족합니다.

단일 기기는 각 라벨의 기준이 충족되면 기기 무결성 확인 결과에 여러 기기 라벨을 반환합니다.

최근 기기 활동

또한 최근 기기 활동을 선택하면 지난 1시간 동안 앱이 특정 기기에서 무결성 토큰을 요청한 횟수를 알 수 있습니다. 최근 기기 활동을 사용하여 공격이 진행 중임을 나타낼 수 있는 예기치 않은 과잉 활성 기기로부터 앱을 보호할 수 있습니다. 일반적인 기기에 설치된 앱이 매시간 무결성 토큰을 요청하는 횟수를 기반으로 각 최근 기기 활동 수준을 얼마나 신뢰할지 결정할 수 있습니다.

recentDeviceActivity 수신을 선택하면 deviceIntegrity 필드에 다음 두 가지 값이 포함됩니다.

deviceIntegrity: {
  deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
  recentDeviceActivity: {
    // "LEVEL_2" is one of several possible values.
    deviceActivityLevel: "LEVEL_2"
  }
}

deviceActivityLevel 정의는 모드마다 다르며 다음 값 중 하나를 가질 수 있습니다.

최근 기기 활동 수준 지난 1시간 동안 이 기기에서 앱별로 표준 API 무결성 토큰 요청 지난 1시간 동안 이 기기에서 앱당 기존 API 무결성 토큰 요청
LEVEL_1 (최저) 10개 이하 5개 이하
LEVEL_2 11~25개 6~10개
LEVEL_3 26~50세 11~15
LEVEL_4 (최고) 50개 초과 15개 초과
UNEVALUATED 최근 기기 활동이 평가되지 않았습니다. 다음과 같은 이유로 이러한 문제가 발생할 수 있습니다.
  • 기기를 충분히 신뢰할 수 없습니다.
  • 기기에 설치된 앱 버전을 Google Play에서 알 수 없습니다.
  • 기기의 기술적 문제입니다.

기기 속성

기기에서 실행 중인 Android OS의 Android SDK 버전을 알려주는 기기 속성을 선택할 수도 있습니다. 향후 다른 기기 속성으로 확장될 수 있습니다.

SDK 버전 값은 Build.VERSION_CODES에 정의된 Android SDK 버전 번호입니다. 필요한 요구사항을 충족하지 못하면 SDK 버전이 평가되지 않습니다. 이 경우 sdkVersion 필드가 설정되지 않으므로 deviceAttributes 필드가 비어 있습니다. 다음과 같은 이유로 발생할 수 있습니다.

  • 기기를 충분히 신뢰할 수 없습니다.
  • 기기에 설치된 앱 버전을 Google Play에서 알 수 없습니다.
  • 기기에 기술적 문제가 있습니다.

deviceAttributes 수신을 선택하면 deviceIntegrity 필드에 다음과 같은 추가 필드가 포함됩니다.

deviceIntegrity: {
  deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
  deviceAttributes: {
    // 33 is one possible value, which represents Android 13 (Tiramisu).
    sdkVersion: 33
  }
}

SDK 버전이 평가되지 않으면 deviceAttributes 필드는 다음과 같이 설정됩니다.

deviceIntegrity: {
  deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
  deviceAttributes: {}  // sdkVersion field is not set.
}

계정 세부정보 필드

accountDetails 필드에는 기기에 로그인한 사용자 계정의 앱 Google Play 라이선스 상태를 나타내는 단일 값 appLicensingVerdict가 포함됩니다. 사용자 계정에 앱의 Play 라이선스가 있는 경우 사용자가 Google Play에서 앱을 다운로드했거나 구매했음을 의미합니다.

accountDetails: {
  // This field can be LICENSED, UNLICENSED, or UNEVALUATED.
  appLicensingVerdict: "LICENSED"
}

appLicensingVerdict의 값은 다음 중 하나일 수 있습니다.

LICENSED
사용자에게 앱 권한이 있습니다. 즉, 사용자가 기기에서 Google Play를 통해 앱을 설치했거나 업데이트했습니다.
UNLICENSED
사용자에게 앱 사용 권한이 없습니다. 예를 들어 사용자가 앱을 사이드로드한 경우 또는 Google Play에서 앱을 획득하지 않은 경우에 이러한 상황이 발생합니다. 사용자에게 GET_LICENSED 대화상자를 표시하여 이 문제를 해결할 수 있습니다.
UNEVALUATED

필요한 요구사항을 충족하지 못하여 라이선스 세부정보가 평가되지 않았습니다.

이는 다음을 비롯하여 여러 가지 이유로 발생할 수 있습니다.

  • 기기를 충분히 신뢰할 수 없습니다.
  • 기기에 설치된 앱 버전을 Google Play에서 알 수 없습니다.
  • 사용자가 Google Play에 로그인하지 않았습니다.

사용자에게 앱 사용 권한이 있는지 확인하려면 다음 코드 스니펫과 같이 appLicensingVerdict가 제대로 표시되는지 확인하세요.

Kotlin

val accountDetails = JSONObject(payload).getJSONObject("accountDetails")
val appLicensingVerdict = accountDetails.getString("appLicensingVerdict")

if (appLicensingVerdict == "LICENSED") {
    // Looks good!
}

Java

JSONObject accountDetails =
    new JSONObject(payload).getJSONObject("accountDetails");
String appLicensingVerdict = accountDetails.getString("appLicensingVerdict");

if (appLicensingVerdict.equals("LICENSED")) {
    // Looks good!
}

환경 세부정보 필드

환경에 관한 추가 신호를 선택할 수도 있습니다. 앱 액세스 위험은 화면을 캡처하거나 오버레이를 표시하거나 기기를 제어하는 데 사용할 수 있는 다른 앱이 실행 중인지 앱에 알려줍니다. Play 프로텍트 확인 결과는 기기에 Google Play 프로텍트가 사용 설정되어 있는지, 알려진 멀웨어가 발견되었는지 알려줍니다.

Google Play Console에서 앱 액세스 위험 확인 결과 또는 Play 프로텍트 확인 결과를 선택했다면 API 응답에 environmentDetails 필드가 포함됩니다. environmentDetails 필드는 appAccessRiskVerdictplayProtectVerdict라는 두 값을 포함할 수 있습니다.

앱 액세스 위험 확인 결과

사용 설정되면 Play Integrity API 페이로드environmentDetails 필드에 새 앱 액세스 위험 확인 결과가 포함됩니다.

{
  requestDetails: { ... }
  appIntegrity: { ... }
  deviceIntegrity: { ... }
  accountDetails: { ... }
  environmentDetails: {
      appAccessRiskVerdict: {
          // This field contains one or more responses, for example the following.
          appsDetected: ["KNOWN_INSTALLED", "UNKNOWN_INSTALLED", "UNKNOWN_CAPTURING"]
      }
 }
}

앱 액세스 위험이 평가된 경우 appAccessRiskVerdict에는 응답이 하나 이상 포함된 appsDetected 필드가 포함됩니다. 이러한 응답은 감지된 앱의 설치 소스에 따라 다음 두 그룹 중 하나에 속합니다.

  • Play 또는 시스템 앱: Google Play에서 설치하거나 기기의 시스템 파티션에 기기 제조업체가 미리 로드한 앱 (FLAG_SYSTEM로 식별됨)입니다. 이러한 앱의 응답에는 KNOWN_ 접두사가 추가됩니다.

  • 기타 앱: Google Play에서 설치하지 않은 앱입니다. 기기 제조업체가 시스템 파티션에 미리 로드한 앱은 제외됩니다. 이러한 앱의 응답에는 UNKNOWN_ 접두사가 붙습니다.

다음과 같은 응답이 반환될 수 있습니다.

UNKNOWN_INSTALLED KNOWN_INSTALLED
해당 설치 소스와 일치하는 앱이 설치되어 있습니다.
UNKNOWN_CAPTURING KNOWN_CAPTURING
앱이 실행되는 동안 화면을 보는 데 사용할 수 있는 권한이 사용 설정된 앱이 실행되고 있습니다. 기기에서 실행 중인 Google Play에 알려진 확인된 접근성 서비스는 제외됩니다.
UNKNOWN_CONTROLLING KNOWN_CONTROLLING
기기를 제어하고 앱에 관한 입력을 직접 제어하는 데 사용할 수 있으며 앱의 입력과 출력을 캡처하는 데 사용할 수 있는 권한이 사용 설정된 앱이 실행 중입니다. 따라서 기기에서 실행 중인 Google Play에 알려진 확인된 접근성 서비스가 모두 제외됩니다.
UNKNOWN_OVERLAYS KNOWN_OVERLAYS
앱에 오버레이를 표시하는 데 사용할 수 있는 권한이 사용 설정된 앱이 실행 중입니다. 따라서 기기에서 실행 중인 Google Play에 알려진 확인된 접근성 서비스가 모두 제외됩니다.
비어 있음(빈 값)

필요한 요구사항을 충족하지 못하면 앱 액세스 위험이 평가되지 않습니다. 이 경우 appAccessRiskVerdict 필드가 비어 있습니다. 이는 다음을 비롯하여 여러 가지 이유로 발생할 수 있습니다.

  • 기기를 충분히 신뢰할 수 없습니다.
  • 기기 폼 팩터가 휴대전화, 태블릿 또는 폴더블이 아닙니다.
  • 기기에서 Android 6 (API 수준 23) 이상을 실행하지 않습니다.
  • 기기에 설치된 앱 버전을 Google Play에서 알 수 없습니다.
  • 기기의 Google Play 스토어 버전이 오래되었습니다.
  • 게임에만 적용: 사용자 계정에 게임의 Play 라이선스가 없습니다.
  • verdictOptOut 매개변수와 함께 표준 요청이 사용되었습니다.
  • 아직 표준 요청의 앱 액세스 위험을 지원하지 않는 Play Integrity API 라이브러리 버전에서 표준 요청이 사용되었습니다.

앱 액세스 위험은 기기의 앱 스토어에서 설치한 향상된 Google Play 접근성 검토를 거친 확인된 접근성 서비스를 자동으로 제외합니다. '제외됨'은 기기에서 실행되는 확인된 접근성 서비스가 앱 액세스 위험 확인 결과에서 캡처, 제어 또는 오버레이 응답을 반환하지 않음을 의미합니다. 접근성 앱에 대한 향상된 Google Play 접근성 검토를 요청하려면 Google Play에 게시하고 앱의 매니페스트에서 isAccessibilityTool 플래그가 true로 설정되어 있는지 확인하거나 검토를 요청하세요.

다음 표에는 확인 결과 예시와 그 의미가 나와 있습니다 (이 표에 가능한 모든 결과가 나열되지는 않음).

앱 액세스 위험 확인 결과 응답 예시 해석
appsDetected:
["KNOWN_INSTALLED"]
Google Play에서 인식하거나 기기 제조업체가 시스템 파티션에 미리 로드한 앱만 설치되어 있습니다.
캡처, 제어 또는 오버레이 확인 결과를 초래하는 앱은 실행되고 있지 않습니다.
appsDetected:
["KNOWN_INSTALLED",
"UNKNOWN_INSTALLED",
"UNKNOWN_CAPTURING"]
Google Play에서 설치했거나 기기 제조업체가 시스템 파티션에 미리 로드한 앱이 있습니다.
화면을 보거나 기타 입력과 출력을 캡처하는 데 사용할 수 있는 권한이 사용 설정된 다른 앱이 실행되고 있습니다.
appsDetected:
["KNOWN_INSTALLED",
"KNOWN_CAPTURING",
"UNKNOWN_INSTALLED",
"UNKNOWN_CONTROLLING"]
화면을 보거나 다른 입력과 출력을 캡처하는 데 사용할 수 있는 권한이 사용 설정된 Play 또는 시스템이 실행되고 있습니다.
기기를 제어하고 앱에 대한 입력을 직접 제어하는 데 사용할 수 있는 권한이 사용 설정된 다른 앱도 실행되고 있습니다.
appAccessRiskVerdict: {} 필요한 요구사항을 충족하지 못하여 앱 액세스 위험이 평가되지 않습니다. 예를 들어 기기를 충분히 신뢰할 수 없습니다.

위험 수준에 따라 계속 진행할 수 있는 확인 결과 조합과 조치를 취할 확인 결과를 결정할 수 있습니다. 다음 코드 스니펫은 화면을 캡처하거나 앱을 제어할 수 있는 앱이 실행되고 있지 않음을 확인하는 예를 보여줍니다.

Kotlin

val environmentDetails =
    JSONObject(payload).getJSONObject("environmentDetails")
val appAccessRiskVerdict =
    environmentDetails.getJSONObject("appAccessRiskVerdict")

if (appAccessRiskVerdict.has("appsDetected")) {
    val appsDetected = appAccessRiskVerdict.getJSONArray("appsDetected").toString()
    if (!appsDetected.contains("CAPTURING") && !appsDetected.contains("CONTROLLING")) {
        // Looks good!
    }
}

자바

JSONObject environmentDetails =
    new JSONObject(payload).getJSONObject("environmentDetails");
JSONObject appAccessRiskVerdict =
    environmentDetails.getJSONObject("appAccessRiskVerdict");

if (appAccessRiskVerdict.has("appsDetected")) {
    String appsDetected = appAccessRiskVerdict.getJSONArray("appsDetected").toString()
    if (!appsDetected.contains("CAPTURING") && !appsDetected.contains("CONTROLLING")) {
        // Looks good!
    }
}
앱 액세스 위험 확인 결과 해결

위험 수준에 따라 사용자가 요청이나 작업을 완료하기 전에 조치를 취할 앱 액세스 위험 확인 결과를 결정할 수 있습니다. 앱 액세스 위험 확인 후 사용자에게 표시할 수 있는 Google Play 메시지(선택사항)가 있습니다. CLOSE_UNKNOWN_ACCESS_RISK를 표시하여 앱 액세스 위험 확인 결과를 일으키는 알 수 없는 앱을 닫으라고 사용자에게 요청하거나 CLOSE_ALL_ACCESS_RISK를 표시하여 앱 액세스 위험 확인 결과를 일으키는 모든 앱 (알려진 앱 및 알 수 없는 앱)을 닫으라고 사용자에게 요청할 수 있습니다.

Play 프로텍트 확인 결과

사용 설정되면 Play Integrity API 페이로드environmentDetails 필드에 Play 프로텍트 확인 결과가 포함됩니다.

environmentDetails: {
  playProtectVerdict: "NO_ISSUES"
}

playProtectVerdict의 값은 다음 중 하나일 수 있습니다.

NO_ISSUES
Play 프로텍트가 사용 설정되어 있으며 기기에서 앱 문제를 발견하지 못했습니다.
NO_DATA
Play 프로텍트가 사용 설정되어 있지만 아직 검사가 이루어지지 않았습니다. 기기 또는 Play 스토어 앱이 최근에 재설정되었을 수 있습니다.
POSSIBLE_RISK
Play 프로텍트가 사용 중지되어 있습니다.
MEDIUM_RISK
Play 프로텍트가 사용 설정되어 있으며 기기에 설치된 잠재적으로 위험한 앱을 발견했습니다.
HIGH_RISK
Play 프로텍트가 사용 설정되어 있으며 기기에 설치된 위험한 앱을 발견했습니다.
UNEVALUATED

Play 프로텍트 확인 결과가 평가되지 않았습니다.

이는 다음을 비롯하여 여러 가지 이유로 발생할 수 있습니다.

  • 기기를 충분히 신뢰할 수 없습니다.
  • 게임에만 적용: 사용자 계정에 게임의 Play 라이선스가 없습니다.

Play 프로텍트 확인 결과 사용에 관한 안내

앱의 백엔드 서버는 위험 허용 범위에 기반한 확인 결과에 따라 조치 방법을 결정할 수 있습니다. 다음은 몇 가지 제안사항과 잠재적인 사용자 작업입니다.

NO_ISSUES
Play 프로텍트가 사용 설정되어 있으며 문제가 발견되지 않았으므로 사용자 작업이 필요하지 않습니다.
POSSIBLE_RISKNO_DATA
이러한 확인 결과를 수신하면 사용자에게 Play 프로텍트가 사용 설정되어 있고 검사를 실행했는지 확인하도록 요청합니다. NO_DATA는 드물게만 표시되어야 합니다.
MEDIUM_RISKHIGH_RISK
위험 허용 범위에 따라 사용자에게 Play 프로텍트를 실행하고 Play 프로텍트 경고에 조치를 취하도록 요청할 수 있습니다. 사용자가 이러한 요구사항을 충족할 수 없다면 서버 작업에서 차단할 수 있습니다.