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

شبكات الإعلانات التي تستخدم إنّ علامات 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++ أدناه إلى بروتوكول OpenSSL BIO_f_base64() الخاصة بالمشروع، وهي جزء من نموذج 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<char*>(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. لمزيد من المعلومات، يُرجى مراجعة التشفير: