الجذر ليس ما يمكّن تطبيق VPN على أندرويد من حمل حركة البيانات أو منع التسريبات أو البقاء بعد إعادة التشغيل. يلخّص الناس كل ذلك بعبارة «التطبيق يملك إذن VPN»، وهذا يخفي ثلاث آليات منفصلة: منح موافقة من المستخدم، وواجهة حزم ينشئها النظام، وسياسة حجب في المنصة ليست من شأن التطبيق تعريفها. مراجعة تصميم VPN على أندرويد تعني السؤال عن أي من الثلاثة يرتكز عليه ادعاء معيّن، لأن لكل منها مالكًا ونمط فشل مختلفين.

الموافقة تصل عبر نشاط في النظام

VpnService.prepare() لا يمنح شيئًا. إنه يعيد intent إلى نشاط الموافقة في النظام عند غياب الموافقة، ويطلق التطبيق ذلك الحوار كي يوافق المستخدم على الخدمة. تشغيل عملية التطبيق نفسه، أو إضافة إذن إلى البيان، أو عرض شاشة «تمكين VPN» مخصّصة، لا ينشئ المنح.

المنح محدود النطاق أيضًا. إنه يخص حزمة داخل ملف تعريف مستخدم، لذا النسخة الشخصية ونسخة العمل قراران منفصلان، ويمكن لمالك الجهاز تهيئة ملف تعريف مُدار بشكل مختلف عن ملف شخصي. إذا لم يظهر الحوار أبدًا وأعاد prepare() القيمة null، فالموافقة موجودة أصلًا لتلك الحزمة في ذلك الملف؛ وهذا ليس دليلًا على أن التطبيق موثوق، بل فقط على أن المنح قائم.

الواجهة طلب ينفّذه النظام

بعد الموافقة، يصف التطبيق ما يريده باستخدام VpnService.Builder: العناوين والمسارات وخوادم DNS و MTU، وحيثما كان مدعومًا، التطبيقات المراد تضمينها أو استثنائها. establish() يطلب من النظام إنشاء واجهة النفق وتوجيه الحركة المطابقة إليها. النظام يملك قرار التوجيه؛ ويستقبل التطبيق واصف الملف وعليه بعد ذلك أن يفعل شيئًا مفيدًا بالحزم.

What you observeWho actually owns it
VPN consent dialogThe system, triggered by prepare()
Routes, DNS and app listsThe app requests them; the system applies them
One active VPN per profileThe platform; a new service replaces the old one
Always-on and block-without-VPNDevice policy enforced by the platform

هذا الجدول هو البنية في مكان واحد. يشارك التطبيق في معالجة الحزم؛ لكنه لا يملك الواجهة ولا جدول التوجيه ولا عدد الخدمات التي يجوز تشغيلها.

على مقبس النفق أن يهرب من نفقِه

الاتصال الذي يحمل الحركة المشفّرة هو نفسه مقبس. إذا أُعيد توجيهه إلى الواجهة التي أنشأها التطبيق للتوّ، يصبح النفق معتمدًا على النفق ويتوقف. VpnService.protect() يربط ذلك المقبس بالشبكة الأساسية فيتجاوز الالتقاط، ولهذا يحمي العميل الصحيح مقبسه الخارجي قبل أول مصافحة.

adb shell dumpsys connectivity | grep -i -A5 vpn
adb shell settings get global always_on_vpn_app
adb shell settings get global always_on_vpn_lockdown

تُظهر هذه الأوامر شبكة VPN المثبّتة، وحزمة التشغيل الدائم، وما إذا كان القفل مفعّلًا. إنها ملاحظات للقراءة فقط: إذا لم تطابق القيم ما يدّعيه التصميم، فالادعاء يخص واجهة التطبيق لا حالة النظام.

قد يكون الحجب من اختصاص المنصة

يوثّق أندرويد سياسة تحجب الاتصالات التي لا تستخدم VPN. ذلك التنفيذ عقد منصة، وليس تطبيقًا يعيد محاولة الاتصالات في حلقة. يوجد المفتاح في إعدادات VPN بالجهاز باسم حجب الاتصالات دون VPN، وعلى جهاز مُدار يمكن لمالك الجهاز تشغيله للملف بأكمله. يهم هذا التمييز عند تقييم ادعاء تسويقي: يمكن للعميل أن يعرض أيقونة قفل ويعيد الاتصال بشراسة بينما تبقى الحركة المباشرة ممكنة، أو يمكن للتطبيقات المشمولة أن تبقى دون اتصال لأن النظام ينفّذ القفل.

أيقونة القفل يرسمها التطبيق، فهي تثبت فقط أن التطبيق يعتقد أنه متصل. أما ما إذا كان تطبيق آخر يفقد وصوله للشبكة فيقرره النظام، لذا الفحص المفيد هو مشاهدة ذلك التطبيق يفشل والنفق قائم.

كذلك لا تحل علامة «التشغيل الدائم» في متجر التطبيقات محل ذلك العقد. التشغيل الدائم والقفل إعدادان للجهاز، غالبًا يفرضهما مالك الجهاز، وسلوكهما مع التطبيقات المستثناة يحدده النظام. اقرأ الإعداد لا الشارة.

أربعة أسئلة لمراجعة التصميم

أولًا، من وافق: أي حزمة، وأي ملف تعريف، وهل كان المستخدم أم سياسة جهاز مُدار؟ ثانيًا، ماذا طلب التطبيق: أي مسارات، وأي عائلات عناوين، وأي تطبيقات مشمولة أو مستثناة؟ ثالثًا، ماذا يحدث عندما يكون النفق غير متاح: هل يحجب النظام، أم تعود الحركة إلى الشبكة الأساسية؟ رابعًا، ماذا يفعل التطبيق بالحزم التي يستقبلها؟

السؤال الرابع هو ما لا يستطيع نموذج الأذونات الإجابة عنه. الموافقة وواجهة TUN تثبت أن التطبيق يستطيع استقبال الحركة، لا أنه يشفّرها أو يوثّق طرفًا آخر أو يتعافى بنظافة. هذه خصائص تنفيذ، وهنا تقضي المراجعة الصادقة وقتها.

طلب جذر غير ضروري لا يصبح مشروعًا بتسمية النتيجة مفتاح إيقاف، وحوار موافقة ليس تدقيقًا أمنيًا. أبقِ الأذونات الثلاثة منفصلة يصبح كل ادعاء قابلًا للفحص.

مقالات ذات صلة