เวิร์กโฟลว์ SKAdNetwork S2S: วิธีการตรวจสอบโพสต์แบ็กจากเครือข่ายโฆษณา

opoinstall
2026-08-21
5 min read

DSP จัดการกับโพสต์แบ็ก SKAdNetwork อย่างไร? แพลตฟอร์มฝั่งอุปสงค์ (DSP) และเครือข่ายโฆษณาจัดการกับโพสต์แบ็ก SKAdNetwork โดยการตั้งค่าจุดสิ้นสุดการรับข้อมูล HTTP POST ที่ปลอดภัย สร้างสตริงข้อความ UTF-8 ที่จัดเรียงแล้วโดยใช้ตัวคั่น U+2063 ตรวจสอบลายเซ็นเข้ารหัส ECDSA P-256 ของ Apple เทียบกับกุญแจสาธารณะที่ Apple เผยแพร่ และบันทึกรหัสธุรกรรมที่ตรวจสอบแล้วเพื่อป้องกันการประมวลผลซ้ำก่อนที่จะอัปเดตโมเดลการประมูล

โพสต์แบ็กยืนยันการติดตั้งของ SKAdNetwork คือการแจ้งเตือน HTTPS POST ที่ลงนามโดย Apple ซึ่งระบบปฏิบัติการจะส่งไปยังเครือข่ายโฆษณาที่มีสิทธิ์ และสำหรับกรณีที่การระบุแหล่งที่มาสำเร็จ (winning attributions) จะส่งไปยังจุดสิ้นสุดสำเนาที่กำหนดค่าไว้ของผู้พัฒนาแอปพลิเคชันที่ลงโฆษณาด้วย เพื่อความสมบูรณ์ของข้อมูล ระบบรับข้อมูลแบ็กเอนด์จะต้องตรวจสอบลายเซ็น ECDSA P-256 ของ Apple ตรวจความถูกต้องของการจัดเรียงพารามิเตอร์ และบังคับใช้การขจัดข้อมูลซ้ำในระดับธุรกรรม

คำศัพท์ คำนิยาม
SKAdNetwork เฟรมเวิร์กในระดับแพลตฟอร์มของ Apple สำหรับการระบุแหล่งที่มาแคมเปญโฆษณาที่รักษาความเป็นส่วนตัว
Install-Validation Postback เพย์โหลด JSON ที่ลงนามโดย Apple ซึ่งประกอบด้วยข้อมูลเมตาดาต้าการยืนยันการติดตั้งและการระบุแหล่งที่มาหลังจากการแปลงโฆษณาที่ผ่านเกณฑ์
ECDSA P-256 อัลกอริทึมการเข้ารหัสลับแบบเส้นโค้งวงรีที่ Apple ใช้เพื่อลงนามโพสต์แบ็กยืนยันการติดตั้ง
Transaction ID ตัวระบุการตรวจสอบที่ไม่ซ้ำกัน ซึ่งระบบปลายทางใช้เป็นคีย์ความสมมูล (idempotency key) สำหรับการตรวจจับข้อมูลที่ซ้ำกัน

สถาปัตยกรรมการรับโพสต์แบ็ก SKAdNetwork สำหรับ DSP และเครือข่ายโฆษณา

ท่อส่งข้อมูลการรับสองทาง: การส่งตรงไปยังเครือข่ายโฆษณาเทียบกับจุดสิ้นสุดโพสต์แบ็กของผู้พัฒนา

เมื่อมีการติดตั้งแอปพลิเคชัน iOS ที่มีการระบุแหล่งที่มาเกิดขึ้น ระบบย่อยการระบุแหล่งที่มาของ Apple จะส่งโพสต์แบ็กยืนยันการติดตั้งผ่าน HTTPS POST:

  • การรับข้อมูลของเครือข่ายโฆษณา: อุปกรณ์จะส่งโพสต์แบ็กหลักที่ชนะ (did-win: true) ไปยัง URL ของเซิร์ฟเวอร์ที่ลงทะเบียนภายใต้ ad-network-id ที่สอดคล้องกันในรีจิสทรีของ Apple โดยตรง
  • การรับข้อมูลสำเนาของผู้พัฒนา: หากแอปพลิเคชันที่ลงโฆษณาระบุคีย์ NSAdvertisingAttributionReportEndpoint ไว้ในไฟล์ Info.plist อุปกรณ์จะส่งสำเนาที่ตรงกันทุกประการของโพสต์แบ็กที่ชนะไปยังเซิร์ฟเวอร์ของผู้พัฒนาพร้อมกันด้วย
  • การส่งเส้นทางโพสต์แบ็กที่ไม่ชนะ: เริ่มตั้งแต่ SKAdNetwork 3.0 หากมีหลายเครือข่ายโฆษณาที่มีคุณสมบัติผ่านเกณฑ์การระบุแหล่งที่มาแต่ไม่ชนะ อุปกรณ์จะส่งโพสต์แบ็กที่ไม่ชนะสูงสุดถึงห้ารายการ (did-win: false) ไปยังเครือข่ายโฆษณาสำรองที่มีคุณสมบัติเหล่านั้นโดยตรง โดยโพสต์แบ็กที่ไม่ชนะจะไม่ถูกส่งไปยังจุดสิ้นสุดสำเนาของผู้พัฒนา

จุดสิ้นสุดการรับข้อมูลแบ็กเอนด์ควรตอบกลับด้วย HTTP 200 OK หากอุปกรณ์ไม่ได้รับ 200 กลับมา อุปกรณ์อาจลองส่งใหม่อีกครั้งสูงสุดถึงเก้าครั้งภายในระยะเวลาสูงสุดเก้าวัน

เวิร์กโฟลว์การรับและตรวจสอบโพสต์แบ็ก SKAdNetwork

บทบาทของ NSAdvertisingAttributionReportEndpoint ในการตรวจสอบของผู้พัฒนา

NSAdvertisingAttributionReportEndpoint ช่วยให้ผู้พัฒนาแอปสามารถรับสำเนาโพสต์แบ็กที่ชนะได้โดยตรง โดยไม่ต้องพึ่งพาการส่งต่อจากเครือข่ายโฆษณา:

  • การตรวจสอบอิสระ: ผู้พัฒนาจะได้รับสำเนาที่แน่นอนของโพสต์แบ็กที่ชนะทั้งหมดที่สร้างขึ้นสำหรับแอปพลิเคชันของตน ซึ่งช่วยให้สามารถตรวจสอบรายงานของเครือข่ายโฆษณาภายในได้
  • เส้นทางจุดสิ้นสุดเฉพาะ: เซิร์ฟเวอร์ของผู้พัฒนาต้องโฮสต์จุดสิ้นสุดที่ https://<domain>/.well-known/skadnetwork/report-attribution/
  • ความแตกต่างของ AdAttributionKit: สำหรับ AdAttributionKit ทาง Apple ได้กำหนดค่าเส้นทางแยกต่างหากไปยัง https://<domain>/.well-known/appattribution/report-attribution/ ซึ่งใช้สถาปัตยกรรมการตรวจสอบความถูกต้องแบบ JSON Web Signature (JWS)

วิธีที่ MMP รับ รวบรวม และปรับมาตรฐานสตรีมเหตุการณ์ S2S จากหลายเครือข่าย

ขึ้นอยู่กับการบูรกราการทางธุรกิจ พันธมิตรการวัดผลมือถือ (MMP) อาจรับข้อมูล SKAdNetwork ผ่านการส่งต่อฝั่งผู้พัฒนา การบูรณาการกับเครือข่ายโฆษณา หรือโฟลว์เซิร์ฟเวอร์พาร์ทเนอร์ที่กำหนดเอง:

  • การรับข้อมูลจากหลายแหล่ง: การรับข้อมูลโพสต์แบ็กที่ตรวจสอบแล้วซึ่งส่งต่อมาจากจุดสิ้นสุดของผู้พัฒนาควบคู่ไปกับสตรีมรายงานจากเครือข่ายโฆษณาโดยตรง
  • การขขจัดความซ้ำซ้อนข้ามสตรีม: การปรับมาตรฐานและขจัดข้อมูลที่ซ้ำกันโดยใช้ transaction-id ที่ไม่ซ้ำกันข้ามสำเนาที่แชร์ระหว่างเครือข่ายโฆษณาและผู้พัฒนา
  • การปรับมาตรฐาน BI ปลายทาง: การแมปค่าการแปลงแบบหยาบและแบบละเอียดเข้ากับโมเดลรายได้และเหตุการณ์ในฟวยด์ที่กำหนดโดยลูกค้า

ดูเพิ่มเติม: SKAdNetwork ──> โมเดลการระบุแหล่งที่มาบนมือถือ

การตรวจสอบด้วยการเข้ารหัส: การตรวจสอบลายเซ็น ECDSA P-256 ของ Apple

ทำความเข้าใจสแต็กการเข้ารหัส: NIST Curve P-256 (secp256r1) ร่วมกับ SHA-256

โพสต์แบ็ก SKAdNetwork ทุกรายการจะมีฟิลด์ attribution-signature ลายเซ็นเข้ารหัสนี้สร้างขึ้นโดย Apple โดยใช้อัลกอริทึมลายเซ็นดิจิทัลเส้นโค้งวงรี (ECDSA) กับเคิร์ฟ NIST P-256 (secp256r1) และใช้ไดเจสต์ SHA-256

ลายเซ็นนี้จะตรวจสอบคุณสมบัติด้านความปลอดภัยพื้นฐานสองประการ:

  • ความแท้จริง (Authenticity): โพสต์แบ็กถูกสร้างขึ้นโดยตรงจากระบบย่อยแพลตฟอร์มของ Apple บนอุปกรณ์ที่ได้รับการตรวจสอบแล้ว ไม่ได้ถูกปลอมแปลงโดยไคลเอนต์หรือพร็อกซีที่ไม่ประสงค์ดี
  • ความสมบูรณ์ของข้อมูล (Integrity): พารามิเตอร์ที่ครอบคลุมโดยลายเซ็นนั้นไม่ถูกแก้ไขระหว่างการส่งผ่านข้อมูล

การใช้กุญแจสาธารณะ SKAdNetwork ที่ Apple เผยแพร่

ในการตรวจสอบลายเซ็น เซิร์ฟเวอร์รับข้อมูลจะต้องโหลดกุญแจสาธารณะอย่างเป็นทางการของ Apple สำหรับ SKAdNetwork 2.1 และใหม่กว่า Apple จะเผยแพร่กุญแจสาธารณะ NIST P-256 โดยเฉพาะไว้ในเอกสารประกอบสำหรับนักพัฒนา:

  • การเริ่มต้นใช้งานกุญแจ: กุญแจสาธารณะจะถูกโหลดลงในหน่วยความจำในฐานะวัตถุกุญแจสาธารณะ X.509/DER มาตรฐานในระหว่างการเริ่มต้นเซิร์ฟเวอร์
  • การตรวจสอบลายเซ็นอสมมาตร: เอ็นจิ้นการตรวจสอบจะสร้างสตริงข้อความที่จัดเรียงรูปแบบ UTF-8 ขึ้นมาใหม่ คำนวณแฮช SHA-256 และตรวจสอบ attribution-signature ที่ถอดรหัส Base64 แล้วเทียบกับข้อความที่สร้างขึ้นใหม่

[Device / Subsystem] ──► [Dispatches Signed JSON Postback]
                                     │
                                     ▼
                  [DSP / Ad Network Ingestion Endpoint]
                  (HTTPS POST to registered postback URL)
                                     │
                                     ▼
                  [Parse JSON & Reconstruct Message String]
                  (Concatenate UTF-8 fields with \u2063)
                                     │
                                     ▼
                  [ECDSA P-256 Public Key Signature Verification]
                                     │
                      ┌──────────────┴──────────────┐
                      ▼                                                          ▼
               [Signature Valid]                                         [Signature Invalid]
                      │                                                          │
                      ▼                                                          ▼
            [Atomic Deduplication]                                       [Log Error & Discard]
            (Check transaction-id)
                      │
                      ▼
            [Process Attribution]
เวิร์กโฟลว์การตรวจสอบลายเซ็น SKAdNetwork ECDSA P 256

เหตุใดการใช้แฮชเพียงอย่างเดียวจึงไม่เพียงพอ: การตรวจสอบลายเซ็นแบบอสมมาตร

เนื่องจาก Apple ลงนามเพย์โหลดด้วยคีย์ส่วนตัว (private key) และไม่มีการแจกจ่ายคีย์ลับร่วมกัน จึงไม่สามารถใช้การตรวจสอบแบบสมมาตร (เช่น HMAC-SHA256) ได้ เอ็นจิ้นการรับข้อมูลจึงต้องใช้งานการตรวจสอบลายเซ็นด้วยกุญแจสาธารณะแบบอสมมาตรมาตรฐานผ่านไลบรารีการเข้ารหัสลับมาตรฐาน (เช่น OpenSSL, crypto ของ Node.js หรือ cryptography ของ Python)

การสร้างสตริงข้อความสำหรับการตรวจสอบลายเซ็น

โปรโตคอลการจัดเรียงรูปแบบที่เข้มงวด: บทบาทของตัวคั่นที่มองไม่เห็น (\u2063)

Apple กำหนดรูปแบบการจัดเรียงไบต์ UTF-8 ที่แน่นอนเพื่อสร้างสตริงข้อความสำหรับการตรวจสอบลายเซ็น พารามิเตอร์จะต้องถูกนำมาต่อกันตามลำดับที่แม่นยำ โดยคั่นด้วยอักขระ Unicode ที่มองไม่เห็น \u2063 (U+2063 Invisible Separator, ชุดไบต์ UTF-8 0xE2 0x81 0xA3):

Message=Param1    "2˘063"    Param2    "2˘063"  Paramn\text{Message} = \text{Param}_1 \;\|\; \text{"\u2063"} \;\|\; \text{Param}_2 \;\|\; \text{"\u2063"} \dots \|\; \text{Param}_n

การแทนที่ด้วยช่องว่าง เครื่องหมายวรรคตอนมาตรฐาน หรือตัวคั่น Unicode ทางเลือกอื่น จะทำให้การตรวจสอบความถูกต้องด้วยการเข้ารหัสล้มเหลว

ลำดับพารามิเตอร์เฉพาะเวอร์ชันสำหรับ SKAN 4.0

อ้างอิงจาก เอกสารประกอบสำหรับนักพัฒนาของ Apple เกี่ยวกับการตรวจสอบโพสต์แบ็กยืนยันการติดตั้ง พารามิเตอร์สำหรับโพสต์แบ็ก SKAdNetwork 4.0 จะต้องถูกจัดเรียงตามลำดับที่แน่นอนดังต่อไปนี้:

  1. version (เช่น "4.0")
  2. ad-network-id (เช่น "example123.skadnetwork")
  3. source-identifier (เช่น "4821")
  4. app-id (เช่น 1234567890)
  5. transaction-id (เช่น "6a8b1c2d-3e4f-5a6b-7c8d-9e0f1a2b3c4d")
  6. redownload (เช่น "true" หรือ "false" ในรูปแบบสตริงตัวพิมพ์เล็ก)
  7. source-app-id (สำหรับโฆษณาแบบแอปพลิเคชันสู่แอปพลิเคชัน) หรือ source-domain (สำหรับโฆษณาแบบเว็บสู่แอปพลิเคชันใน Safari) ซึ่งรวมไว้เฉพาะเมื่อมีอยู่ในโพสต์แบ็กเท่านั้น
  8. fidelity-type (เช่น 1 สำหรับโฆษณาที่เรนเดอร์โดย StoreKit หรือโฆษณาบนเว็บที่ระบุแหล่งที่มาโดย SKAdNetwork; 0 สำหรับโฆษณาแบบมองเห็นโดยไม่ต้องคลิก)
  9. did-win (เช่น "true" หรือ "false" ในรูปแบบสตริงตัวพิมพ์เล็ก)
  10. postback-sequence-index (เช่น 0, 1 หรือ 2)

ข้อกำหนดสำคัญของ SKAN 4: ค่าการแปลงถูกยกเว้นจากการลงนามลายเซ็น

ใน SKAdNetwork 4.0 ลายเซ็นของ Apple ไม่รวมถึง conversion-value หรือ coarse-conversion-value แม้ว่าฟิลด์ใดฟิลด์หนึ่งเหล่านี้จะปรากฏอยู่ในเพย์โหลด JSON ก็ตาม สตริงที่จัดเรียงสำหรับ SKAN 4 จะสิ้นสุดที่ postback-sequence-index การพยายามผนวกค่าการแปลงเข้ากับสตริงข้อความจะทำให้การตรวจสอบล้มเหลว

เพย์โหลด JSON ด้านล่างแสดงโครงสร้างโพสต์แบ็ก SKAdNetwork 4.0 ที่สมบูรณ์ ลายเซ็นด้านล่างนี้เป็นตัวอย่างจำลองและจะไม่ผ่านการตรวจสอบด้วยการเข้ารหัส สำหรับการทดสอบยูนิตเทสต์ ให้ใช้ตัวอย่างที่ลงนามแล้วของ Apple จากเอกสารประกอบการตรวจสอบอย่างเป็นทางการ:

{
  "version": "4.0",
  "ad-network-id": "example123.skadnetwork",
  "source-identifier": "4821",
  "app-id": 1234567890,
  "transaction-id": "6a8b1c2d-3e4f-5a6b-7c8d-9e0f1a2b3c4d",
  "redownload": false,
  "source-app-id": 9876543210,
  "fidelity-type": 1,
  "did-win": true,
  "postback-sequence-index": 0,
  "conversion-value": 47,
  "attribution-signature": "MEQCIFz8...SAMPLE_CRYPTOGRAPHIC_SIGNATURE...=="
}

การจัดเรียงข้อความลายเซ็น SKAN 4 ด้วย U 2063

การจัดการเพย์โหลด SKAN 4.0 แบบหลายหน้าต่างเวลาและจุดสิ้นสุดของผู้พัฒนา

การแยกวิเคราะห์ postback-sequence-index ข้ามหน้าต่างเวลาการแปลงแบบลำดับ

ใน SKAdNetwork 4.0 การแปลงจะสร้างโพสต์แบ็กจากหน้าต่างเวลาการแปลงที่ครอบคลุมนานสูงสุด 35 วันหลังจากการเปิดใช้งานแอปครั้งแรก โดยการส่งมอบจริงจะเกิดขึ้นหลังจากความล่าชหลังสิ้นสุดหน้าต่างเวลาที่สุ่มโดย Apple ระบบรับข้อมูลแบ็กเอนด์จะแยกวิเคราะห์ฟิลด์ postback-sequence-index เพื่อกำหนดข้อมูลการแปลงไปยังหน้าต่างวงจรชีวิตที่ถูกต้อง:

  • ดัชนี 0 (หน้าต่างที่ 1: วันที่ 0–2): ประกอบด้วยค่าการแปลงแบบละเอียด (0–63) หรือค่าการแปลงแบบหยาบ (low, medium, high) หรือฟิลด์อาจไม่มีอยู่
  • ดัชนี 1 (หน้าต่างที่ 2: วันที่ 3–7): สำหรับระดับข้อมูลโพสต์แบ็ก 1–3 อาจมีการเปิดเผย coarse-conversion-value (low, medium, high) เมื่อมีข้อมูล; ระดับ 0 ไม่มีสิทธิ์ได้รับโพสต์แบ็กครั้งที่สองหรือสาม
  • ดัชนี 2 (หน้าต่างที่ 3: วันที่ 8–35): สำหรับระดับข้อมูลโพสต์แบ็ก 1–3 อาจมีการเปิดเผย coarse-conversion-value (low, medium, high) เมื่อมีข้อมูล; ระดับ 0 ไม่มีสิทธิ์ได้รับโพสต์แบ็กครั้งที่สองหรือสาม

การจัดการค่าการแปลงแบบละเอียดเทียบกับแบบหยาบ

ตัวถอดรหัสการรับข้อมูลจะต้องคำนึงถึงความแปรปรวนของเพย์โหลด:

  • ความไม่เข้ากันร่วมกัน (Mutual Exclusivity): Apple กำหนดว่าโพสต์แบ็กยืนยันการติดตั้งอาจมี conversion-value หรือ coarse-conversion-value อย่างใดอย่างหนึ่ง แต่จะไม่มีทั้งสองค่าพร้อมกัน
  • การไม่มีค่าการแปลง: หากระดับข้อมูลโพสต์แบ็กที่กำหนดอยู่ในระดับต่ำ (ระดับ 0) ฟิลด์ค่าการแปลงจะถูกละเว้นจากเพย์โหลด JSON

การป้องกันการโจมตีแบบ Replay Attack และเพย์โหลดการแปลงที่ถูกปลอมแปลง

บทบาทของ transaction-id ในฐานะคีย์สำหรับการขจัดข้อมูลซ้ำ

โพสต์แบ็ก SKAdNetwork ทุกรายการจะมี UUID ของ transaction-id ที่ไม่ซ้ำกัน เอกสารของ Apple แนะนำให้ผู้รับใช้ตัวระบุนี้เป็นคีย์ความสมมูล (idempotency key) เพื่อตรวจจับและทิ้งโพสต์แบ็กการแปลงที่ซ้ำกัน

เนื่องจากตัวรับโพสต์แบ็กเป็นจุดสิ้นสุด HTTPS ที่เข้าถึงได้แบบสาธารณะ ผู้ไม่ประสงค์ดีจึงอาจพยายามโจมตีด้วยการเล่นซ้ำ (replay attack) โดยการดักจับโพสต์แบ็กที่ถูกต้องและส่งซ้ำหลายๆ ครั้งเพื่อเพิ่มตัวชี้วัดการแปลงอย่างไม่ถูกต้อง

การใช้งานแคชในหน่วยความจำแบบกระจายและบันทึกข้อมูลถาวร

Apple ไม่ได้กำหนดระยะเวลาการเก็บรักษาสำหรับการขจัดข้อมูลซ้ำที่เป็นสากล ผู้รับในระบบจริงควรเก็บบันทึกความสมมูลที่มีความคงทนสำหรับรหัสธุรกรรมที่ตรวจสอบแล้ว ตามข้อกำหนดในการรวบรวมบัญชีและการป้องกันการเล่นซ้ำของตน โดยอาจใช้ Redis TTL เป็นการเพิ่มประสิทธิภาพแคชแบบร้อน (hot-cache) แทนที่จะเป็นบันทึกข้อมูลซ้ำที่เชื่อถือได้เพียงแห่งเดียว:

  1. ตรวจสอบลายเซ็นด้วยการเข้ารหัสก่อน: ตรวจสอบลายเซ็น ECDSA กับกุญแจสาธารณะของ Apple ให้สมบูรณ์ก่อนที่จะบันทึกรหัสธุรกรรมลงในพื้นที่จัดเก็บ
  2. การขจัดข้อมูลซ้ำแบบอะตอมิก (Atomic Deduplication): ดำเนินการเขียนแบบอะตอมิก (เช่น คำสั่ง Redis SET key value NX EX <seconds>) ที่สนับสนุนด้วยข้อจำกัดความไม่ซ้ำกันของฐานข้อมูลความสัมพันธ์หรือฐานข้อมูลเอกสารแบบถาวร
  3. ขอบเขตการขจัดข้อมูลซ้ำ: กำหนดหน้าต่างเวลาการเก็บรักษาในการทำงานในชั้นแคชที่ครอบคลุมถึงการส่งมอบโพสต์แบ็กที่คาดไว้ การลองส่งซ้ำของเครือข่าย (Apple จะลองส่งซ้ำสำหรับการส่งมอบที่ล้มเหลวเป็นเวลาสูงสุด 9 วัน) และการกระทบยอดข้อมูลในขั้นตอนต่อไป

ท่อส่งข้อมูลการป้องกันการเล่นซ้ำและการขจัดข้อมูลซ้ำของ SKAdNetwork


การใช้งานแบ็กเอนด์ด้านล่างแสดงตัวอย่างการตรวจสอบความถูกต้องของลายเซ็น การตรวจสอบสคีมา และการขจัดข้อมูลซ้ำแบบอะตอมิกใน Python:

import base64
import json
import redis
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import load_der_public_key
from cryptography.exceptions import InvalidSignature

# Initialize Redis client for hot-cache transaction deduplication
redis_client = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)

# Official Apple SKAdNetwork 2.1+ Public Key (Base64 DER encoded, published by Apple)
APPLE_SKAN_PUBLIC_KEY_B64 = (
    "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWdp8GPcGqmhgzEFj9Z2nSpQVdday"
    "aPe4FMzqM9wib1+aHaaIzoHoLN9zW4K8y4SPykE3YVK3sVqW6Af0lfx3gg=="
)

# Exact Apple-specified invisible separator: U+2063 INVISIBLE SEPARATOR (UTF-8: 0xE2 0x81 0xA3)
SEPARATOR = "\u2063"

def construct_skan4_message_bytes(payload: dict) -> bytes:
    """
    Constructs the serialized UTF-8 message string for SKAN 4.0 signature verification.
    Apple specification explicitly EXCLUDES conversion-value and coarse-conversion-value from the signature.
    """
    parts = [
        str(payload["version"]),
        str(payload["ad-network-id"]),
        str(payload["source-identifier"]),
        str(payload["app-id"]),
        str(payload["transaction-id"]),
        "true" if payload["redownload"] is True else "false"
    ]

    # Include source-app-id (app ad) OR source-domain (web ad) if present
    if payload.get("source-app-id") is not None:
        parts.append(str(payload["source-app-id"]))
    elif payload.get("source-domain") is not None:
        parts.append(str(payload["source-domain"]))

    parts.append(str(payload["fidelity-type"]))
    parts.append("true" if payload["did-win"] is True else "false")
    parts.append(str(payload["postback-sequence-index"]))

    # Join with U+2063 separator and encode to UTF-8
    message_string = SEPARATOR.join(parts)
    return message_string.encode('utf-8')

def verify_and_ingest_skan4_postback(postback_json_str: str) -> dict:
    """
    Validates payload schema, verifies the ECDSA P-256 signature against Apple's public key,
    and performs atomic transaction-id deduplication.
    """
    try:
        payload = json.loads(postback_json_str)
    except Exception:
        return {"status": "REJECTED", "reason": "INVALID_JSON_FORMAT"}

    # Version Gate: Enforce SKAN 4.0 payload handling
    if payload.get("version") != "4.0":
        return {"status": "REJECTED", "reason": "UNSUPPORTED_SKAN_VERSION"}

    # Schema Validation: Required fields for SKAN 4.0
    required_fields = [
        "version", "ad-network-id", "source-identifier", "app-id",
        "transaction-id", "redownload", "fidelity-type", "did-win",
        "postback-sequence-index", "attribution-signature"
    ]
    for field in required_fields:
        if field not in payload:
            return {"status": "REJECTED", "reason": f"MISSING_REQUIRED_FIELD_{field.upper()}"}

    # Strict Type Validation
    if not isinstance(payload["redownload"], bool):
        return {"status": "REJECTED", "reason": "INVALID_TYPE_REDOWNLOAD"}
    if not isinstance(payload["did-win"], bool):
        return {"status": "REJECTED", "reason": "INVALID_TYPE_DID_WIN"}
    if payload["postback-sequence-index"] not in (0, 1, 2):
        return {"status": "REJECTED", "reason": "INVALID_SEQUENCE_INDEX"}
    if payload["fidelity-type"] not in (0, 1):
        return {"status": "REJECTED", "reason": "INVALID_FIDELITY_TYPE"}

    # Enforce mutual exclusivity between source-app-id and source-domain
    has_source_app = payload.get("source-app-id") is not None
    has_source_domain = payload.get("source-domain") is not None
    if has_source_app and has_source_domain:
        return {"status": "REJECTED", "reason": "CONFLICTING_SOURCE_FIELDS"}

    signature_b64 = payload["attribution-signature"]
    transaction_id = payload["transaction-id"]

    try:
        # Step 1: Reconstruct the exact UTF-8 serialized message
        message_bytes = construct_skan4_message_bytes(payload)
        signature_der = base64.b64decode(signature_b64, validate=True)

        # Step 2: Verify ECDSA P-256 / SHA-256 signature using Apple's published public key
        apple_public_key = load_der_public_key(base64.b64decode(APPLE_SKAN_PUBLIC_KEY_B64))
        apple_public_key.verify(
            signature_der,
            message_bytes,
            ec.ECDSA(hashes.SHA256())
        )
    except InvalidSignature:
        return {"status": "REJECTED", "reason": "INVALID_CRYPTOGRAPHIC_SIGNATURE"}
    except Exception as e:
        return {"status": "ERROR", "reason": f"VERIFICATION_FAILED: {str(e)}"}

    # Step 3: Atomic Deduplication via Redis (Hot cache layer)
    # Note: In production, pair this hot cache with a persistent unique database constraint.
    # 14-day TTL (1,209,600 seconds) serves as an illustrative receiver cache policy covering retries.
    is_new = redis_client.set(f"skan_tx:{transaction_id}", "1", nx=True, ex=1209600)
    if not is_new:
        return {"status": "DUPLICATE", "reason": "TRANSACTION_ALREADY_PROCESSED"}

    return {
        "status": "VERIFIED",
        "transaction_id": transaction_id,
        "sequence_index": payload["postback-sequence-index"],
        "did_win": payload["did-win"]
    }

การนำโพสต์แบ็กเข้าสู่โมเดลการประมูลแบบเรียลไทม์และตัวเพิ่มประสิทธิภาพ CPA

การแยกส่วนการรับข้อมูลที่ขอบเครือข่าย (Edge Ingestion) ออกจากการประมวลผลแบบอะซิงโครนัส

DSP ที่มีปริมาณงานสูงจะประมวลผลโพสต์แบ็กจำนวนมากในช่วงที่มีแคมเปญหนาแน่น การประมวลผลแบบซิงโครนัสด้านปลายทางอาจทำให้เกิดปัญหาคอข่วนด้านความหน่วงเวลา

สถาปัตยกรรมระดับองค์กรจึงใช้ไปป์ไลน์แบบอะซิงโครนัส:

  1. ตัวรับที่ขอบเครือข่าย (Edge Receiver): ยอมรับคำขอ HTTP POST ที่เข้ามา ตรวจสอบความแท้จริงของลายเซ็น ดำเนินการขจัดข้อมูลซ้ำแบบอะตอมิกบน transaction-id และส่งคืน HTTP 200 OK ทันที
  2. คิวเหตุการณ์ (Event Queue): เผยแพร่เพย์โหลดที่ตรวจสอบแล้วไปยังโบรกเกอร์เหตุการณ์แบบกระจาย (เช่น Apache Kafka หรือ AWS SQS)
  3. เวิร์กเกอร์การประมูลและวิเคราะห์: ประมวลผลสตรีมเหตุการณ์ แมปค่าการแปลงเข้ากับตัวชี้วัดรายได้ และอัปเดตโมเดลเป้าหมาย CPA สำหรับการประมูลแบบเรียลไทม์ (RTB)

การใช้ตัวระบุแหล่งที่มาแบบลำดับชั้น (Hierarchical Source Identifier)

โพสต์แบ็กที่ชนะโพสต์แรกอาจเปิดเผยตัวเลขสอง สาม หรือสี่หลักของ source-identifier แบบลำดับชั้น ขึ้นอยู่กับระดับข้อมูลโพสต์แบ็ก ความหมายเชิงความหมายของตัวเลขเหล่านั้นถูกกำหนดโดยอนุกรมวิธาน source-identifier ของเครือข่ายโฆษณาเอง ระบบการประมูลควรแก้ไข source-identifier ที่ได้รับเทียบกับข้อมูลเมตาแคมเปญของเครือข่าย แทนที่จะสมมติว่ามีการแมปแบบสากลระหว่างความยาวของตัวเลขกับตำแหน่งเฉพาะหรือความละเอียดของชิ้นงานโฆษณา

ตารางเปรียบเทียบ: การส่งตรงจาก Apple เทียบกับการรับข้อมูลผ่าน MMP S2S

มิติการทำงาน การส่งตรงจาก Apple (เครือข่ายโฆษณา) จุดสิ้นสุดของผู้พัฒนา (NSAdvertising...) ไปป์ไลน์การรับข้อมูล MMP S2S
ผู้รับ เครือข่ายโฆษณาที่ลงทะเบียน ผู้พัฒนาแอปที่ลงโฆษณา พาร์ทเนอร์การวัดผลมือถือ (MMP)
ขอบเขตการระบุแหล่งที่มา โพสต์แบ็กที่ชนะสำหรับเครือข่ายนั้นๆ สำเนาโพสต์แบ็กที่ชนะสำหรับแอปพลิเคชัน มุมมองรวมจากหลายเครือข่าย
การตรวจสอบลายเซ็น ดำเนินการโดยแบ็กเอนด์ของเครือข่ายโฆษณา ดำเนินการโดยแบ็กเอนด์ของผู้พัฒนา ขึ้นอยู่กับการใช้งาน (โฟลว์ของพาร์ทเนอร์)
โพสต์แบ็กที่ไม่ชนะ ได้รับหากผ่านเกณฑ์ (did-win: false) ไม่ส่งไปยังจุดสิ้นสุดของผู้พัฒนา อาจมีให้ใช้งานผ่านโฟลว์ของพาร์ทเนอร์
กรณีการใช้งานหลัก การเพิ่มประสิทธิภาพการประมูลโดยตรงและเป้าหมาย CPA การตรวจสอบและการตรวจสอบบัญชีคลังข้อมูลภายใน แดชบอร์ดประสิทธิภาพข้ามช่องทาง

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

กุญแจสาธารณะใดที่ใช้ในการตรวจสอบลายเซ็นโพสต์แบ็กของ Apple?
Apple เผยแพร่กุญแจสาธารณะ NIST P-256 อย่างเป็นทางการที่ใช้สำหรับการยืนยันการติดตั้ง SKAdNetwork 2.1+ ในเอกสารประกอบสำหรับนักพัฒนา เซิร์ฟเวอร์รับข้อมูลจะโหลดกุญแจสาธารณะนี้ในรูปแบบ X.509/DER เพื่อตรวจสอบลายเซ็นที่เข้ามา
เหตุใดโพสต์แบ็ก SKAdNetwork ที่ถูกต้องจึงไม่ผ่านการตรวจสอบลายเซ็น?
ความล้มเหลวในการตรวจสอบลายเซ็นมักเกิดขึ้นเนื่องจากข้อผิดพลาดในการจัดเรียงรูปแบบ: การใช้อักขระตัวคั่นผิด (ใช้ `\u2060` แทนที่จะเป็น `\u2063`) การเรียงลำดับพารามิเตอร์ไม่ถูกต้อง การผนวกค่าการแปลงเข้ากับสตริงข้อความ SKAN 4 อย่างไม่ถูกต้อง หรือการจัดการการเข้ารหัสสตริงบูกาลไม่ถูกต้อง (`"true"` เทียบกับ `"false"`)
จุดสิ้นสุดของผู้พัฒนาแอปที่ลงโฆษณาสามารถรับโพสต์แบ็ก SKAdNetwork ที่ไม่ชนะได้หรือไม่?
ไม่ได้ จุดสิ้นสุดสำเนาของผู้พัฒนาจะได้รับสำเนาของโพสต์แบ็กยืนยันการติดตั้งที่ชนะเมื่อมีการกำหนดค่าไว้ โพสต์แบ็กที่ไม่ชนะสูงสุดถึงห้ารายการ (`did-win: false`) จะถูกส่งไปยังเครือข่ายโฆษณาอื่นที่มีคุณสมบัติผ่านเกณฑ์โดยตรง ไม่ได้ส่งไปยังจุดสิ้นสุดสำเนาของผู้พัฒนา

บทสรุปและกรอบการตัดสินใจ

การจัดการโพสต์แบ็ก SKAdNetwork ในสเกลขนาดใหญ่จำเป็นต้องมีการผสมผสานการรับข้อมูลที่ขอบเครือข่ายแบบหน่วงเวลาต่ำ เข้ากับการตรวจสอบความถูกต้องด้วยการเข้ารหัสอย่างเข้มงวดและการขจัดข้อมูลซ้ำในระดับธุรกรรม เนื่องจากโพสต์แบ็กของ Apple มีอิทธิพลโดยตรงต่อการจัดสรรงบประมาณและอัลกอริทึมการประมูล การตรวจสอบลายเซ็น ECDSA และการบังคับใช้ความสมมูลของ transaction-id จึงช่วยปกป้องไปป์ไลน์การรับข้อมูลจากโพสต์แบ็กที่ถูกปลอมแปลงหรือถูกดัดแปลง รวมถึงการประมวลผลการเล่นซ้ำที่ซ้ำซ้อน

เพื่อเสริมการรายงาน SKAdNetwork ที่สื่อกลางโดยแพลตฟอร์ม ด้วยกระบวนการเริ่มต้นใช้งานผู้ใช้ระดับไมโครและการกำหนดเส้นทางลิงก์เชิงลึกทันที ทีมวิศวกรรมจึงปรับใช้สถาปัตยกรรมการกำหนดเส้นทางแบบเฟิร์สพาร์ที้ควบคู่ไปกับ API ของแพลตฟอร์ม

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

วัสดุที่เกี่ยวข้อง

  • แนวคิด: โพสต์แบ็ก S2S, การตรวจสอบด้วยการเข้ารหัส, ECDSA P-256, การป้องกันการโจมตีแบบ Replay Attack, การขจัดข้อมูลธุรกรรมซ้ำ

  • เทคโนโลยี: Apple SKAdNetwork, Apple AdAttributionKit, Redis In-Memory Cache, OpoInstall Mobile SDK

  • มาตรฐาน: IETF RFC 8259 (การแลกเปลี่ยนข้อมูล JSON), RFC 5480 (การเข้ารหัสลับเส้นโค้งวงรี)

  • API: StoreKit SKAdNetwork API, ข้อมูลจำเพาะการส่งโพสต์แบ็ก Apple S2S, OpoInstall S2S API

เอกสารประกอบอย่างเป็นทางการ

Share this article

Keep Discovering

OpenAI เพิ่มฟีเจอร์ควบคุม Apple Messages ให้กับ ChatGPT บน Mac

OpenAI เพิ่มฟีเจอร์ควบคุม Apple Messages ให้กับ ChatGPT บน Mac

OpenAI เพิ่มการรองรับ Apple Messages บน ChatGPT สำหรับ Mac เจาะลึกว่าสิทธิ์การใช้งานใหม่ การอนุมัติการส่งข้อความเริ่มต้น และระบบอัตโนมัติของ macOS ส่งผลต่อความปลอดภัยของระบบเอเจนต์อย่างไร

Alibaba Qwen UI Agent ควบคุมโทรศัพท์จริงได้ครอบคลุมกว่า 150 แอปพลิเคชัน

Alibaba Qwen UI Agent ควบคุมโทรศัพท์จริงได้ครอบคลุมกว่า 150 แอปพลิเคชัน

Alibaba Qwen UI Agent ควบคุมโทรศัพท์จริงได้ครอบคลุมกว่า 150 แอปพลิเคชัน อ่านบทวิเคราะห์ทางเทคนิคของโมเดลรากฐาน GUI นี้และผลการทดสอบมาตรฐาน

วิธีใช้งาน PrivacyInfo.xcprivacy สำหรับแอปพลิเคชันและ SDK บน iOS

วิธีใช้งาน PrivacyInfo.xcprivacy สำหรับแอปพลิเคชันและ SDK บน iOS

เรียนรู้วิธีการใช้งาน PrivacyInfo.xcprivacy สำหรับแอปและ SDK บน iOS: การประกาศ API ที่ต้องระบุเหตุผล (Required Reason APIs), การสร้างรายงานความเป็นส่วนตัว (Privacy Reports) และการแก้ไขข้อผิดพลาดการลงนาม CocoaPods