ตรวจสอบการเรียกกลับของการยืนยันฝั่งเซิร์ฟเวอร์ (SSV)

การเรียกกลับการยืนยันฝั่งเซิร์ฟเวอร์คือคําขอ URL ที่มีพารามิเตอร์การค้นหาที่ Google ขยาย ซึ่ง Google ส่งไปยังระบบภายนอกเพื่อแจ้งให้ทราบว่าผู้ใช้ควรได้รับรางวัลจากการโต้ตอบกับโฆษณาคั่นระหว่างหน้าที่มีการให้รางวัลหรือโฆษณาคั่นระหว่างหน้าที่มีการให้รางวัล การเรียกกลับ SSV (การยืนยันฝั่งเซิร์ฟเวอร์) ที่มีรางวัลจะเพิ่มการป้องกันอีกชั้นหนึ่งเพื่อป้องกันการปลอมแปลงการเรียกกลับฝั่งไคลเอ็นต์เพื่อมอบรางวัลแก่ผู้ใช้

คู่มือนี้จะแสดงวิธียืนยันการเรียกกลับ SSV ที่มีการให้รางวัลโดยใช้ไลบรารีการเข้ารหัสของบุคคลที่สามใน Tink Java Apps เพื่อให้แน่ใจว่าพารามิเตอร์การค้นหาในการเรียกกลับเป็นค่าที่ถูกต้อง แม้ว่าคู่มือนี้จะกล่าวถึงการใช้ Tink แต่คุณมีตัวเลือกในการใช้ไลบรารีของบุคคลที่สามที่รองรับ ECDSA นอกจากนี้ คุณยังทดสอบเซิร์ฟเวอร์ด้วยเครื่องมือทดสอบใน UI ของ AdMob ได้ด้วย

ข้อกำหนดเบื้องต้น

ใช้ RewardedAdsVerifier จากไลบรารีแอป Java ของ Tink

ที่เก็บ GitHub ของ Tink Java Apps ประกอบด้วยคลาสตัวช่วย RewardedAdsVerifier เพื่อลดโค้ดที่จําเป็นในการยืนยันการเรียกกลับ SSV ที่มีรางวัล การใช้คลาสนี้จะช่วยให้คุณยืนยัน 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 ตัวระบุแหล่งที่มาของโฆษณาสําหรับแหล่งที่มาของโฆษณาที่แสดงโฆษณานี้ ชื่อแหล่งที่มาของโฆษณาที่สอดคล้องกับค่ารหัสจะแสดงอยู่ในส่วนตัวระบุแหล่งที่มาของโฆษณา 1953547073528090325
ad_unit รหัสหน่วยโฆษณา AdMob ที่ใช้เพื่อขอโฆษณาที่มีการให้รางวัล 2747237135
key_id คีย์ที่จะใช้ยืนยันการติดต่อกลับ SSV ค่านี้จะจับคู่กับคีย์สาธารณะที่เซิร์ฟเวอร์คีย์ AdMob ระบุ 1234567890
reward_amount จํานวนรางวัลตามที่ระบุไว้ในการตั้งค่าหน่วยโฆษณา 5
reward_item ไอเทมรางวัลตามที่ระบุไว้ในการตั้งค่าหน่วยโฆษณา เหรียญ
ลายเซ็น ลายเซ็นสําหรับการเรียกกลับ SSV ที่ AdMob สร้างขึ้น MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
การประทับเวลา การประทับเวลาที่ผู้ใช้ได้รับรางวัลเป็นเวลาตามยุคสมัยในหน่วยมิลลิวินาที 1507770365237823
transaction_id ตัวระบุที่เข้ารหัสฐาน 16 ที่ไม่ซ้ำกันสําหรับกิจกรรมการให้รางวัลแต่ละรายการที่ AdMob สร้างขึ้น 18fa792de1bca816048293fc71035638
user_id ตัวระบุผู้ใช้ตามที่ SetUserId ระบุ

หากแอปไม่ได้ระบุตัวระบุผู้ใช้ พารามิเตอร์การค้นหานี้จะไม่อยู่ในการเรียกกลับ SSV

1234567

ตัวระบุแหล่งที่มาของโฆษณา

ชื่อและรหัสแหล่งที่มาของโฆษณา

ชื่อแหล่งที่มาของโฆษณา รหัสแหล่งที่มาของโฆษณา
Aarki (การเสนอราคา)5240798063227064260
การสร้างโฆษณา (การเสนอราคา)1477265452970951479
AdColony15586990674969969776
AdColony (ไม่ใช่ SDK) (การเสนอราคา)4600416542059544716
AdColony (การเสนอราคา)6895345910719072481
AdFalcon3528208921554210682
เครือข่าย AdMob5450213213286189855
Waterfall ของเครือข่าย AdMob1215381445328257950
ADResult10593873382626181482
AMoAd17253994435944008978
Applovin1063618907739174004
Applovin (การเสนอราคา)1328079684332308356
Chartboost2873236629771172317
Chocolate Platform (การเสนอราคา)6432849193975106527
CrossChannel (MdotM)9372067028804390441
เหตุการณ์ที่กำหนดเอง18351550913290782395
DT Exchange*
* ก่อนวันที่ 21 กันยายน 2022 เครือข่ายนี้เรียกว่า "Fyber Marketplace"
2179455223494392917
EMX (การเสนอราคา)8497809869790333482
Fluct (การเสนอราคา)8419777862490735710
Flurry3376427960656545613
Fyber*
* แหล่งที่มาของโฆษณานี้ใช้สําหรับการรายงานข้อมูลย้อนหลัง
4839637394546996422
i-mobile5208827440166355534
ปรับปรุงโซลูชันทางดิจิทัล (การเสนอราคา)159382223051638006
Index Exchange (การเสนอราคา)4100650709078789802
InMobi7681903010231960328
InMobi (การเสนอราคา)6325663098072678541
InMobi Exchange (การเสนอราคา)5264320421916134407
IronSource6925240245545091930
ironSource Ads (การเสนอราคา)1643326773739866623
Leadbolt2899150749497968595
LG U+AD18298738678491729107
LINE Ads Network3025503711505004547
maio7505118203095108657
maio (การเสนอราคา)1343336733822567166
Media.net (การเสนอราคา)2127936450554446159
โฆษณาเฮาส์แอ็ดที่ใช้สื่อกลาง6060308706800320801
Meta Audience Network*
* ก่อนวันที่ 6 มิถุนายน 2022 เครือข่ายนี้เรียกว่า "Facebook Audience Network"
10568273599589928883
Meta Audience Network (การเสนอราคา)*
* ก่อนวันที่ 6 มิถุนายน 2022 เครือข่ายนี้เรียกว่า "Facebook Audience Network (การเสนอราคา)"
11198165126854996598
Mintegral1357746574408896200
Mintegral (การเสนอราคา)6250601289653372374
MobFox8079529624516381459
MobFox (การเสนอราคา)3086513548163922365
MoPub (เลิกใช้งานแล้ว)10872986198578383917
myTarget8450873672465271579
Nend9383070032774777750
Nexxen (การเสนอราคา)*

* ก่อนวันที่ 1 พฤษภาคม 2024 เครือข่ายนี้ชื่อ "UnrulyX"

2831998725945605450
ONE by AOL (Millennial Media)6101072188699264581
ONE by AOL (Nexage)3224789793037044399
OneTag Exchange (การเสนอราคา)4873891452523427499
OpenX (การเสนอราคา)4918705482605678398
Pangle4069896914521993236
Pangle (การเสนอราคา)3525379893916449117
PubMatic (การเสนอราคา)3841544486172445473
แคมเปญแบบจองล่วงหน้า7068401028668408324
RhythmOne (การเสนอราคา)2831998725945605450
Magnite DV+ (การเสนอราคา)3993193775968767067
ดาวเคราะห์ SK734341340207269415
Sharethrough (การเสนอราคา)5247944089976324188
Smaato (การเสนอราคา)3362360112145450544
Equativ (การเสนอราคา)*

* ก่อนวันที่ 12 มกราคม 2023 เครือข่ายนี้เรียกว่า "Smart Adserver"

5970199210771591442
Sonobi (การเสนอราคา)3270984106996027150
Tapjoy7295217276740746030
Tapjoy (การเสนอราคา)4692500501762622178
Tencent GDT7007906637038700218
TripleLift (การเสนอราคา)8332676245392738510
Unity Ads4970775877303683148
Unity Ads (การเสนอราคา)7069338991535737586
Verizon Media7360851262951344112
Verve Group (การเสนอราคา)5013176581647059185
Vpon1940957084538325905
Liftoff Monetize*

* ก่อนวันที่ 30 มกราคม 2023 เครือข่ายนี้เรียกว่า "Vungle"

1953547073528090325
Liftoff Monetize (การเสนอราคา)*

* ก่อนวันที่ 30 มกราคม 2023 เครือข่ายนี้เรียกว่า "Vungle (การเสนอราคา)"

4692500501762622185
Yieldmo (การเสนอราคา)4193081836471107579
YieldOne (การเสนอราคา)3154533971590234104
Zucks5506531810221735863

การมอบรางวัลแก่ผู้ใช้

สิ่งสำคัญคือการสร้างสมดุลระหว่างประสบการณ์ของผู้ใช้และการยืนยันรางวัลเมื่อตัดสินใจว่าจะให้รางวัลแก่ผู้ใช้เมื่อใด การเรียกกลับฝั่งเซิร์ฟเวอร์อาจเกิดความล่าช้าก่อนที่จะเข้าถึงระบบภายนอก ดังนั้น แนวทางปฏิบัติแนะนำคือให้ใช้การเรียกกลับฝั่งไคลเอ็นต์เพื่อมอบรางวัลแก่ผู้ใช้ทันที พร้อมกับตรวจสอบรางวัลทั้งหมดเมื่อได้รับการเรียกกลับฝั่งเซิร์ฟเวอร์ แนวทางนี้ช่วยให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ดีไปพร้อมกับการรับรองความถูกต้องของรางวัลที่มอบ

อย่างไรก็ตาม สําหรับแอปพลิเคชันที่ความถูกต้องของรางวัลสําคัญ (เช่น รางวัลส่งผลต่อระบบเศรษฐกิจในเกมของแอป) และยอมรับความล่าช้าในการให้รางวัลได้ การรอการเรียกกลับฝั่งเซิร์ฟเวอร์ที่ยืนยันแล้วอาจเป็นแนวทางที่ดีที่สุด

ข้อมูลที่กำหนดเอง

แอปที่ต้องใช้ข้อมูลเพิ่มเติมในการเรียกกลับการยืนยันฝั่งเซิร์ฟเวอร์ควรใช้ฟีเจอร์ข้อมูลที่กำหนดเองของโฆษณาที่มีการให้รางวัล ระบบจะส่งค่าสตริงที่ตั้งค่าไว้ในออบเจ็กต์โฆษณาที่มีการให้รางวัลไปยังพารามิเตอร์การค้นหา custom_data ของคอลแบ็ก SSV หากไม่ได้ตั้งค่าcustom_dataค่าพารามิเตอร์การค้นหา ระบบจะไม่แสดงค่าดังกล่าวในการเรียกกลับ SSV

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีตั้งค่าตัวเลือก SSV หลังจากโหลดโฆษณาที่มีการให้รางวัลแล้ว

private void LoadRewardedAd(string adUnitId)
{
  // Send the request to load the ad.
  AdRequest adRequest = new AdRequest();
  RewardedAd.Load(adUnitId, adRequest, (RewardedAd rewardedAd, LoadAdError error) =>
  {
    // If the operation failed with a reason.
    if (error != null)
    {
        Debug.LogError("Rewarded ad failed to load an ad with error : " + error);
        return;
    }

    var options = new ServerSideVerificationOptions
                          .Builder()
                          .SetCustomData("SAMPLE_CUSTOM_DATA_STRING")
                          .Build()
    rewardedAd.SetServerSideVerificationOptions(options);
  });
}

หากต้องการตั้งค่าสตริงรางวัลที่กำหนดเอง คุณต้องดำเนินการก่อนแสดงโฆษณา

การยืนยัน SSV ที่มีการให้รางวัลด้วยตนเอง

ขั้นตอนที่คลาส RewardedAdsVerifier ดำเนินการเพื่อยืนยัน SSV ที่มีรางวัลมีดังนี้ แม้ว่าข้อมูลโค้ดที่รวมอยู่จะเป็นภาษา Java และใช้ประโยชน์จากไลบรารีของบุคคลที่สามอย่าง Tink แต่คุณก็สามารถใช้ขั้นตอนเหล่านี้ในภาษาที่ต้องการได้โดยใช้ไลบรารีของบุคคลที่สามที่รองรับ ECDSA

ดึงข้อมูลคีย์สาธารณะ

หากต้องการยืนยันการเรียกกลับ SSV ที่มีรางวัล คุณต้องมีคีย์สาธารณะที่ AdMob ระบุ

รายการคีย์สาธารณะที่จะใช้ตรวจสอบความถูกต้องของ Callback ของ 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 ไปยังคีย์สาธารณะ ซึ่งจะรวมอยู่ในออบเจ็กต์ ECPublicKey จากไลบรารี Tink

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

รับการยืนยันเนื้อหา

พารามิเตอร์การค้นหา 2 รายการสุดท้ายของการเรียกกลับ SSV ที่มีรางวัลจะเป็น signature และ key_id, ตามลำดับเสมอ พารามิเตอร์การค้นหาที่เหลือจะระบุเนื้อหาที่จะได้รับการยืนยัน สมมติว่าคุณกําหนดค่า AdMob ให้ส่งการเรียกกลับรางวัลไปยัง https://www.myserver.com/mypath ตัวอย่างข้อมูลโค้ดด้านล่างแสดงการเรียกกลับ SSV แบบให้รางวัลพร้อมไฮไลต์เนื้อหาที่จะได้รับการยืนยัน

https://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins
&timestamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887

โค้ดด้านล่างแสดงวิธีแยกวิเคราะห์เนื้อหาที่จะยืนยันจาก URL การเรียกกลับเป็นอาร์เรย์ไบต์ UTF-8

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

รับลายเซ็นและ key_id จาก URL ของ Callback

ใช้ค่า queryString จากขั้นตอนก่อนหน้าเพื่อแยกวิเคราะห์พารามิเตอร์การค้นหา signature และ key_id จาก URL เรียกกลับดังที่แสดงด้านล่าง

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 และใช้พารามิเตอร์ key_id จาก URL การเรียกกลับเพื่อรับคีย์สาธารณะจากการแมปนั้น จากนั้นยืนยันลายเซ็นด้วยคีย์สาธารณะนั้น ขั้นตอนเหล่านี้แสดงอยู่ในวิธีการ 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 เรียกกลับได้รับการยืนยันเรียบร้อยแล้ว

คำถามที่พบบ่อย

ฉันจะแคชคีย์สาธารณะที่เซิร์ฟเวอร์คีย์ของ AdMob ระบุไว้ได้ไหม
เราขอแนะนําให้คุณแคชคีย์สาธารณะที่ให้บริการโดยเซิร์ฟเวอร์คีย์ AdMob เพื่อลดจํานวนการดําเนินการที่จําเป็นในการตรวจสอบการเรียกกลับ SSV อย่างไรก็ตาม โปรดทราบว่าคีย์สาธารณะจะเปลี่ยนอยู่เป็นประจำและไม่ควรแคชไว้นานกว่า 24 ชั่วโมง
คีย์สาธารณะที่เซิร์ฟเวอร์คีย์ของ AdMob ระบุมีการหมุนเวียนบ่อยแค่ไหน
คีย์สาธารณะที่เซิร์ฟเวอร์คีย์ของ AdMob ระบุจะหมุนเวียนตามกำหนดการแบบไม่แน่นอน คุณไม่ควรแคชคีย์สาธารณะไว้นานกว่า 24 ชั่วโมงเพื่อให้การยืนยันการเรียกกลับ SSV ทำงานต่อไปได้ตามที่ต้องการ
จะเกิดอะไรขึ้นหากเข้าถึงเซิร์ฟเวอร์ของฉันไม่ได้
Google คาดว่าจะได้รับรหัสการตอบกลับสถานะ "สำเร็จ" HTTP 200 OK สำหรับ SSV callbacks หากเข้าถึงเซิร์ฟเวอร์ไม่ได้หรือไม่มีการตอบกลับตามที่คาดไว้ Google จะพยายามส่งการติดต่อกลับ SSV อีกครั้งสูงสุด 5 ครั้งโดยเว้นระยะห่าง 1 วินาที
ฉันจะยืนยันได้อย่างไรว่าการเรียกกลับ SSV มาจาก Google
ใช้การค้นหา DNS แบบย้อนกลับเพื่อยืนยันว่าการเรียกกลับ SSV มาจาก Google