אימות בצד השרת

בחירת פלטפורמה: Android iOS Unity Flutter Android (Legacy)

קריאות חוזרות (callback) של אימות בצד השרת הן בקשות לכתובות URL, עם פרמטרים של שאילתה שמורחבים על ידי Google, שנשלחות על ידי Google למערכת חיצונית כדי להודיע לה שצריך לתגמל משתמש על אינטראקציה עם מודעה מתגמלת או עם מודעת מעברון מתגמלת. קריאות חוזרות (callback) של אימות בצד השרת (SSV) של מודעות מתגמלות מספקות שכבת הגנה נוספת מפני זיוף של קריאות חוזרות (callback) בצד הלקוח כדי לתגמל משתמשים.

במדריך הזה מוסבר איך לאמת קריאות חוזרות (callback) של SSV על מודעות מתגמלות באמצעות ספריית ההצפנה של צד שלישי Tink Java Apps, כדי לוודא שפרמטרים השאילתה בקריאה החוזרת הם ערכים לגיטימיים. למרות שבמדריך הזה נעשה שימוש ב-Tink, אפשר להשתמש בכל ספרייה של צד שלישי שתומכת ב-ECDSA. אפשר גם לבדוק את השרת באמצעות כלי הבדיקה בממשק המשתמש של AdMob.

דרישות מוקדמות

שימוש ב-RewardedAdsVerifier מהספרייה Tink Java Apps

מאגר GitHub‏ Tink Java Apps כולל מחלקה מסייעת RewardedAdsVerifier שמצמצמת את כמות הקוד שנדרשת לאימות קריאה חוזרת (callback) של SSV עם תגמול. אפשר להשתמש במחלקה הזו כדי לאמת כתובת URL של קריאה חוזרת באמצעות הקוד הבא.

RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
    .fetchVerifyingPublicKeysWith(
        RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
    .build();
String rewardUrl = ...;
verifier.verify(rewardUrl);

אם הפונקציה verify() מופעלת בלי להפעיל חריגה, כתובת ה-URL של הקריאה החוזרת אומתה בהצלחה. בקטע תגמול המשתמש מפורטות שיטות מומלצות לגבי המקרים שבהם כדאי לתגמל את המשתמשים. כדי לקבל פירוט של השלבים שהמחלקה הזו מבצעת כדי לאמת קריאות חוזרות (callback) של SSV מתגמלות, אפשר לקרוא את הקטע אימות ידני של SSV מתגמלות.

פרמטרים של קריאה חוזרת (callback) בצד השרת

קריאות חוזרות (callback) של אימות בצד השרת מכילות פרמטרים של שאילתה שמתארים את האינטראקציה עם המודעה המתגמלת. בהמשך מפורטים שמות הפרמטרים, התיאורים והערכים לדוגמה. הפרמטרים נשלחים בסדר אלפביתי.

שם פרמטר תיאור ערך לדוגמה
ad_network מזהה מקור המודעות של מקור המודעות שממנו הגיעה המודעה הזו. שמות מקורות המודעות שמתאימים לערכי המזהים מפורטים בקטע מזהים של מקורות מודעות. 1953547073528090325
ad_unit מזהה יחידת המודעות ב-AdMob ששימש לבקשת המודעה המתגמלת. 2747237135
custom_data מחרוזת נתונים בהתאמה אישית כפי שסופקה על ידי customRewardString.

אם האפליקציה לא מספקת מחרוזת של נתונים מותאמים אישית, הערך של פרמטר השאילתה הזה לא יופיע בקריאה החוזרת ל-SSV.

SAMPLE_CUSTOM_DATA_STRING
key_id המפתח שישמש לאימות הקריאה החוזרת של SSV. הערך הזה ממופה למפתח ציבורי שסופק על ידי שרת המפתחות של AdMob. 1234567890
reward_amount סכום התגמול שצוין בהגדרות של יחידת המודעות. 5
reward_item פריט התגמול כפי שצוין בהגדרות של יחידת המודעות. מטבעות
signature חתימה של קריאה חוזרת ל-SSV שנוצרה על ידי AdMob. MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
חותמת זמן חותמת הזמן של מועד התגמול של המשתמש, כזמן Epoch באלפיות שנייה. 1507770365237823
transaction_id מזהה ייחודי בקידוד הקסדצימלי לכל אירוע הענקת תגמול שנוצר על ידי AdMob. 18fa792de1bca816048293fc71035638
user_id מזהה המשתמש כפי שסופק על ידי userIdentifier.

אם האפליקציה לא מספקת מזהה משתמש, פרמטר השאילתה הזה לא יופיע בקריאה החוזרת ל-SSV.

1234567

מזהים של מקורות למודעות

שמות ומזהים של מקורות מודעות

שם מקור המודעה מזהה מקור המודעות
Ad Generation (bidding)1477265452970951479
רשת AdMob5450213213286189855
‫Waterfall של רשת AdMob1215381445328257950
AppLovin1063618907739174004
AppLovin (בידינג)1328079684332308356
Bidease (בידינג)3670825090829827805
BidMachine (בידינג)7943972370566394673
Chartboost2873236629771172317
Chocolate Platform (bidding)6432849193975106527
אירוע מותאם אישית18351550913290782395
DT Exchange*

* לפני 21 בספטמבר 2022, הרשת הזו נקראה 'Fyber Marketplace'.

2179455223494392917
DT Exchange (בידינג)8189833498765234879
Equativ (bidding)*

* עד 12 בינואר 2023, הרשת הזו נקראה Smart Adserver.

5970199210771591442
‫Fluct (בידינג)8419777862490735710
i-mobile5208827440166355534
Improve Digital (בידינג)159382223051638006
Index Exchange (בידינג)4100650709078789802
InMobi7681903010231960328
‫InMobi ‏ (SDK) (בידינג)8468954295581492586
‫InMobi Exchange (בידינג)5264320421916134407
‫ironSource Ads6925240245545091930
‫ironSource Ads (בידינג)1643326773739866623
Liftoff Monetize*

* לפני 30 בינואר 2023, הרשת הזו נקראה Vungle.

1953547073528090325
‫Liftoff Monetize (בידינג)*

* לפני 30 בינואר 2023, הרשת הזו נקראה Vungle (bidding).

4692500501762622185
LY Ads Network3025503711505004547
‫LY Ads Network (בידינג)2615812619460460513
Magnite DV+‎ (בידינג)3993193775968767067
maio7505118203095108657
Media.net (בידינג)2127936450554446159
מודעות בית בתהליך בחירת הרשת6060308706800320801
Meta Audience Network*

* לפני 6 ביוני 2022, הרשת הזו נקראה Facebook Audience Network.

10568273599589928883
Meta Audience Network (בידינג)*

* לפני 6 ביוני 2022, הרשת הזו נקראה Facebook Audience Network (bidding).

11198165126854996598
Mintegral1357746574408896200
Mintegral (בידינג)6250601289653372374
Mobfox (בידינג)3086513548163922365
MobileFuse (בידינג)7303547408604090310
Moloco Ads SDK (bidding)8267622065755668722
myTarget8450873672465271579
Nativo (בידינג)3240503836211327896
Nexxen (bidding)*

* עד 1 במאי 2024, הרשת הזו נקראה UnrulyX.

2831998725945605450
OneTag Exchange (בידינג)4873891452523427499
OpenX (בידינג)4918705482605678398
Pangle4069896914521993236
‫Pangle KR SDK (בידינג)12171279046073404914
‫Pangle ROW SDK (בידינג)3525379893916449117
‫Pangle US SDK (בידינג)15999446638585856012
PubMatic (בידינג)3841544486172445473
PubMatic OpenWrap SDK7702975372504485373
‫PubMatic OpenWrap SDK (בידינג)1234567890123456789
קמפיין עם מיקומים מוזמנים7068401028668408324
עלייה (בידינג)6816468518946650043
Sharethrough (בידינג)5247944089976324188
Smaato (בידינג)3362360112145450544
Sonobi (בידינג)3270984106996027150
TripleLift (בידינג)8332676245392738510
Unity Ads4970775877303683148
Unity Ads (בידינג)7069338991535737586
Verve Group (בידינג)5013176581647059185
Vpon1940957084538325905
Yieldmo (בידינג)4193081836471107579
YieldOne (bidding)3154533971590234104
Zucks5506531810221735863

תגמול המשתמש

כשמחליטים מתי לתגמל משתמש, חשוב ליצור איזון בין חוויית המשתמש לבין אימות התגמול. יכול להיות שיהיו עיכובים בהגעת קריאות חוזרות (callback) בצד השרת למערכות חיצוניות. לכן, השיטה המומלצת היא להשתמש בקריאה חוזרת בצד הלקוח כדי לתגמל את המשתמש באופן מיידי, ובמקביל לבצע אימות של כל התגמולים עם קבלת קריאות חוזרות בצד השרת. הגישה הזו מספקת חוויית משתמש טובה, וגם מוודאת שהתגמולים שניתנו תקפים.

עם זאת, באפליקציות שבהן תוקף הפרס הוא קריטי (לדוגמה, אם הפרס משפיע על הכלכלה במשחק באפליקציה) ועיכובים במתן הפרסים הם מקובלים, יכול להיות שהגישה הכי טובה היא לחכות לקריאה החוזרת (callback) מאומתת בצד השרת.

נתונים בהתאמה אישית

באפליקציות שנדרשים בהן נתונים נוספים בקריאות חוזרות (callback) של אימות בצד השרת (SSV), צריך להשתמש בתכונה 'נתונים מותאמים אישית' של מודעות מתגמלות. כל ערך מחרוזת שמוגדר באובייקט של מודעה מתגמלת מועבר לפרמטר השאילתה custom_data של הקריאה החוזרת ל-SSV. אם לא מוגדר ערך של נתונים מותאמים אישית, הערך של פרמטר השאילתה custom_data לא יופיע בקריאה החוזרת ל-SSV.

בדוגמה הבאה מוצגות הגדרות של אפשרויות SSV אחרי שהמודעה המתגמלת נטענת:

Swift

RewardedAd.load(with:"AD_UNIT_ID",
                       request: request,
                       completionHandler: { [self] ad, error in
      if let error != error {
      rewardedAd = ad
      let options = ServerSideVerificationOptions()
      options.customRewardString = "SAMPLE_CUSTOM_DATA_STRING"
      rewardedAd.serverSideVerificationOptions = options
    }
})

Objective-C

GADRequest *request = [GADRequest request];
[GADRewardedAd loadWithAdUnitID:@"AD_UNIT_ID"
                        request:request
              completionHandler:^(GADRewardedAd *ad, NSError *error) {
                if (error) {
                  // Handle Error
                  return;
                }
                self.rewardedAd = ad;
                GADServerSideVerificationOptions *options =
                    [[GADServerSideVerificationOptions alloc] init];
                options.customRewardString = @"SAMPLE_CUSTOM_DATA_STRING";
                ad.serverSideVerificationOptions = options;
              }];

אימות ידני של אימות בצד השרת לצפייה במודעות מתגמלות

בהמשך מפורטים השלבים שמבצעת המחלקה RewardedAdsVerifier כדי לאמת SSV עם תגמול. קטעי הקוד שמופיעים כאן הם ב-Java ומתבססים על ספריית Tink של צד שלישי, אבל אתם יכולים ליישם את השלבים האלה בשפה שתבחרו, באמצעות כל ספרייה של צד שלישי שתומכת ב-ECDSA.

אחזור מפתחות ציבוריים

כדי לאמת קריאה חוזרת (callback) של 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;
}

איך מקבלים תוכן לאימות

שני פרמטרי השאילתה האחרונים של קריאות חוזרות ל-SSV של מודעות מתגמלות הם תמיד signature ו-key_id,, בסדר הזה. שאר הפרמטרים של השאילתה מציינים את התוכן שצריך לאמת. נניח שהגדרתם את AdMob לשליחת קריאות חוזרות (callback) על תגמולים אל 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 לקריאה חוזרת (callback) באמצעות המפתח הציבורי המתאים. לוקחים את המיפוי שמוחזר מהשיטה 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 עוברים רוטציה בלוח זמנים משתנה. כדי לוודא שהאימות של קריאות חוזרות (callback) של SSV ימשיך לפעול כמצופה, לא מומלץ לשמור במטמון מפתחות ציבוריים למשך יותר מ-24 שעות.
מה קורה אם אי אפשר להגיע לשרת שלי?
Google מצפה לקבל קוד תגובה HTTP 200 OK בתגובה להחזרות (callback) של SSV. אם אי אפשר להגיע לשרת או שהוא לא מספק את התשובה הצפויה, Google תנסה שוב לשלוח קריאות חוזרות של SSV עד חמש פעמים במרווחי זמן של שנייה אחת.
איך אפשר לוודא שהקריאות החוזרות של אימות בצד השרת (SSV) מגיעות מ-Google?
משתמשים בשאילתת DNS הפוכה כדי לוודא שהקריאות החוזרות של SSV מגיעות מ-Google.