EncryptedSharedPreferences هو مكون من مكتبة AndroidX Security يوفر تشفيراً شفافاً للبيانات المحفوظة عبر واجهة برمجة تطبيقات SharedPreferences. على عكس SharedPreferences العادية، حيث يتم تخزين البيانات في ملف XML مفتوح، يقوم EncryptedSharedPreferences تلقائياً بتشفير المفاتيح والقيم قبل الكتابة على القرص. وفقاً لـ Android Developers، تستخدم المكتبة AES-256 GCM للقيم وAES-256 SIV (RFC 5297) للمفاتيح، مما يضمن سرية البيانات وسلامتها.
النقاط الرئيسية
EncryptedSharedPreferences هي فئة من حزمة androidx.security.crypto، تم تقديمها في AndroidX Security 1.0.0 (2019). تقوم بتطبيق واجهة SharedPreferences، ولكن جميع عمليات الكتابة (putString، putInt، putBoolean، إلخ) تشفر البيانات مسبقاً، وعمليات القراءة تفك تشفيرها قبل الإرجاع.
SharedPreferences القياسية تحفظ البيانات في ملف XML في دليل التطبيق (/data/data/package/shared_prefs/). الملف غير مشفر — مع وصول الجذر إلى الجهاز أو أثناء تحليل النسخ الاحتياطي، تُقرأ جميع البيانات كـ XML عادي. رموز المصادقة ومفاتيح API والبيانات الشخصية للمستخدم تصبح متاحة للمهاجم.
EncryptedSharedPreferences يحل هذه المشكلة على مستوى المكتبة: يتم تشفير البيانات قبل الكتابة على القرص وفك تشفيرها عند القراءة. لا يحتاج المطور إلى استدعاء وظائف التشفير يدوياً — تبقى واجهة API مطابقة لـ SharedPreferences العادية.
مكتبة AndroidX Security v1.0.0 صدرت في ديسمبر 2019. حلت EncryptedSharedPreferences محل النهج القديم للتشفير اليدوي عبر Cipher + SharedPreferences. الإصدار المستقر الحالي هو 1.1.0-alpha06 (2024)، ويدعم API 19+. المكتبة جزء من Jetpack ولا تتطلب أذونات إضافية.
وفقاً لـ مدونة Google Security (2024)، EncryptedSharedPreferences هي الطريقة الموصى بها لتخزين إعدادات التطبيق الحساسة التي لا تتطلب مزامنة عبر السحابة. للسيناريوهات الأكثر تعقيداً، يُوصى باستخدام Room مع تشفير SQLCipher.
EncryptedSharedPreferences يستخدم مخطط تشفير ثنائي المستوى: المفتاح الرئيسي يُخزَّن في Android Keystore، وتُستخدم مفاتيح مشتقة لتشفير البيانات. هذا يجمع بين حماية Keystore وأداء التشفير المتماثل.
لـلقيم يُستخدم AES-256 GCM (وضع Galois/Counter) — وضع تشفير موثق (AEAD) يضمن سرية البيانات وسلامتها. لـلمفاتيح (أسماء المعاملات) يُطبق AES-256 SIV (RFC 5297) — تشفير حتمي ضروري للبحث عن المفتاح دون الكشف عن محتواه.
كل ملف في EncryptedSharedPreferences يحتوي على أزواج مفتاح-قيمة مشفرة. هيكل الملف يشمل: أولاً رأس مع بيانات وصفية (إصدار، معرف مفتاح)، ثم قائمة من الإدخالات المشفرة. الملف ليس XML صالحاً ولا يمكن قراءته بواسطة محررات النصوص.
فئة MasterKey مسؤولة عن إنشاء وإدارة مفتاح رئيسي بحجم 256 بت يُخزَّن في Android Keystore. يتيح MasterKey.Builder تكوين: نوع التخزين (Keystore أو برمجي)، الحماية البيومترية، وعمر المفتاح. افتراضياً، يتم إنشاء المفتاح الرئيسي في Android Keystore باستخدام خوارزمية AES/GCM/NoPadding.
import androidx.security.crypto.MasterKey
import androidx.security.crypto.EncryptedSharedPreferences
fun getEncryptedPrefs() {
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.AES256_GCM_SPEC)
.build()
val prefs = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
EncryptedSharedPreferences.create يقبل خمس معاملات: السياق، اسم الملف، المفتاح الرئيسي، مخطط تشفير المفاتيح، ومخطط تشفير القيم. اختيار المخططات يؤثر على الأداء ومستوى الأمان.
AES256_SIV — تشفير حتمي: المفاتيح المتطابقة تنتج دائماً نفس النص المشفر. هذا ضروري للبحث عن المفاتيح (SharedPreferences.getX(key)). العيب: يمكن للمهاجم تحديد المفاتيح المستخدمة من خلال مطابقة النصوص المشفرة المتكررة. AES256_SIV2 — إصدار محسّن مع إضافة العشوائية.
لـلقيم يُستخدم AES256_GCM. يضيف GCM IV بحجم 12 بايت (متجه التهيئة) وعلامة مصادقة بحجم 16 بايت لكل قيمة. هذا يوفر السرية (لا يمكن لأحد قراءة القيمة) والمصادقة (لا يمكن لأحد التلاعب بالقيمة دون اكتشاف).
طريقة setUserAuthenticationRequired(true) في MasterKey.Builder تتطلب تأكيداً بيومترياً قبل استرجاع المفتاح الرئيسي من Keystore. هذا يضيف طبقة إضافية: حتى إذا كان التطبيق يعمل على جهاز غير مقفل، لا يمكن للمهاجم قراءة EncryptedSharedPreferences بدون Face ID أو Touch ID.
هام: مع setUserAuthenticationRequired، يصبح المفتاح الرئيسي غير متاح إذا قام المستخدم بتغيير أو إزالة البيانات البيومترية. يجب معالجة KeyPermanentlyInvalidatedException وإنشاء مفتاح رئيسي جديد مع ترحيل البيانات.
fun createBiometricKey(): MasterKey {
return MasterKey.Builder(context)
.setKeyScheme(MasterKey.AES256_GCM_SPEC)
.setUserAuthenticationRequired(true)
.setRequestStrongBoxBacked(true)
.build()
}
fun writeSecureToken(token: String) {
try {
prefs.edit().putString("auth_token", token).apply()
} catch (e: KeyPermanentlyInvalidatedException) {
// تغيرت البيانات البيومترية — يجب إعادة إنشاء المفتاح
}
}
لنلق نظرة على مثال كامل لدمج EncryptedSharedPreferences في تطبيق Android باستخدام Kotlin. مكتبة androidx.security:security-crypto تُضاف عبر Gradle.
في ملف build.gradle (app)، أضف: implementation "androidx.security:security-crypto:1.1.0-alpha06". لمشاريع Kotlin، مطلوب أيضاً kotlin-stdlib. تهيئة MasterKey تحدث مرة واحدة، عادة في Application.onCreate أو عبر حاوية DI.
بعد إنشاء مثيل EncryptedSharedPreferences، واجهة API لا تختلف عن SharedPreferences العادية. edit() ترجع Editor، جميع الطرق (putString، getString، putBoolean، getBoolean) تعمل بنفس الطريقة. الفرق الوحيد داخلي: البيانات تُشفر عند الكتابة وتُفك تشفيرها عند القراءة.
class AuthRepository(context: Context) {
private val prefs = createEncryptedPrefs(context)
fun saveCredentials(login: String, password: String) {
prefs.edit()
.putString("login", login)
.putString("password", password)
.apply()
}
fun getToken(): String? {
return prefs.getString("auth_token", null)
}
fun clearAll() {
prefs.edit().clear().apply()
}
}
لـترحيل البيانات الموجودة من SharedPreferences غير المشفرة إلى EncryptedSharedPreferences، يجب: قراءة جميع البيانات من الملف القديم، إنشاء EncryptedSharedPreferences جديدة، كتابة جميع البيانات، وحذف الملف القديم. Google لا توفر مرحلة ترحيل مدمجة — المطور ينفذها يدوياً.
الاختيار بين SharedPreferences و EncryptedSharedPreferences يعتمد على نوع البيانات المخزنة. لـإعدادات واجهة المستخدم (السمة، اللغة، الترتيب)، SharedPreferences العادية كافية. للمعلومات السرية (الرموز، كلمات المرور، المفاتيح)، EncryptedSharedPreferences إلزامي.
EncryptedSharedPreferences أبطأ من العادي بسبب عمليات التشفير. كتابة قيمة نصية واحدة تستغرق ~5-15 مللي ثانية (حسب حجم البيانات وتسريع AES للعتاد). القراءة تستغرق 2-5 مللي ثانية. لمعظم التطبيقات هذا غير ملحوظ، ولكن للعمليات الدفعية (الترحيل، الاستعادة) استخدم apply() بدلاً من commit().
SharedPreferences العادية لا توفر أي حماية تشفيرية: ملف XML يمكن قراءته بواسطة أي عملية مع وصول جذر أو عبر adb backup. EncryptedSharedPreferences يشفر البيانات على مستوى التطبيق، والمفتاح الرئيسي يُخزَّن في Android Keystore مع حماية عتادية اختيارية (StrongBox).
| الخاصية | SharedPreferences | EncryptedSharedPreferences |
|---|---|---|
| التخزين | XML مفتوح | ملف ثنائي مشفر |
| التشفير | لا يوجد | AES-256 GCM + SIV |
| حماية المفاتيح | لا توجد | Android Keystore + StrongBox |
| الأداء | 0.1-1 مللي ثانية | 2-15 مللي ثانية |
| التوصية | إعدادات UI | الرموز، المفاتيح، PII |
استخدم EncryptedSharedPreferences لتخزين: رموز تحديث OAuth، مفاتيح API للخدمات الخارجية، البريد الإلكتروني أو رقم هاتف المستخدم، وإعدادات التطبيق الحساسة (PIN، علامات المصادقة). EncryptedSharedPreferences غير مناسب لتخزين البيانات البيومترية أو المستندات الكبيرة — استخدم EncryptedFile أو Room مع SQLCipher.
القاعدة العامة: إذا كان تسرب البيانات سيضر بالمستخدم أو العمل — استخدم EncryptedSharedPreferences. إذا كانت البيانات تجميلية فقط (السمة، اللغة، الترتيب) — SharedPreferences العادية. من المنطقي تنفيذ EncryptedSharedPreferences منذ البداية، دون إعادة هيكلة: استبداله في مشروع قائم يتطلب ترحيلاً ومعالجة البيانات القديمة غير المشفرة.
تذكر أن EncryptedSharedPreferences لا يحمي البيانات أثناء تشغيل التطبيق — فقط على القرص. إذا كان للمهاجم وصول إلى ذاكرة العملية، يمكن اعتراض البيانات المفكوك تشفيرها. استخدم حماية إضافية: ProGuard/DexGuard للتمويه.
الأسئلة الشائعة
Jetpack DataStore هو بديل أكثر حداثة لـ SharedPreferences، يعتمد على Flow و coroutines في Kotlin. DataStore لا يشفر البيانات افتراضياً، ولكن يمكن دمجه مع EncryptedSharedPreferences أو استخدامه مع التشفير اليدوي عبر Proto DataStore مع بروتوكولات التشفير.
لا يُوصى بذلك. EncryptedSharedPreferences مصمم للأحجام الصغيرة (حتى 100-200 كيلوبايت). للبيانات الأكبر، استخدم Room مع SQLCipher أو تشفير الملفات عبر EncryptedFile من نفس مكتبة AndroidX Security.
لا، لا يوجد ترحيل مخطط تلقائي. عند تغيير هيكل البيانات، يجب على المطور قراءة البيانات القديمة يدوياً عبر KeyGen القديم وكتابتها عبر الجديد. يُوصى بتخزين إصدار المخطط في معامل منفصل.
AndroidX Security 1.0.0 يدعم API 19+ (Android KitKat). الإصدار 1.1.0-alpha06 يدعم أيضاً API 19+. StrongBox يتطلب API 28+ وجهازاً مع دعم عتادي (Google Pixel 3+، Samsung Galaxy S9+).
نعم، refresh token هو أحد حالات الاستخدام الرئيسية. تشفير AES-256 GCM، المفتاح الرئيسي في Keystore، الحماية البيومترية — مستوى كافٍ لرموز OAuth. لرموز الوصول قصيرة العمر مناسب أيضاً، على الرغم من أن بعض الفرق تفضل تخزينها في الذاكرة.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا