المصادقة والأمان

تشويش الكود وإزالة الرموز

15 دقيقة الدرس 10 من 12

تشويش الكود وإزالة الرموز

عند نشر تطبيق Flutter في وضع الإصدار، لا يزال الكود المُجمَّع بلغة Dart قابلاً للقراءة جزئياً بواسطة أدوات الهندسة العكسية. يمكن للمهاجم فك تجميع ملف APK أو IPA واستخراج أسماء الفئات والطرق والنصوص الحرفية. التشويش (Obfuscation) في Dart يُعيد تسمية هذه الرموز بمعرّفات عشوائية لا معنى لها، مما يجعل الملف الثنائي أصعب بكثير في التحليل. مقروناً بـإزالة الرموز (Symbol Stripping)، يصبح إصدار الإنتاج أخف وزناً وأكثر مقاومةً للهندسة العكسية.

ملاحظة: التشويش هو عائق رادع وليس حاجزاً منيعاً. المهاجم المصمّم بوقت كافٍ لا يزال قادراً على عكس المنطق. اقرن التشويش دائماً بضوابط أمنية أخرى (تثبيت الشهادة، فحوصات سلامة وقت التشغيل، التحقق من جانب الخادم).

ما الذي يفعله التشويش فعلاً

يُجري مترجم Dart التشويش على مستوى اللقطة (Snapshot) أثناء التجميع AOT (Ahead-of-Time). يستبدل المعرّفات البشرية القابلة للقراءة بأسماء قصيرة مبهمة:

  • أسماء الفئاتLoginRepository تصبح شيئاً مثل a1
  • أسماء الطرقfetchUserToken() تصبح b3
  • أسماء الحقول_secretKey تصبح c7
  • أسماء المكتبات — يتم استبدال المسارات المؤهلة بالكامل

النصوص الحرفية المضمّنة (مثل مفاتيح API) لا يُشوَّش عليها بهذه الراية وحدها؛ فهي تبقى مقروءة في الملف الثنائي. بالنسبة للنصوص الحساسة، استخدم التخزين الآمن أو جلبها من الخادم الخلفي بدلاً من تضمينها مباشرةً.

تفعيل التشويش في إصدارات الإنتاج

أضف رايتين إلى أمر flutter build. الراية --split-debug-info إلزامية عند استخدام --obfuscate — فهي تكتب خريطة الرموز إلى مجلد محلي لتتمكن من فك تشويش تتبعات الأعطال لاحقاً.

بناء APK مُشوَّش

# Android APK
flutter build apk --release \
  --obfuscate \
  --split-debug-info=build/debug-info/android

# Android App Bundle (المفضل لمتجر Play)
flutter build appbundle --release \
  --obfuscate \
  --split-debug-info=build/debug-info/android-aab

# iOS
flutter build ipa --release \
  --obfuscate \
  --split-debug-info=build/debug-info/ios

سيتم تعبئة المجلد الذي تمرره إلى --split-debug-info بملفات .symbols (واحد لكل ABI على Android، وواحد لـ iOS). احتفظ بهذه الملفات — بدونها، تتبعات الأعطال من الإصدار المُشوَّش غير قابلة للقراءة.

تحذير: لا تحتفظ أبداً بمجلد build/debug-info في مستودع عام. تُعيد ملفات الرموز هذه جزئياً عكس التشويش ويجب تخزينها في سجل قطع أثر خاص (مثل دلو S3 خاص، أو Firebase App Distribution، أو مخزن أسرار CI الخاص بك).

فك تشويش تتبعات أعطال الأعطال

عندما يُرسل مستخدم تقرير عطل من إصدار مُشوَّش، يُظهر تتبع المكدس أسماء رموز مشوهة. تُعيد أداة Dart المسماة flutter symbolize الأسماء الأصلية باستخدام ملف .symbols المحفوظ.

استعادة تتبع المكدس المقروء

# احفظ تتبع العطل المُشوَّش في ملف، ثم نفّذ:
flutter symbolize \
  --debug-info=build/debug-info/android/app.android-arm64.symbols \
  --input=crash_trace.txt

# المخرج: تتم استعادة أسماء الفئات والطرق الأصلية
# مثال على سطر مُشوَّش:
#   #01 a1.b3 (package:myapp/c7.dart:1)
# بعد symbolize:
#   #01 LoginRepository.fetchUserToken (package:myapp/src/auth/login_repository.dart:42)

ما الذي لا يحميه التشويش

فهم حدود التشويش يمنع الثقة الزائفة:

  • النصوص المضمّنة — مفاتيح API والعناوين الأساسية والأسرار المضمّنة في الكود المصدري تبقى نصاً عادياً في الملف الثنائي
  • حركة الشبكة — التشويش لا يُشفّر أو يُخفي طلبات HTTP؛ استخدم TLS وتثبيت الشهادة
  • منطق التطبيق في وقت التشغيل — لا يزال بالإمكان ربط مُصحِّح الأخطاء وفحص الذاكرة
  • المكونات الداخلية لمحرك Flutter — يُشوَّش فقط كود Dart الخاص بك؛ محرك Flutter (مكتبة .so) لا يُشوَّش
  • ملفات الأصول — الصور والخطوط وملفات JSON في assets/ تُحزَم كما هي
نصيحة: انقل التهيئة الحساسة (مفاتيح API، أسرار OAuth) خارج الملف الثنائي للتطبيق كلياً. اجلبها من خادمك الخلفي بعد مصادقة المستخدم، أو استخدم التخزين الآمن للمنصة (flutter_secure_storage) مُهيَّأً عند الإطلاق الأول.

دمج التشويش في خط CI/CD

في مشروع حقيقي، يجب أن تكون رايات التشويش جزءاً من خط بناء آلي، وليست تُشغَّل بشكل مخصص. فيما يلي نهج نموذجي لسير عمل GitHub Actions أو ما شابه:

مقتطف نص CI (Shell)

# حفظ قطع أثر debug-info للرجوع إليها لاحقاً
DEBUG_INFO_DIR="artifacts/debug-info/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$DEBUG_INFO_DIR"

flutter build appbundle --release \
  --obfuscate \
  --split-debug-info="$DEBUG_INFO_DIR"

# رفع debug-info إلى تخزين خاص (مثال: AWS S3)
# aws s3 cp "$DEBUG_INFO_DIR" s3://my-private-bucket/debug-info/ --recursive

# ملف .aab يذهب إلى متجر Play؛ debug-info يبقى خاصاً
echo "تم حفظ ملفات الرموز في: $DEBUG_INFO_DIR"

التحقق من تفعيل التشويش

بعد البناء، يمكنك تأكيد نجاح التشويش بفحص الملف الثنائي باستخدام أداة تفكيك التجميع، أو تشغيل strings على المكتبة المشتركة. في الإصدار المُشوَّش يجب أن ترى أجزاء معرّفات قصيرة بدلاً من أسماء الفئات مثل LoginRepository.

ملخص: فعّل التشويش في كل إصدار إنتاج باستخدام --obfuscate --split-debug-info=<dir>. احفظ ملفات الرموز في تخزين خاص ومُصنَّف مرتبط برقم الإصدار. استخدم flutter symbolize لفك تشفير تقارير الأعطال. تذكر أن التشويش يُصلّب الملف الثنائي لكنه لا يُعوّض ممارسات الترميز الآمن — أبقِ الأسرار خارج الجهاز وحمِ حركة الشبكة بـ TLS وتثبيت الشهادة.