การเรียกกลับการยืนยันฝั่งเซิร์ฟเวอร์คือคําขอ URL ที่มีพารามิเตอร์การค้นหาซึ่ง Google ขยายให้ ซึ่ง Google จะส่งไปยังระบบภายนอกเพื่อแจ้งให้ทราบว่าผู้ใช้ควรได้รับรางวัลจากการโต้ตอบกับโฆษณาคั่นระหว่างหน้าที่มีการให้รางวัลหรือโฆษณาที่มีการให้รางวัล โค้ดเรียกกลับ SSV (การยืนยันฝั่งเซิร์ฟเวอร์) ที่มีการให้รางวัลให้การป้องกันอีกชั้นจากการปลอมแปลงโค้ดเรียกกลับฝั่งไคลเอ็นต์เพื่อตอบแทนผู้ใช้
คู่มือนี้แสดงวิธียืนยันโค้ดเรียกกลับ SSV ที่มีการให้รางวัลโดยใช้ไลบรารีการเข้ารหัสของบุคคลที่สามของ Tink Java Apps เพื่อให้แน่ใจว่าพารามิเตอร์การค้นหาในโค้ดเรียกกลับเป็นค่าที่ถูกต้อง แม้ว่า Tink จะใช้เพื่อวัตถุประสงค์ของคู่มือนี้ แต่คุณก็มีตัวเลือกในการใช้ไลบรารีของบุคคลที่สามที่รองรับ ECDSA นอกจากนี้ คุณยังทดสอบเซิร์ฟเวอร์ด้วยเครื่องมือทดสอบใน UI ของ AdMob ได้อีกด้วย
ดูตัวอย่างที่ใช้งานได้จริงนี้โดยใช้ Java Springt-boot
สิ่งที่ต้องดำเนินการก่อน
ผสานรวมโฆษณาที่มีการให้รางวัลไว้ในแอปบนอุปกรณ์เคลื่อนที่ด้วย SDK โฆษณาในอุปกรณ์เคลื่อนที่ของ Google เวอร์ชัน v7.28.0 ขึ้นไปสําหรับ SDK โฆษณาในอุปกรณ์เคลื่อนที่ของ Google
เปิดใช้การยืนยันฝั่งเซิร์ฟเวอร์ที่มีการให้รางวัลในหน่วยโฆษณา
ใช้ RewardAdsVerifier จากไลบรารี Tink Java Apps
ที่เก็บ Tink Java Appsที่เก็บ GitHub
มี
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
โค้ดเรียกกลับการยืนยันฝั่งเซิร์ฟเวอร์มีพารามิเตอร์การค้นหาที่อธิบายการโต้ตอบกับโฆษณาที่มีการให้รางวัล ชื่อพารามิเตอร์ คําอธิบาย และค่าพารามิเตอร์ จะแสดงไว้ด้านล่างนี้ ระบบจะส่งพารามิเตอร์ตามลําดับตัวอักษร
ชื่อพารามิเตอร์ | คำอธิบาย | ค่าตัวอย่าง |
---|---|---|
เครือข่ายโฆษณา | ตัวระบุแหล่งที่มาของโฆษณาสําหรับแหล่งที่มาของโฆษณานี้ ชื่อแหล่งที่มาของโฆษณาที่สอดคล้องกับค่ารหัสจะแสดงอยู่ในส่วนตัวระบุแหล่งที่มาของโฆษณา | 1953547073528090325 |
หน่วยโฆษณา | รหัสหน่วยโฆษณา AdMob ที่ใช้ในการขอโฆษณาที่มีการให้รางวัล | 2747237135 |
ข้อมูลที่กําหนดเอง | สตริงข้อมูลที่กําหนดเองโดย
customRewardString
หากไม่มีแอปให้บริการสตริงข้อมูลที่กําหนดเอง ค่าพารามิเตอร์การค้นหานี้จะไม่แสดงในการเรียกกลับ SSV |
SAMPLE_CUSTOM_DATA_STRING |
รหัสคีย์ | คีย์ที่จะใช้เพื่อยืนยันการเรียกกลับ SSV ค่านี้จะแมปกับคีย์สาธารณะที่เซิร์ฟเวอร์คีย์ AdMob ระบุไว้ | 1234567890 |
จํานวนรางวัล | จํานวนรางวัลที่ระบุในการตั้งค่าหน่วยโฆษณา | 5 |
รางวัล_สินค้า | ได้รางวัลตามที่ระบุไว้ในการตั้งค่าหน่วยโฆษณา | เหรียญ |
ลายเซ็น | ลายเซ็นสําหรับการเรียกกลับ SSV ที่ AdMob สร้างขึ้น | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
การประทับเวลา | การประทับเวลาที่ผู้ใช้จะได้รับรางวัลเป็นเวลา Epoch ในหน่วยมิลลิวินาที | 1507770365237823 |
transaction_id | ตัวระบุที่เข้ารหัสแบบเลขฐานสิบหกที่ไม่ซ้ํากันสําหรับเหตุการณ์การมอบรางวัลแต่ละครั้งที่ AdMob สร้างขึ้น | 18fa792de1bca816048293fc71035638 |
user_id | ตัวระบุผู้ใช้จาก
userIdentifier
หากแอปไม่ได้ให้ตัวระบุผู้ใช้ไว้ พารามิเตอร์การค้นหานี้จะไม่อยู่ในโค้ดเรียกกลับ SSV |
1234567 |
ตัวระบุแหล่งที่มาของโฆษณา
ชื่อและแหล่งที่มาของโฆษณา
ชื่อแหล่งที่มาของโฆษณา | รหัสแหล่งที่มาของโฆษณา |
---|---|
Aarki (การเสนอราคา) | 5240798063227064260 |
การสร้างโฆษณา (การเสนอราคา) | 1477265452970951479 |
AdColony | 15586990674969969776 |
AdColony (ไม่ใช่ SDK) (การเสนอราคา) | 4600416542059544716 |
AdColony (การเสนอราคา) | 6895345910719072481 |
AdFalcon | 3528208921554210682 |
เครือข่าย AdMob | 5450213213286189855 |
ผลลัพธ์โฆษณา | 10593873382626181482 |
โฆษณา AMo | 17253994435944008978 |
แอปโลฟิน | 1063618907739174004 |
Applovin (การเสนอราคา) | 1328079684332308356 |
แผนภูมิ | 2873236629771172317 |
แพลตฟอร์มช็อกโกแลต (การเสนอราคา) | 6432849193975106527 |
หลายแชแนล (MdotM) | 9372067028804390441 |
เหตุการณ์ที่กำหนดเอง | 18351550913290782395 |
DT Exchange ก่อนวันที่ 21 กันยายน 2022 เครือข่ายนี้เรียกว่า "Fyber" | 4839637394546996422 |
Fluct (การเสนอราคา) | 8419777862490735710 |
Flurry | 3376427960656545613 |
i-mobile | 5208827440166355534 |
ปรับปรุงดิจิทัล (การเสนอราคา) | 159382223051638006 |
Index Exchange (การเสนอราคา) | 4100650709078789802 |
InMobi | 7681903010231960328 |
InMobi (การเสนอราคา) | 6325663098072678541 |
ธาตุเหล็ก | 6925240245545091930 |
Leadbolt | 2899150749497968595 |
LG U+โฆษณา | 18298738678491729107 |
Maio | 7505118203095108657 |
maio (การเสนอราคา) | 1343336733822567166 |
Media.net (การเสนอราคา) | 2127936450554446159 |
โฆษณาเฮาส์แอ็ดที่ใช้สื่อกลาง | 6060308706800320801 |
Meta Audience Network ก่อนวันที่ 6 มิถุนายน 2022 เครือข่ายนี้เรียกว่า "Facebook Audience Network" | 10568273599589928883 |
Meta Audience Network (การเสนอราคา) ก่อนวันที่ 6 มิถุนายน 2022 เครือข่ายนี้เรียกว่า "Facebook Audience Network (bidding)" | 11198165126854996598 |
MobFox | 8079529624516381459 |
MoPub (เลิกใช้งาน) | 10872986198578383917 |
เป้าหมายของฉัน | 8450873672465271579 |
Nend | 9383070032774777750 |
ONE by AOL (Millennial Media) | 6101072188699264581 |
ONE by AOL (Nexage) | 3224789793037044399 |
OpenX (การเสนอราคา) | 4918705482605678398 |
Pangle (การเสนอราคา) | 3525379893916449117 |
PubMatic (การเสนอราคา) | 3841544486172445473 |
แคมเปญแบบจองล่วงหน้า | 7068401028668408324 |
RhythmOne (การเสนอราคา) | 2831998725945605450 |
Rubicon (การเสนอราคา) | 3993193775968767067 |
ดาวเคราะห์ SK | 734341340207269415 |
ส่วนแบ่งการตลาด (การเสนอราคา) | 5247944089976324188 |
Smaato (การเสนอราคา) | 3362360112145450544 |
อิเควทิฟ (การเสนอราคา)* * ก่อนวันที่ 12 มกราคม 2023 เครือข่ายนี้เรียกว่า "Smart Adserver" | 5970199210771591442 |
Sonobi (การเสนอราคา) | 3270984106996027150 |
Tapjoy | 7295217276740746030 |
Tapjoy (การเสนอราคา) | 4692500501762622178 |
GDT สมบูรณ์ | 7007906637038700218 |
TripleLift (การเสนอราคา) | 8332676245392738510 |
โฆษณา Unity | 4970775877303683148 |
UnrulyX (การเสนอราคา) | 2831998725945605450 |
Verizon Media | 7360851262951344112 |
VPN | 1940957084538325905 |
การสร้างรายได้จาก Brand Lift* * ก่อนวันที่ 30 มกราคม 2023 เครือข่ายนี้เรียกว่า "Vungle" | 1953547073528090325 |
การสร้างรายได้ที่เพิ่มขึ้น (การเสนอราคา)* * ก่อนวันที่ 30 มกราคม 2023 เครือข่ายนี้เรียกว่า "Vungle" | 4692500501762622185 |
Yieldmo (การเสนอราคา) | 4193081836471107579 |
YieldOne (การเสนอราคา) | 3154533971590234104 |
ซูกส์ | 5506531810221735863 |
การให้รางวัลผู้ใช้
การสร้างสมดุลระหว่างประสบการณ์ของผู้ใช้กับการตรวจสอบรางวัลมีความสําคัญเมื่อตัดสินใจว่าจะให้รางวัลผู้ใช้เมื่อใด โค้ดเรียกกลับฝั่งเซิร์ฟเวอร์อาจทํางานล่าช้าก่อนที่จะเข้าถึงระบบภายนอก ดังนั้นแนวทางปฏิบัติแนะนําคือให้ใช้โค้ดเรียกกลับฝั่งไคลเอ็นต์เพื่อตอบแทนผู้ใช้ทันที โดยอาจใช้การตรวจสอบรางวัลทั้งหมดเมื่อได้รับการเรียกกลับฝั่งเซิร์ฟเวอร์ วิธีการนี้มอบประสบการณ์ที่ดีแก่ผู้ใช้ไปพร้อมๆ กับการตรวจสอบความถูกต้องของรางวัลที่มอบให้
อย่างไรก็ตาม สําหรับแอปพลิเคชันที่มีความถูกต้องของรางวัล (เช่น รางวัลจะมีผลกับเศรษฐกิจในเกมของแอป) และความล่าช้าในการให้รางวัลเป็นสิ่งที่ยอมรับได้ การรอโค้ดเรียกกลับจากฝั่งเซิร์ฟเวอร์ที่ยืนยันแล้วอาจเหมาะสมที่สุด
ข้อมูลที่กำหนดเอง
แอปที่ต้องมีข้อมูลเพิ่มเติมในการเรียกกลับสําหรับการยืนยันฝั่งเซิร์ฟเวอร์ควรใช้ฟีเจอร์ข้อมูลที่กําหนดเองของโฆษณาที่มีการให้รางวัล ระบบจะส่งค่าสตริงที่กําหนดไว้ในออบเจ็กต์โฆษณาที่มีการให้รางวัลไปยังพารามิเตอร์การค้นหา custom_data
ของโค้ดเรียกกลับ SSV หากไม่ได้กําหนดค่าข้อมูลที่กําหนดเองไว้ ค่าพารามิเตอร์การค้นหา custom_data
จะไม่แสดงในโค้ดเรียกกลับ SSV
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีตั้งค่าตัวเลือก SSV หลังจากที่โหลดโฆษณาที่มีการให้รางวัลแล้ว
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; }];
การยืนยัน SSV ที่มีการให้รางวัลด้วยตนเอง
โปรดดูขั้นตอนที่ RewardedAdsVerifier
ดําเนินการเพื่อยืนยัน SSV ที่มีการให้รางวัลที่ด้านล่างนี้ แม้ว่าข้อมูลโค้ดที่รวมไว้จะอยู่ใน Java และใช้ประโยชน์จากไลบรารีของบุคคลที่สามของ Tink แต่คุณใช้ขั้นตอนเหล่านี้ในภาษาที่คุณเลือกได้โดยใช้ไลบรารีของบุคคลที่สามที่รองรับ ECDSA
ดึงข้อมูลคีย์สาธารณะ
หากต้องการยืนยันการเรียกกลับ 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
ไปยังคีย์สาธารณะ ซึ่งรวมไว้ในออบเจ็กต์ 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 ×tamp=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 โค้ดเรียกกลับ
ใช้ค่า 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 หมุนเวียนหมุนเวียนตามกําหนดการตัวแปร เพื่อให้มั่นใจว่าการยืนยันการเรียกกลับสําหรับ SSV ยังคงดําเนินต่อไปตามที่ตั้งใจ ไม่ควรแคชคีย์สาธารณะไว้นานกว่า 24 ชั่วโมง
- จะเกิดอะไรขึ้นหากเข้าถึงเซิร์ฟเวอร์ของฉันไม่ได้
- Google ต้องการโค้ดตอบกลับสถานะความสําเร็จ
HTTP 200 OK
สําหรับการเรียกกลับ SSV หากเข้าถึงเซิร์ฟเวอร์ไม่ได้หรือไม่ให้การตอบสนองที่คาดไว้ Google จะพยายามส่งโค้ดเรียกกลับ SSV อีกครั้งสูงสุด 5 ครั้งในระยะเวลา 1 วินาที - ฉันจะยืนยันได้อย่างไรว่าการติดต่อกลับ SSV มาจาก Google
- ใช้การค้นหา DNS แบบย้อนกลับเพื่อยืนยันว่าการเรียกกลับ SSV มาจาก Google