فك تشفير معرّفات المعلنين لشبكات المواقع الإعلانية

تكون شبكات الإعلانات التي تستخدم علامات JavaScript لتعبئة الإعلانات من خلال "الشراة المعتمَدون" مؤهَّلة للحصول على معرّفات المعلِنين لكلّ من أجهزة Android وiOS. ويتم إرسال المعلومات من خلال وحدة الماكرو %%EXTRA_TAG_DATA%% أو %%ADVERTISING_IDENTIFIER%% في علامة JavaScript التي يديرها الشراة المعتمَدون. يركّز الجزء المتبقي من هذا القسم على استخراج "%%EXTRA_TAG_DATA%%"، ولكن اطّلِع على تجديد النشاط التسويقي باستخدام معرّف المعلِنين (IDFA) أو المعرّف الإعلاني للحصول على تفاصيل عن %%ADVERTISING_IDENTIFIER%%المخزن المؤقت المشفّر للنموذج الأوّلي MobileAdvertisingId الذي يمكن فك تشفيره بشكل مماثل.

المخطط الزمني

  1. تحدِّث شبكة المواقع الإعلانية علامات JavaScript داخل التطبيق من خلال واجهة مستخدم "الشراة المعتمَدون"، مع إضافتها في وحدة ماكرو %%EXTRA_TAG_DATA%% كما هو موضّح أدناه.
  2. في وقت العرض، يطلب التطبيق إعلانًا من "الشراة المعتمَدين" من خلال حزمة SDK الخاصة بإعلانات Google على الأجهزة الجوّالة، مع تمرير معرّف المعلِن بأمان.
  3. يسترد التطبيق علامة JavaScript، مع ملء وحدة الماكرو %%EXTRA_TAG_DATA%% بذاكرة التخزين المؤقت لبروتوكول شبكة الإعلانات المشفر التي تحتوي على هذا المعرّف.
  4. ويشغّل التطبيق هذه العلامة، فيتصل إلى شبكة المواقع الإعلانية للحصول على الإعلان الفائز.
  5. لاستخدام هذه المعلومات (تحقيق الربح)، يجب أن تعالج شبكة المواقع الإعلانية المورد الاحتياطي للبروتوكولات:
    1. فك ترميز سلسلة websafe مرة أخرى إلى سلسلة بايت باستخدام WebSafeBase64.
    2. وفك تشفيرها باستخدام النظام الموضح أدناه.
    3. عليك إكمال النموذج الأوّلي والحصول على رقم تعريف المعلِن من ExtraTagData.advertising_id أو ExtraTagData.hashed_idfa.

التبعيات

  1. برنامج ترميز WebSafeBase64
  2. مكتبة عملات مشفّرة تتوافق مع خوارزمية SHA-1 HMAC، مثل Openssl
  3. هو برنامج التحويل البرمجي للبروتوكول المؤقت من Google.

فك ترميز سلسلة websafe

ولأنّ المعلومات التي يتم إرسالها من خلال وحدة الماكرو %%EXTRA_TAG_DATA%% يجب إرسالها من خلال عنوان URL، تحوّلها خوادم Google إلى ترميز base64 آمن على الويب (RFC 3548).

لذلك، قبل محاولة فك التشفير، يجب عليك فك ترميز أحرف ASCII مرة أخرى في سلسلة بايت. يستند نموذج رمز C++ أدناه إلى BIO_f_base64() من مشروع OpenSSL، وهو جزء من نموذج رمز فك التشفير من Google.

string AddPadding(const string& b64_string) {
  if (b64_string.size() % 4 == 3) {
    return b64_string + "=";
  } else if (b64_string.size() % 4 == 2) {
    return b64_string + "==";
  }
  return b64_string;
}

// Adapted from http://www.openssl.org/docs/man1.1.0/crypto/BIO_f_base64.html
// Takes a web safe base64 encoded string (RFC 3548) and decodes it.
// Normally, web safe base64 strings have padding '=' replaced with '.',
// but we will not pad the ciphertext. We add padding here because
// openssl has trouble with unpadded strings.
string B64Decode(const string& encoded) {
  string padded = AddPadding(encoded);
  // convert from web safe -> normal base64.
  int32 index = -1;
  while ((index = padded.find_first_of('-', index + 1)) != string::npos) {
    padded[index] = '+';
  }
  index = -1;
  while ((index = padded.find_first_of('_', index + 1)) != string::npos) {
    padded[index] = '/';
  }

  // base64 decode using openssl library.
  const int32 kOutputBufferSize = 256;
  char output[kOutputBufferSize];

  BIO* b64 = BIO_new(BIO_f_base64());
  BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
  BIO* bio = BIO_new_mem_buf(const_cast(padded.data()),
                             padded.length());
  bio = BIO_push(b64, bio);
  int32 out_length = BIO_read(bio, output, kOutputBufferSize);
  BIO_free_all(bio);
  return string(output, out_length);
}

بنية سلسلة بايت مشفّرة

بعد فك ترميز أحرف ASCII مرة أخرى في سلسلة بايت، تكون جاهزًا لفك تشفيرها. تتضمّن سلسلة البايت المشفّرة 3 أقسام:

  • initialization_vector: 16 بايت.
  • ciphertext: سلسلة من أقسام 20 بايت.
  • integrity_signature: 4 بايت.
{initialization_vector (16 bytes)}{ciphertext (20-byte sections)}{integrity_signature (4 bytes)}

تنقسم مصفوفة ciphertext بايت إلى عدة أقسام مكونة من 20 بايت، باستثناء القسم الأخير منها والذي قد يتراوح بين 1 و20 بايت. لكل قسم من عنصر byte_array الأصلي، يتم إنشاء ciphertext المقابل للـ 20 بايت على النحو التالي:

<byte_array <xor> HMAC(encryption_key, initialization_vector || counter_bytes)>

حيث يتم دمج ||.

التعريفات

متغير التفاصيل
initialization_vector 16 بايت - فريد فقط لمرات الظهور.
encryption_key 32 بايت - يتم توفيرها عند إعداد الحساب.
integrity_key 32 بايت - يتم توفيرها عند إعداد الحساب.
byte_array كائن ExtraTagData متسلسل، في أقسام 20 بايت.
counter_bytes قيمة بايت تعرض العدد الترتيبي للقسم، راجِع المعلومات أدناه.
final_message إجمالي مصفوفة البايت التي تم إرسالها من خلال وحدة الماكرو %%EXTRA_TAG_DATA%% (بدون ترميز WebSafeBase64)
عوامل التشغيل التفاصيل
hmac(key, data) خوارزمية SHA-1 HMAC، باستخدام key لتشفير data.
a || b السلسلة a مرتبطة بالسلسلة b.

حساب عدّاد_بايت

يحدّد counter_bytes ترتيب كل قسم 20 بايت من ciphertext. يُرجى العِلم أنّ القسم الأخير قد يحتوي على عدد يتراوح بين 1 و20 بايت. لملء counter_bytes بالقيمة الصحيحة عند تشغيل دالة hmac()، احسب الأقسام التي يبلغ حجمها 20 بايت (بما في ذلك الباقي) واستخدِم الجدول المرجعي التالي:

رقم القسم قيمة counter_bytes
0 لا ينطبق
1 ... 256 1 بايت. تزيد القيمة من 0 إلى 255 بشكل تسلسلي.
257 ... 512 2 بايت. قيمة البايت الأول هي 0، ويزداد قيمة البايت الثاني من 0 إلى 255 على التوالي.
513 ... 768 3 بايت. قيمة أول بايت من البايت هي 0، وتتزايد قيمة آخر بايت من 0 إلى 255 على التوالي.

الرجوع إلى الأعلى

مخطط التشفير

يستند مخطط التشفير إلى النظام نفسه المستخدَم في فك تشفير إشارة الاستهداف المحلي الفائق.

  1. التسلسل: يتم أولاً ترتيب مثيل الكائن ExtraTagData، كما هو محدّد في المخزن المؤقت للبروتوكولات، من خلال SerializeAsString() إلى صفيف بايت.

  2. التشفير: يتم بعد ذلك تشفير مصفوفة البايت باستخدام نظام تشفير مخصص مُصمم لتقليل حجم النفقات العامة مع ضمان توفير مستوى أمان كافٍ. يستخدم نظام التشفير خوارزمية HMAC مع مفتاح لإنشاء لوحة سرية تستند إلى initialization_vector، وهي فريدة وخاصة بحدث الظهور.

الرمز الزائف للتشفير

byte_array = SerializeAsString(ExtraTagData object)
pad = hmac(encryption_key, initialization_vector ||
      counter_bytes )  // for each 20-byte section of byte_array
ciphertext = pad <xor> byte_array // for each 20-byte section of byte_array
integrity_signature = hmac(integrity_key, byte_array ||
                      initialization_vector)  // first 4 bytes
final_message = initialization_vector || ciphertext || integrity_signature

مخطط فك التشفير

يجب على رمز فك التشفير 1) فك تشفير المخزن المؤقت للبروتوكول باستخدام مفتاح التشفير، و2) التحقق من بتات التكامل باستخدام مفتاح التكامل. سيتم توفير المفاتيح لك أثناء إعداد الحساب. لا توجد أي قيود على كيفية هيكلة عملية التنفيذ. بالنسبة للجزء الأكبر، يجب أن تكون قادرًا على أخذ عينة التعليمات البرمجية وتكييفها وفقًا لاحتياجاتك.

  1. إنشاء اللوحة: HMAC(encryption_key, initialization_vector || counter_bytes)
  2. XOR: خذ هذه النتيجة و<xor> مع النص المشفر لعكس التشفير.
  3. التحقّق: يمرّر توقيع السلامة 4 بايت من HMAC(integrity_key, byte_array || initialization_vector)

الرمز الزائف لفك التشفير

// split up according to length rules
(initialization_vector, ciphertext, integrity_signature) = final_message

// for each 20-byte section of ciphertext
pad = hmac(encryption_key, initialization_vector || counter_bytes)

// for each 20-byte section of ciphertext
byte_array = ciphertext <xor> pad

confirmation_signature = hmac(integrity_key, byte_array ||
                         initialization_vector)
success = (confirmation_signature == integrity_signature)

نموذج رمز C++

تم تضمين إحدى الدوال الرئيسية من مثال الرمز البرمجي لفك التشفير الكامل.

bool DecryptByteArray(
    const string& ciphertext, const string& encryption_key,
    const string& integrity_key, string* cleartext) {
  // Step 1. find the length of initialization vector and clear text.
  const int cleartext_length =
     ciphertext.size() - kInitializationVectorSize - kSignatureSize;
  if (cleartext_length < 0) {
    // The length cannot be correct.
    return false;
  }

  string iv(ciphertext, 0, kInitializationVectorSize);

  // Step 2. recover clear text
  cleartext->resize(cleartext_length, '\0');
  const char* ciphertext_begin = string_as_array(ciphertext) + iv.size();
  const char* const ciphertext_end = ciphertext_begin + cleartext->size();
  string::iterator cleartext_begin = cleartext->begin();

  bool add_iv_counter_byte = true;
  while (ciphertext_begin < ciphertext_end) {
    uint32 pad_size = kHashOutputSize;
    uchar encryption_pad[kHashOutputSize];

    if (!HMAC(EVP_sha1(), string_as_array(encryption_key),
              encryption_key.length(), (uchar*)string_as_array(iv),
              iv.size(), encryption_pad, &pad_size)) {
      printf("Error: encryption HMAC failed.\n");
      return false;
    }

    for (int i = 0;
         i < kBlockSize && ciphertext_begin < ciphertext_end;
         ++i, ++cleartext_begin, ++ciphertext_begin) {
      *cleartext_begin = *ciphertext_begin ^ encryption_pad[i];
    }

    if (!add_iv_counter_byte) {
      char& last_byte = *iv.rbegin();
      ++last_byte;
      if (last_byte == '\0') {
        add_iv_counter_byte = true;
      }
    }

    if (add_iv_counter_byte) {
      add_iv_counter_byte = false;
      iv.push_back('\0');
    }
  }

الحصول على البيانات من المخزن المؤقت لبروتوكول شبكة الإعلانات

بعد فك ترميز البيانات التي تم تمريرها في %%EXTRA_TAG_DATA%% وفك تشفيرها، ستكون جاهزًا لإلغاء تسلسل البروتوكول المؤقت والحصول على معرّف المعلن للاستهداف.

إذا لم تكن على دراية بالموارد الاحتياطية للبروتوكولات، ابدأ بمستنداتنا.

التعريف

ويُعرف المخزن المؤقت لبروتوكول شبكة الإعلانات على النحو التالي:

message ExtraTagData {
  // advertising_id can be Apple's identifier for advertising (IDFA)
  // or Android's advertising identifier. When the advertising_id is an IDFA,
  // it is the plaintext returned by iOS's [ASIdentifierManager
  // advertisingIdentifier]. For hashed_idfa, the plaintext is the MD5 hash of
  // the IDFA.  Only one of the two fields will be available, depending on the
  // version of the SDK making the request.  Later SDKs provide unhashed values.
  optional bytes advertising_id = 1;
  optional bytes hashed_idfa = 2;
}

ستحتاج إلى إلغاء تسلسله باستخدام ParseFromString() كما هو موضّح في وثائق المخزن المؤقت لبروتوكول C++.

للحصول على تفاصيل عن حقلَي Android advertising_id وiOS hashed_idfa، اطّلِع على فك تشفير المعرِّف الإعلاني واستهداف مستودع التطبيقات المتوافقة مع الأجهزة الجوّالة باستخدام معرّف المعلِنين (IDFA).

مكتبة Java

بدلاً من تنفيذ خوارزميات التشفير لترميز وفك ترميز معرّفات المعلنين لشبكات الإعلانات، يمكنك استخدام DoubleClickCrypto.java. لمزيد من المعلومات، راجع التشفير.