Xác thực các lệnh gọi lại xác thực phía máy chủ (SSV)

Lệnh gọi lại xác minh phía máy chủ là các yêu cầu URL, với tham số truy vấn do Google mở rộng, được Google gửi tới một hệ thống bên ngoài để thông báo rằng một người dùng cần được thưởng vì đã tương tác với một quảng cáo có tặng thưởng hoặc quảng cáo xen kẽ có tặng thưởng. Lệnh gọi lại SSV dành cho quảng cáo có tặng thưởng (xác minh phía máy chủ) cung cấp thêm một lớp bảo vệ chống lại hành vi giả mạo lệnh gọi lại phía máy khách để tặng thưởng cho người dùng.

Hướng dẫn này cho bạn biết cách xác minh lệnh gọi lại SSV dành cho quảng cáo có tặng thưởng bằng cách sử dụng thư viện mật mã bên thứ ba Ứng dụng Java của Tink để đảm bảo rằng tham số truy vấn trong lệnh gọi lại là giá trị hợp lệ. Mặc dù chúng tôi sử dụng Tink cho các mục đích của hướng dẫn này, nhưng bạn có thể sử dụng bất kỳ thư viện bên thứ ba nào có hỗ trợ ECDSA. Bạn cũng có thể kiểm tra máy chủ bằng công cụ kiểm thử trong giao diện người dùng AdMob.

Hãy xem ví dụ đầy đủ chức năng này (thực hiện bằng ứng dụng spring-boot dựa trên Java).

Điều kiện tiên quyết

Sử dụng RewardedAdsVerifier trong thư viện Ứng dụng Java của Tink

Kho lưu trữ Ứng dụng Java của Tink trên GitHub bao gồm một lớp trình trợ giúp RewardedAdsVerifier nhằm rút gọn đoạn mã cần thiết để xác minh lệnh gọi lại SSV dành cho quảng cáo có tặng thưởng. Khi sử dụng lớp này, bạn có thể xác minh URL của lệnh gọi lại bằng mã sau.

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

Nếu không phát sinh trường hợp ngoại lệ trong quá trình thực thi phương thức verify(), thì URL của lệnh gọi lại đã được xác minh thành công. Mục Trao thưởng cho người dùng trình bày một cách chi tiết các phương pháp hay nhất về thời điểm trao thưởng cho người dùng. Để biết thông tin chi tiết về các bước mà lớp này sẽ thực hiện để xác minh lệnh gọi lại SSV dành cho quảng cáo có tặng thưởng, bạn có thể tìm hiểu trong mục Quy trình xác minh SSV dành cho quảng cáo có tặng thưởng theo cách thủ công.

Các tham số gọi lại SSV

Lệnh gọi lại xác minh phía máy chủ chứa các tham số truy vấn mô tả hoạt động tương tác với quảng cáo có tặng thưởng. Bạn có thể xem danh sách liệt kê tên tham số, nội dung mô tả và giá trị mẫu ở bên dưới. Hệ thống sẽ gửi các tham số theo thứ tự bảng chữ cái.

Tên thông số Nội dung mô tả Giá trị mẫu
ad_network Giá trị nhận dạng nguồn quảng cáo cho nguồn quảng cáo đã thực hiện quảng cáo này. Bạn có thể xem danh sách tên nguồn quảng cáo tương ứng với giá trị mã nhận dạng trong phần Giá trị nhận dạng nguồn quảng cáo. 1953547073528090325
ad_unit Mã đơn vị quảng cáo AdMob được dùng để yêu cầu quảng cáo có tặng thưởng. 2747237135
custom_data Chuỗi dữ liệu tuỳ chỉnh do customRewardString .

Nếu ứng dụng không cung cấp chuỗi dữ liệu tùy chỉnh, thì giá trị tham số truy vấn này sẽ không hiển thị trong lệnh gọi lại SSV.

SAMPLE_CUSTOM_DATA_STRING
key_id Khóa được sử dụng để xác minh lệnh gọi lại SSV. Giá trị này liên kết với khóa công khai do máy chủ khóa AdMob cung cấp. 1234567890
reward_amount Số tiền thưởng như được chỉ định trong tùy chọn cài đặt đơn vị quảng cáo. 5
reward_item Vật phẩm thưởng như được chỉ định trong tùy chọn cài đặt đơn vị quảng cáo. xu
Chữ ký Chữ ký cho lệnh gọi lại SSV do AdMob tạo ra. MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
timestamp Dấu thời gian về thời điểm người dùng được tặng thưởng dưới dạng Epoch time tính bằng mili giây. 1507770365237823
transaction_id Giá trị nhận dạng được mã hóa hex duy nhất cho từng sự kiện cấp phần thưởng do AdMob tạo ra. 18fa792de1bca816048293fc71035638
user_id Giá trị nhận dạng người dùng do userIdentifier.

Nếu ứng dụng không cung cấp giá trị nhận dạng người dùng, thông số truy vấn này sẽ không hiển thị trong lệnh gọi lại SSV.

1234567

Giá trị nhận dạng nguồn quảng cáo

Tên và giá trị nhận dạng nguồn quảng cáo

Tên nguồn quảng cáo Mã nguồn quảng cáo
Aarki (đặt giá thầu)5240798063227064260
Ad Generation (đặt giá thầu)1477265452970951479
AdColony15586990674969969776
AdColony (không phải SDK) (đặt giá thầu)4600416542059544716
AdColony (đặt giá thầu)6895345910719072481
AdFalcon3528208921554210682
Mạng AdMob5450213213286189855
ADResult10593873382626181482
AMoAd17253994435944008978
Applovin1063618907739174004
Applovin (đặt giá thầu)1328079684332308356
Biểu đồ tăng cường2873236629771172317
Sô-cô-la (đặt giá thầu)6432849193975106527
Kênh chéo (MdotM)9372067028804390441
Sự kiện tuỳ chỉnh18351550913290782395
DT Exchange*
* Trước ngày 21 tháng 9 năm 2022, mạng này được gọi là "Fyber Marketplace".
2179455223494392917
EMX (đặt giá thầu)8497809869790333482
Fluct (đặt giá thầu)8419777862490735710
Flurry3376427960656545613
Fyber*
* Nguồn quảng cáo này được dùng để báo cáo dữ liệu trong quá khứ.
4839637394546996422
i-mobile5208827440166355534
Cải thiện nền tảng Kỹ thuật số (đặt giá thầu)159382223051638006
Index Exchange (đặt giá thầu)4100650709078789802
InMobi7681903010231960328
NavHost (đặt giá thầu)6325663098072678541
IronSource6925240245545091930
Leadbolt2899150749497968595
LG U+AD18298738678491729107
Mạng quảng cáo LINE3025503711505004547
hoa maio7505118203095108657
maio (đặt giá thầu)1343336733822567166
Media.net (đặt giá thầu)2127936450554446159
Quảng cáo tự quảng bá đã dàn xếp6060308706800320801
Meta Audience Network*
* Trước ngày 6 tháng 6 năm 2022, mạng này được gọi là "Facebook Audience Network".
10568273599589928883
Meta Audience Network (đặt giá thầu)*
* Trước ngày 6 tháng 6 năm 2022, mạng này được gọi là "Facebook Audience Network (đặt giá thầu)".
11198165126854996598
Bạc hà1357746574408896200
Mintegral (đặt giá thầu)6250601289653372374
MobFox8079529624516381459
MobFox (đặt giá thầu)3086513548163922365
MoPub (đã ngừng hoạt động)10872986198578383917
myTarget8450873672465271579
Nend9383070032774777750
ONE do AOL (Truyền thông thế hệ Y)6101072188699264581
ONE do AOL (Nexage)3224789793037044399
OneTag Exchange (đặt giá thầu)4873891452523427499
OpenX (đặt giá thầu)4918705482605678398
Pangle (đặt giá thầu)3525379893916449117
PubMatic (đặt giá thầu)3841544486172445473
Chiến dịch đặt trước7068401028668408324
RhythmOne (đặt giá thầu)2831998725945605450
Rubicon (đặt giá thầu)3993193775968767067
Hành tinh SK734341340207269415
Chia sẻ (đặt giá thầu)5247944089976324188
Smaato (đặt giá thầu)3362360112145450544
Equativ (đặt giá thầu)*

* Trước ngày 12 tháng 1 năm 2023, mạng này được gọi là "Smart AdServer".

5970199210771591442
Sonobi (đặt giá thầu)3270984106996027150
Tapjoy7295217276740746030
TapJoy (đặt giá thầu)4692500501762622178
GDT Tencent7007906637038700218
Triple Lift (đặt giá thầu)8332676245392738510
Quảng cáo Unity4970775877303683148
UnrulyX (đặt giá thầu)2831998725945605450
Phương tiện Verizon7360851262951344112
Verve Group (đặt giá thầu)5013176581647059185
Vpon1940957084538325905
Liftoff Monetize*

* Trước ngày 30 tháng 1 năm 2023, mạng này được gọi là "Vungle".

1953547073528090325
Liftoff Monetize (đặt giá thầu)*

* Trước ngày 30 tháng 1 năm 2023, mạng này được gọi là "Vungle (đặt giá thầu)".

4692500501762622185
Yieldmo (đặt giá thầu)4193081836471107579
YieldOne (đặt giá thầu)3154533971590234104
Zucks5506531810221735863

Tặng thưởng cho người dùng

Điều quan trọng là phải cân bằng trải nghiệm người dùng và xác thực phần thưởng khi quyết định thời điểm tặng thưởng cho người dùng. Các lệnh gọi lại phía máy chủ có thể bị trì hoãn trước khi tiếp cận các hệ thống bên ngoài. Do đó, phương pháp đề xuất hay nhất là sử dụng lệnh gọi lại phía máy khách để tặng thưởng cho người dùng ngay lập tức, trong khi thực hiện xác thực trên tất cả các phần thưởng khi nhận được lệnh gọi lại phía máy chủ. Phương pháp này mang lại trải nghiệm người dùng tốt trong khi vẫn đảm bảo tính hợp lệ của các phần thưởng được cấp.

Tuy nhiên, đối với các ứng dụng đòi hỏi tính hợp lệ của phần thưởng là rất quan trọng (ví dụ: phần thưởng ảnh hưởng đến kinh tế trong trò chơi của ứng dụng) và sự chậm trễ trong việc cấp phần thưởng có thể được chấp nhận, thì việc chờ lệnh gọi lại phía máy chủ được xác minh có thể là phương pháp tốt nhất.

Dữ liệu tùy chỉnh

Những ứng dụng cần có thêm dữ liệu trong lệnh gọi lại của cơ chế xác minh phía máy chủ nên sử dụng tính năng dữ liệu tuỳ chỉnh của quảng cáo có tặng thưởng. Bất kỳ giá trị chuỗi nào được đặt cho đối tượng quảng cáo có tặng thưởng đều sẽ được truyền đến tham số truy vấn custom_data cho lệnh gọi lại của SSV. Nếu bạn không đặt giá trị dữ liệu tuỳ chỉnh, thì giá trị tham số truy vấn custom_data sẽ không hiển thị trong lệnh gọi lại của SSV.

Mã mẫu sau đây minh hoạ cách đặt các lựa chọn của SSV sau khi quảng cáo có tặng thưởng được tải.

Swift

GADRewardedAd.load(withAdUnitID:"ca-app-pub-3940256099942544/1712485313",
                       request: request,
                       completionHandler: { [self] ad, error in
      if let error != error {
      rewardedAd = ad
      let options = GADServerSideVerificationOptions()
      options.customRewardString = "SAMPLE_CUSTOM_DATA_STRING"
      rewardedAd.serverSideVerificationOptions = options
    }

Objective-C

GADRequest *request = [GADRequest request];
[GADRewardedAd loadWithAdUnitID:@"ca-app-pub-3940256099942544/1712485313"
                        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;
              }];

Quy trình xác minh SSV dành cho quảng cáo có tặng thưởng theo cách thủ công

Bạn có thể xem các bước mà lớp RewardedAdsVerifier sẽ thực hiện để xác minh SSV dành cho quảng cáo có tặng thưởng bên dưới. Mặc dù đoạn mã được sử dụng nằm trong Java và sử dụng thư viện Tink của bên thứ ba, nhưng bạn có thể triển khai các bước này theo ngôn ngữ mà mình muốn bằng cách sử dụng bất kỳ thư viện bên thứ ba nào có hỗ trợ ECDSA.

Tìm nạp khóa công khai

Để xác minh lệnh gọi lại SSV dành cho quảng cáo có tặng thưởng, bạn cần có khóa công khai do AdMob cung cấp.

Bạn có thể tìm nạp danh sách khóa công khai mà hệ thống dùng để xác thực lệnh gọi lại SSV dành cho quảng cáo có tặng thưởng từ máy chủ khóa AdMob. Danh sách khóa công khai được cung cấp dưới dạng một JSON có định dạng tương tự như sau:

{
 "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=="
    },
  ],
}

Để truy xuất khóa công khai, hãy kết nối với máy chủ khóa AdMob và tải các khóa đó xuống. Mã sau đây giúp bạn hoàn thành công việc này và lưu phép biểu diễn JSON của các khóa đó vào biến 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();
}

Lưu ý: Chúng tôi sẽ thường xuyên xoay vòng các khóa công khai. Chúng tôi sẽ gửi email cho bạn để thông báo về việc xoay vòng sắp tới. Nếu đang lưu khóa công khai vào bộ nhớ đệm, bạn nên cập nhật các khóa đó khi nhận được email này.

Sau khi tìm nạp, các khóa công khai phải được phân tích cú pháp. Phương thức parsePublicKeysJson dưới đây sẽ lấy chuỗi JSON (như ví dụ ở trên) làm dữ liệu nhập và tạo mối liên kết từ giá trị key_id đến khoá công khai. Các khoá này sẽ được đóng gói dưới dạng đối tượng ECPublicKey trong thư viện 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;
}

Lấy nội dung cần xác minh

2 tham số truy vấn cuối cùng của lệnh gọi lại SSV dành cho quảng cáo có tặng thưởng luôn có thứ tự là signaturekey_id,. Các tham số truy vấn còn lại chỉ định nội dung cần xác minh. Giả sử bạn đã định cấu hình để AdMob gửi lệnh gọi lại phần thưởng đến https://www.myserver.com/mypath. Đoạn mã sau đây là một ví dụ về lệnh gọi lại SSV dành cho quảng cáo có tặng thưởng, trong đó làm nổi bật nội dung cần xác minh.

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

Mã dưới đây minh họa cách phân tích cú pháp nội dung cần được xác minh từ URL gọi lại dưới dạng mảng byte 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"));

Nhận chữ ký và key_id từ URL gọi lại

Sử dụng giá trị queryString từ bước trước, phân tích cú pháp thông số truy vấn signaturekey_id của URL gọi lại như bên dưới:

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

Thực hiện việc xác minh

Bước cuối cùng là xác minh nội dung của URL gọi lại bằng khóa công khai phù hợp. Hãy lấy đường liên kết mà phương thức parsePublicKeysJson trả về và sử dụng thông số key_id của URL gọi lại để nhận khóa công khai từ đường liên kết đó. Sau đó, hãy xác minh chữ ký bằng cách sử dụng khóa công khai đó. Bạn có thể xem các bước này bên dưới trong phương thức 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);
  }
}

Nếu không phát sinh trường hợp ngoại lệ trong quá trình thực thi phương thức này, thì URL gọi lại đã được xác minh thành công.

Câu hỏi thường gặp

Tôi có thể lưu khóa công khai do máy chủ khóa AdMob cung cấp vào bộ nhớ đệm không?
Bạn nên lưu khóa công khai do máy chủ khóa AdMob cung cấp vào bộ nhớ đệm nhằm giảm số lượng thao tác mà bạn phải thực hiện để xác thực lệnh gọi lại SSV. Tuy nhiên, xin lưu ý rằng hệ thống sẽ thường xuyên xoay vòng các khóa công khai, do đó, bạn chỉ nên lưu các khóa công khai vào bộ nhớ đệm trong thời gian không quá 24 giờ.
Tần suất xoay vòng của các khóa công khai do máy chủ khóa AdMob cung cấp như thế nào?
Hệ thống sẽ xoay vòng các khóa công khai do máy chủ khóa AdMob cung cấp với lịch biểu thường xuyên thay đổi. Để đảm bảo rằng quy trình xác minh lệnh gọi lại SSV vẫn hoạt động bình thường, bạn chỉ nên lưu các khóa công khai vào bộ nhớ đệm trong thời gian không quá 24 giờ.
Điều gì sẽ xảy ra nếu tôi không thể truy cập vào máy chủ?
Google dự kiến sử dụng mã phản hồi trạng thái thành công là HTTP 200 OK cho lệnh gọi lại SSV. Nếu bạn không thể truy cập vào máy chủ hoặc máy chủ không phản hồi như dự kiến, Google sẽ thử gửi lại các lệnh gọi lại SSV tối đa 5 lần trong khoảng thời gian một giây.
Làm cách nào để tôi có thể xác minh rằng các lệnh gọi lại SSV là của Google?
Bạn có thể sử dụng công cụ tra cứu DNS ngược để xác minh rằng các lệnh gọi lại SSV là của Google.