서버 측 인증 콜백은 Google에서 외부 시스템에 보내고 Google에서 확장하는 쿼리 매개변수가 포함된 URL 요청입니다. 이를 통해 보상형 광고 또는 보상형 전면 광고와의 상호작용에 대해 사용자에게 보상해야 함을 알립니다. 보상형 SSV(서버 측 인증) 콜백은 클라이언트 측 콜백 스푸핑에 대한 추가 보호 계층을 제공하여 사용자에게 보상합니다.
이 가이드에는 Tink 제3자 암호화 라이브러리를 사용해 보상형 SSV 콜백을 인증하여 콜백의 쿼리 매개변수가 올바른 값인지 확인하는 방법이 나와 있습니다. 이 가이드에서는 Tink를 사용하지만 ECDSA를 지원하는 제3자 라이브러리를 사용할 수 있습니다. AdMob UI에서 테스트 도구를 사용하여 서버를 테스트할 수도 있습니다.
자바 spring-boot를 사용하는 완전히 작동하는 예를 확인하세요.
기본 요건
보상형 광고를 다음과 같은 버전의 모바일 앱에 통합하세요. Google 모바일 광고 SDK 7.28.0 버전 이상
광고 단위에서 보상형 서버 측 인증을 사용 설정하세요.
Tink의 RewardedAdsVerifier 사용
Tink GitHub 저장소에는
RewardedAdsVerifier
도우미 클래스가 포함되어 있어 보상형 SSV 콜백을 인증하는 데 필요한 코드를
줄여줍니다.
이 클래스를 Tink 제3자 암호화 라이브러리와 함께 사용하면
다음 코드를 통해 콜백 URL을
인증할 수 있습니다.
RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(
RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build();
String rewardUrl = ...;
verifier.verify(rewardUrl);
예외를 발생시키지 않고 verify()
메서드를 실행하여 콜백 URL을
성공적으로 인증했습니다. 사용자에 대한 보상 섹션에는
사용자가 보상을 받는 경우에 대한 권장사항이 자세히 나와 있습니다. 보상형 SSV 콜백을 인증하기 위해
이 클래스에서 수행한 단계를 자세히 알아보려면
보상형 SSV 수동 인증 섹션을
읽어보세요.
SSV 콜백 매개변수
서버 측 인증 콜백에는 보상형 광고 상호작용을 설명하는 쿼리 매개변수가 포함되어 있습니다. 매개변수 이름, 설명 및 예시 값은 다음과 같습니다. 매개변수는 알파벳순으로 전송됩니다.
매개변수 이름 | 설명 | 예시 값 |
---|---|---|
ad_network | 이 광고를 게재한 네트워크의 광고 네트워크 식별자입니다. ID 값에 해당하는 네트워크 이름이 광고 네트워크 식별자 섹션에 표시됩니다. | 1953547073528090325 |
ad_unit | 보상형 광고를 요청할 때 사용된 AdMob 광고 단위 ID입니다. | 2747237135 |
custom_data | customRewardString 에서 제공한 맞춤 데이터 문자열입니다.
앱에서 맞춤 데이터 문자열을 제공하지 않으면 이 쿼리 매개변수 값이 SSV 콜백에 표시되지 않습니다. |
SAMPLE_CUSTOM_DATA_STRING |
key_id | SSV 콜백을 인증할 때 사용하는 키입니다. 이 값은 AdMob 키 서버에서 제공하는 공개 키에 매핑됩니다. | 1234567890 |
reward_amount | 광고 단위 설정에 지정된 보상 금액입니다. | 5 |
reward_item | 광고 단위 설정에 지정된 대로 보상을 합니다. | 동전 |
signature | AdMob에서 생성한 SSV 콜백 서명입니다. | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
timestamp | 사용자가 보상을 받은 시간의 타임스탬프입니다(에포크 시간, ms). | 1507770365237823 |
transaction_id | AdMob에서 생성된 각 보상 이벤트의 고유한 16진수 코드 식별자입니다. | 18fa792de1bca816048293fc71035638 |
user_id | 다음에서 제공한 사용자 식별자입니다.
userIdentifier .
앱에서 사용자 식별자를 제공하지 않으면 이 쿼리 매개변수는 SSV 콜백에 표시되지 않습니다. |
1234567 |
광고 네트워크 식별자
아래 표에는 SSV 콜백의 광고 네트워크 식별자 값에 해당하는 광고 네트워크 이름이 나와 있습니다.
광고 네트워크 이름 | 광고 네트워크 ID |
---|---|
AdColony | 15586990674969969776 |
AdMob | 5450213213286189855 |
Applovin | 1063618907739174004 |
Chartboost | 2873236629771172317 |
Facebook Audience Network | 10568273599589928883 |
Fuse | 8914788932458531264 |
Fyber | 4839637394546996422 |
InMobi | 7681903010231960328 |
maio | 7505118203095108657 |
myTarget | 8450873672465271579 |
Nend | 9383070032774777750 |
Tapjoy | 7295217276740746030 |
Unity Ads | 4970775877303683148 |
Vungle | 1953547073528090325 |
사용자에 대한 보상
사용자에게 보상해야 할 시점을 정할 때 사용자 경험과 보상 검증의 균형을 맞추는 것이 중요합니다. 서버 측 콜백은 외부 시스템에 도달하기 전에 지연이 발생할 수 있습니다. 따라서 클라이언트 측 콜백을 사용하여 즉시 사용자에게 보상하고 서버 측 콜백을 받는 즉시 모든 보상에 대한 유효성 검사를 하는 것이 좋습니다. 이 방법에서는 보상의 유효성을 확인하는 동안 우수한 사용자 경험을 제공합니다.
그러나 보상이 앱 게임 내의 경제에 영향을 주는 등 보상 유효성이 앱에 매우 중요하고 보상 지급 지연이 허용되는 경우에는 인증된 서버 측 콜백을 기다리는 것이 가장 좋습니다.
맞춤 데이터
서버 측 확인 콜백에서 추가 데이터가 필요한 앱은
보상형 광고의 맞춤 데이터 기능을 사용해야 합니다. 보상형 광고 객체에 설정된
모든 문자열 값은 SSV 콜백의
custom_data
쿼리 매개변수 내에서 전달됩니다. 맞춤 데이터 값이 설정되지 않은 경우 custom_data
쿼리 매개변수
값은 SSV 콜백에 표시되지 않습니다.
아래 코드 스니펫은 광고를 요청하기 전에 보상형 광고 객체에 맞춤 데이터를 설정하는 방법을 보여줍니다.
GADRewardedAd *rewardedAd = [[GADRewardedAd alloc] initWithAdUnitID:@"ca-app-pub-3940256099942544/5224354917"]; GADServerSideVerificationOptions *options = [[GADServerSideVerificationOptions alloc] init]; options.customRewardString = @"SAMPLE_CUSTOM_DATA_STRING"; rewardedAd.serverSideVerificationOptions = options;
참고: 맞춤 보상 문자열은 이스케이프된 비율로 표시되며 SSV 콜백에서 파싱할 때 디코딩이 필요할 수도 있습니다.
보상형 SSV의 수동 인증
RewardedAdsVerifier
클래스가 보상형 SSV를 인증하기 위해 수행하는 단계는
아래에 나와 있습니다. 포함된 코드 스니펫이 자바로 되어 있고
Tink 제3자 라이브러리를 활용해도 이 단계는 ECDSA를
지원하는 제3자 라이브러리를 통해
원하는 언어로 구현할 수 있습니다.
공개 키 가져오기
보상형 SSV 콜백을 인증하려면 AdMob에서 제공하는 공개 키가 필요합니다.
보상형 SSV 콜백을 확인하는 데 사용되는 공개 키의 목록은 AdMob 키 서버에서 가져올 수 있습니다. 공개 키 목록은 다음과 비슷한 형식의 JSON 표현으로 제공됩니다.
{
"keys": [
{
keyId: 1916455855,
pem: "-----BEGIN PUBLIC KEY-----\nMF...YTPcw==\n-----END PUBLIC KEY-----"
base64: "MFkwEwYHKoZIzj0CAQYI...ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw=="
},
{
keyId: 3901585526,
pem: "-----BEGIN PUBLIC KEY-----\nMF...aDUsw==\n-----END PUBLIC KEY-----"
base64: "MFYwEAYHKoZIzj0CAQYF...4akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw=="
},
],
}
공개 키를 검색하려면 AdMob 키 서버에 연결하고 키를
다운로드하세요. 다음 코드에서는 이 작업을 처리하고 키의 JSON 표현을
data
변수에 저장합니다.
String url = ...;
NetHttpTransport httpTransport = new NetHttpTransport.Builder().build();
HttpRequest httpRequest =
httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(url));
HttpResponse httpResponse = httpRequest.execute();
if (httpResponse.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK) {
throw new IOException("Unexpected status code = " + httpResponse.getStatusCode());
}
String data;
InputStream contentStream = httpResponse.getContent();
try {
InputStreamReader reader = new InputStreamReader(contentStream, UTF_8);
data = readerToString(reader);
} finally {
contentStream.close();
}
참고: 공개 키는 정기적으로 로테이션됩니다. 예정된 로테이션을 알리는 이메일이 발송됩니다. 공개 키를 캐싱하는 경우 이 이메일을 받으면 키를 업데이트해야 합니다.
가져온 공개 키는 파싱해야 합니다. 아래의
parsePublicKeysJson
메서드는 위의 예시와 같은
JSON 문자열을 입력으로 사용하고 key_id
값을 공개 키로 매핑합니다.
이 공개 키는 Tink 라이브러리의 ECPublicKey
객체로 캡슐화됩니다.
private static Map<Integer, ECPublicKey> parsePublicKeysJson(String publicKeysJson)
throws GeneralSecurityException {
Map<Integer, ECPublicKey> publicKeys = new HashMap<>();
try {
JSONArray keys = new JSONObject(publicKeysJson).getJSONArray("keys");
for (int i = 0; i < keys.length(); i++) {
JSONObject key = keys.getJSONObject(i);
publicKeys.put(
key.getInt("keyId"),
EllipticCurves.getEcPublicKey(Base64.decode(key.getString("base64"))));
}
} catch (JSONException e) {
throw new GeneralSecurityException("failed to extract trusted signing public keys", e);
}
if (publicKeys.isEmpty()) {
throw new GeneralSecurityException("No trusted keys are available.");
}
return publicKeys;
}
인증할 내용 가져오기
보상형 SSV 콜백의 마지막 두 개 쿼리 매개변수는
항상 signature
와 key_id,
순서로 정렬됩니다. 나머지 쿼리 매개변수는 인증할
내용을 지정합니다. 보상 콜백을 https://www.myserver.com/mypath
로 보내도록 AdMob을 구성했다고
가정해 보겠습니다. 아래 스니펫은 인증할 콘텐츠(강조)가 포함된
보상형 SSV 콜백의 예를 보여줍니다.
https://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins ×tamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887
아래 코드에는 인증할 내용을 UTF-8 바이트 배열로 콜백 URL에서 파싱하는 방법이 나와 있습니다.
public static final String SIGNATURE_PARAM_NAME = "signature=";
...
URI uri;
try {
uri = new URI(rewardUrl);
} catch (URISyntaxException ex) {
throw new GeneralSecurityException(ex);
}
String queryString = uri.getQuery();
int i = queryString.indexOf(SIGNATURE_PARAM_NAME);
if (i == -1) {
throw new GeneralSecurityException("needs a signature query parameter");
}
byte[] queryParamContentData =
queryString
.substring(0, i - 1)
// i - 1 instead of i because of & in the query string
.getBytes(Charset.forName("UTF-8"));
콜백 URL에서 서명 및 key_id 가져오기
이전 단계의 queryString
값을 사용하여 아래와 같이
콜백 URL의 signature
및 key_id
쿼리 매개변수를 파싱합니다.
public static final String KEY_ID_PARAM_NAME = "key_id=";
...
String sigAndKeyId = queryString.substring(i);
i = sigAndKeyId.indexOf(KEY_ID_PARAM_NAME);
if (i == -1) {
throw new GeneralSecurityException("needs a key_id query parameter");
}
String sig =
sigAndKeyId.substring(
SIGNATURE_PARAM_NAME.length(), i - 1 /* i - 1 instead of i because of & */);
int keyId = Integer.valueOf(sigAndKeyId.substring(i + KEY_ID_PARAM_NAME.length()));
인증 처리
마지막은 공개 키로 콜백 URL의 내용을
인증하는 단계입니다. parsePublicKeysJson
메서드에서 반환된 매핑을 가져와서
콜백 URL의 key_id
매개변수를 통해
해당 매핑의 공개 키를 가져옵니다. 그런 다음 공개 키로 서명을
인증하세요. 아래의 verify
메서드에 이 단계에 대한 설명이 나와 있습니다.
private void verify(final byte[] dataToVerify, int keyId, final byte[] signature)
throws GeneralSecurityException {
Map<Integer, ECPublicKey> publicKeys = parsePublicKeysJson();
if (publicKeys.containsKey(keyId)) {
foundKeyId = true;
ECPublicKey publicKey = publicKeys.get(keyId);
EcdsaVerifyJce verifier = new EcdsaVerifyJce(publicKey, HashType.SHA256, EcdsaEncoding.DER);
verifier.verify(signature, dataToVerify);
} else {
throw new GeneralSecurityException("cannot find verifying key with key id: " + keyId);
}
}
메서드가 예외 없이 실행되면 콜백 URL이 성공적으로 인증된 것입니다.
FAQ
- AdMob 키 서버에서 제공하는 공개 키를 캐시할 수 있나요?
- SSV 콜백을 확인하는 데 필요한 작업 수를 줄이려면 AdMob 키 서버에서 제공하는 공개 키를 캐시하는 것이 좋습니다. 그러나 공개 키는 정기적으로 로테이션되며 24시간 이상 캐시해서는 안 됩니다.
- AdMob 키 서버에서 제공하는 공개 키는 얼마나 자주 로테이션되나요?
- AdMob 키 서버에서 제공하는 공개 키는 다양한 일정으로 로테이션됩니다. SSV 콜백 확인이 의도한 대로 계속 작동하려면 공개 키가 24시간 이상 캐시되지 않아야 합니다.
- 서버에 연결할 수 없는 경우 어떻게 되나요?
- Google은 SSV 콜백에 대해
HTTP 200 OK
성공 상태 응답 코드를 예상합니다. 서버에 연결할 수 없거나 서버에서 예상되는 응답을 제공하지 않으면 Google은 1초 간격으로 최대 5회 SSV 콜백을 전송하려고 재시도합니다. - SSV 콜백이 Google에서 전송되는지 확인하려면 어떻게 해야 하나요?
- SSV 콜백의 출처가 Google인지 확인하려면 역방향 DNS 조회를 사용하세요.