تحسين أمان تطبيقك

ويساهم تعزيز أمان تطبيقك في الحفاظ على ثقة المستخدمين وسلامة أجهزتهم.

تقدّم هذه الصفحة العديد من أفضل الممارسات التي تُحدث تأثيرًا إيجابيًا هائلاً في أمان تطبيقك.

فرض الاتصالات الآمنة

عند حماية البيانات التي تتبادلها بين تطبيقك وغيرها من التطبيقات، أو بين تطبيقك وموقع إلكتروني، يمكنك تحسين ثبات تطبيقك وحماية البيانات التي ترسلها وتتلقّاها.

الحفاظ على التواصل بين التطبيقات

للتواصل بين التطبيقات بأمان أكبر، استخدِم النوايا الضمنية مع ملف اختيار التطبيقات وأذونات مستندة إلى التوقيع ومقدّمي المحتوى غير المُصدَّر.

عرض أداة اختيار التطبيقات

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

Kotlin

val intent = Intent(Intent.ACTION_SEND)
val possibleActivitiesList: List<ResolveInfo> =
        packageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL)

// Verify that an activity in at least two apps on the user's device
// can handle the intent. Otherwise, start the intent only if an app
// on the user's device can handle the intent.
if (possibleActivitiesList.size > 1) {

    // Create intent to show chooser.
    // Title is something similar to "Share this photo with."

    val chooser = resources.getString(R.string.chooser_title).let { title ->
        Intent.createChooser(intent, title)
    }
    startActivity(chooser)
} else if (intent.resolveActivity(packageManager) != null) {
    startActivity(intent)
}

Java

Intent intent = new Intent(Intent.ACTION_SEND);
List<ResolveInfo> possibleActivitiesList = getPackageManager()
        .queryIntentActivities(intent, PackageManager.MATCH_ALL);

// Verify that an activity in at least two apps on the user's device
// can handle the intent. Otherwise, start the intent only if an app
// on the user's device can handle the intent.
if (possibleActivitiesList.size() > 1) {

    // Create intent to show chooser.
    // Title is something similar to "Share this photo with."

    String title = getResources().getString(R.string.chooser_title);
    Intent chooser = Intent.createChooser(intent, title);
    startActivity(chooser);
} else if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(intent);
}

معلومات ذات صلة:

تطبيق الأذونات المستندة إلى التوقيع

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

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
    <permission android:name="my_custom_permission_name"
                android:protectionLevel="signature" />

معلومات ذات صلة:

عدم السماح بالوصول إلى موفّري المحتوى في تطبيقك

ما لم تكن تريد إرسال بيانات من تطبيقك إلى تطبيق مختلف لا تتعلّق بملكيتك، يجب منع تطبيقات المطوّرين الآخرين صراحةً من الوصول إلى ContentProvider عناصر تطبيقك. وهذا الإعداد مهم بشكل خاص إذا كان من الممكن تثبيت تطبيقك على أجهزة Android 4.1.1 (المستوى 16 من واجهة برمجة التطبيقات) أو الإصدارات الأقدم، لأنّ السمة android:exported للعنصر <provider> هي true تلقائيًا على تلك الإصدارات من Android.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
    <application ... >
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.myapp.fileprovider"
            ...
            android:exported="false">
            <!-- Place child elements of <provider> here. -->
        </provider>
        ...
    </application>
</manifest>

طلب بيانات الاعتماد قبل عرض معلومات حسّاسة

عند طلب بيانات اعتماد من المستخدمين كي يتمكّنوا من الوصول إلى معلومات حساسة أو محتوى مدفوع في تطبيقك، اطلب منهم إما إدخال رقم تعريف شخصي/كلمة مرور/نقش أو بيانات اعتماد بالمقاييس الحيوية، مثل التعرّف على الوجه أو بصمة الإصبع.

لمزيد من المعلومات حول طريقة طلب بيانات الاعتماد باستخدام المقاييس الحيوية، يُرجى الاطّلاع على دليل المصادقة بالمقاييس الحيوية.

تطبيق تدابير أمان الشبكة

توضّح الأقسام التالية كيفية تحسين أمان شبكة تطبيقك.

استخدام زيارات بروتوكول أمان طبقة النقل (TLS)

إذا كان تطبيقك يتواصل مع خادم ويب لديه شهادة صادرة عن مرجع تصديق موثوق به ومعروف، استخدِم طلب HTTPS على النحو التالي:

Kotlin

val url = URL("https://www.google.com")
val urlConnection = url.openConnection() as HttpsURLConnection
urlConnection.connect()
urlConnection.inputStream.use {
    ...
}

Java

URL url = new URL("https://www.google.com");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.connect();
InputStream in = urlConnection.getInputStream();

إضافة إعدادات أمان الشبكة

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

لإضافة ملف إعداد أمان الشبكة إلى تطبيقك، اتّبِع الخطوات التالية:

  1. حدِّد الإعدادات في ملف بيان تطبيقك:
  2. <manifest ... >
        <application
            android:networkSecurityConfig="@xml/network_security_config"
            ... >
            <!-- Place child elements of <application> element here. -->
        </application>
    </manifest>
    
  3. أضِف ملفّ مرجع XML، والذي يمكن العثور عليه في res/xml/network_security_config.xml.

    حدِّد أنّه يجب أن تستخدم جميع الزيارات إلى نطاقات معيّنة بروتوكول HTTPS من خلال إيقاف النصّ الواضح:

    <network-security-config>
        <domain-config cleartextTrafficPermitted="false">
            <domain includeSubdomains="true">secure.example.com</domain>
            ...
        </domain-config>
    </network-security-config>
    

    أثناء عملية التطوير، يمكنك استخدام العنصر <debug-overrides> للسماح صراحةً بالشهادات المثبَّتة من المستخدم. يتجاهل هذا العنصر خيارات تطبيقك المهمة للأمان أثناء تصحيح الأخطاء والاختبار بدون التأثير في إعدادات إصدار التطبيق. يوضِّح المقتطف التالي كيفية تحديد هذا العنصر في ملف XML الخاص بإعدادات أمان الشبكة لتطبيقك:

    <network-security-config>
        <debug-overrides>
            <trust-anchors>
                <certificates src="user" />
            </trust-anchors>
        </debug-overrides>
    </network-security-config>
    

معلومات ذات صلة: إعدادات أمان الشبكة

إنشاء مدير ثقة

يجب ألا يقبل مدقّق بروتوكول أمان طبقة النقل (TLS) كل شهادة. قد تحتاج إلى إعداد مدير ثقة ومعالجة جميع تحذيرات بروتوكول أمان طبقة النقل (TLS) التي تحدث إذا انطبق أحد الشروط التالية على حالة الاستخدام:

  • إذا كنت تتواصل مع خادم ويب لديه شهادة موقَّعة من قِبل هيئة إصدار شهادة جديدة أو مخصّصة
  • لا يثق الجهاز الذي تستخدمه في هيئة إصدار الشهادات هذه.
  • لا يمكنك استخدام إعدادات أمان الشبكات.

لمعرفة المزيد من المعلومات حول كيفية إكمال هذه الخطوات، اطّلِع على المناقشة حول التعامل مع هيئة اعتماد غير معروفة.

معلومات ذات صلة:

استخدام عناصر WebView بعناية

WebView يجب ألا تسمح العناصر في تطبيقك للمستخدمين بالانتقال إلى مواقع إلكترونية خارج نطاق تحكّمك. استخدِم قائمة مسموح بها كلما أمكن لتقييد المحتوى الذي تحمّله عناصر WebView في تطبيقك.

بالإضافة إلى ذلك، لا تفعِّل أبدًا دعم واجهة JavaScript ما لم تكن تتحكم بشكل كامل في المحتوى المضمّن في كائنات WebView في تطبيقك وتثق به.

استخدام قنوات رسائل HTML

إذا كان تطبيقك يتطلّب استخدام واجهة JavaScript على الأجهزة التي تعمل بالإصدار Android 6.0 (المستوى 23 من واجهة برمجة التطبيقات) والإصدارات الأحدث، استخدِم قنوات رسائل HTML بدلاً من التواصل بين موقع إلكتروني وتطبيقك، كما هو موضّح في مقتطف الرمز التالي:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)

// channel[0] and channel[1] represent the two ports.
// They are already entangled with each other and have been started.
val channel: Array<out WebMessagePort> = myWebView.createWebMessageChannel()

// Create handler for channel[0] to receive messages.
channel[0].setWebMessageCallback(object : WebMessagePort.WebMessageCallback() {

    override fun onMessage(port: WebMessagePort, message: WebMessage) {
        Log.d(TAG, "On port $port, received this message: $message")
    }
})

// Send a message from channel[1] to channel[0].
channel[1].postMessage(WebMessage("My secure message"))

Java

WebView myWebView = (WebView) findViewById(R.id.webview);

// channel[0] and channel[1] represent the two ports.
// They are already entangled with each other and have been started.
WebMessagePort[] channel = myWebView.createWebMessageChannel();

// Create handler for channel[0] to receive messages.
channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
    @Override
    public void onMessage(WebMessagePort port, WebMessage message) {
         Log.d(TAG, "On port " + port + ", received this message: " + message);
    }
});

// Send a message from channel[1] to channel[0].
channel[1].postMessage(new WebMessage("My secure message"));

معلومات ذات صلة:

تقديم الأذونات المناسبة

يجب طلب الحد الأدنى فقط من الأذونات اللازمة لكي يعمل تطبيقك بشكلٍ سليم. عند الإمكان، التخلي عن الأذونات عندما لا يحتاج التطبيق إليها

استخدام عناصر intent لتأجيل الأذونات

تجنَّب إضافة إذن إلى تطبيقك لإكمال إجراء يمكن إكماله في تطبيق آخر، كلما أمكن ذلك. وبدلاً من ذلك، استخدِم نية لإحالة الطلب إلى تطبيق مختلف لديه الإذن اللازم.

يوضّح المثال التالي كيفية استخدام نية لتوجيه المستخدمين إلى تطبيق جهات اتصال بدلاً من طلب إذنَي READ_CONTACTS وWRITE_CONTACTS:

Kotlin

// Delegates the responsibility of creating the contact to a contacts app,
// which has already been granted the appropriate WRITE_CONTACTS permission.
Intent(Intent.ACTION_INSERT).apply {
    type = ContactsContract.Contacts.CONTENT_TYPE
}.also { intent ->
    // Make sure that the user has a contacts app installed on their device.
    intent.resolveActivity(packageManager)?.run {
        startActivity(intent)
    }
}

Java

// Delegates the responsibility of creating the contact to a contacts app,
// which has already been granted the appropriate WRITE_CONTACTS permission.
Intent insertContactIntent = new Intent(Intent.ACTION_INSERT);
insertContactIntent.setType(ContactsContract.Contacts.CONTENT_TYPE);

// Make sure that the user has a contacts app installed on their device.
if (insertContactIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(insertContactIntent);
}

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

معلومات ذات صلة:

مشاركة البيانات بأمان بين التطبيقات

اتّبِع أفضل الممارسات التالية لمشاركة محتوى تطبيقك مع التطبيقات الأخرى بطريقة أكثر أمانًا:

  • فرض أذونات القراءة فقط أو الكتابة فقط حسب الحاجة
  • يمكنك منح العملاء إمكانية الوصول إلى البيانات لمرة واحدة باستخدام العلامتَين FLAG_GRANT_READ_URI_PERMISSION وFLAG_GRANT_WRITE_URI_PERMISSION.
  • عند مشاركة البيانات، استخدِم معرّفات الموارد المنتظِمة content:// وليس معرّفات الموارد المنتظِمة file://. وتتولى مثيلات "FileProvider" هذا الإجراء نيابةً عنك.

يوضّح المقتطف التالي من الرمز البرمجي كيفية استخدام علامات منح أذونات عناوين URL و أذونات مقدّمي المحتوى لعرض ملف PDF للتطبيق في تطبيق مستقل لعرض ملف PDF:

Kotlin

// Create an Intent to launch a PDF viewer for a file owned by this app.
Intent(Intent.ACTION_VIEW).apply {
    data = Uri.parse("content://com.example/personal-info.pdf")

    // This flag gives the started app read access to the file.
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}.also { intent ->
    // Make sure that the user has a PDF viewer app installed on their device.
    intent.resolveActivity(packageManager)?.run {
        startActivity(intent)
    }
}

Java

// Create an Intent to launch a PDF viewer for a file owned by this app.
Intent viewPdfIntent = new Intent(Intent.ACTION_VIEW);
viewPdfIntent.setData(Uri.parse("content://com.example/personal-info.pdf"));

// This flag gives the started app read access to the file.
viewPdfIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

// Make sure that the user has a PDF viewer app installed on their device.
if (viewPdfIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(viewPdfIntent);
}

ملاحظة: يؤدي تنفيذ الملفات من directory الرئيسي للتطبيق القابل للكتابة إلى انتهاك W^X. لهذا السبب، لا يمكن للتطبيقات غير الموثوق بها التي تستهدف الإصدار 10 من نظام التشغيل Android (المستوى 29 من واجهة برمجة التطبيقات) والإصدارات الأحدث استدعاء exec() على الملفات ضمن الدليل الرئيسي للتطبيق، بل فقط الرمز الثنائي المضمّن في ملف APK الخاص بالتطبيق. بالإضافة إلى ذلك، لا يمكن للتطبيقات التي تستهدف الإصدار 10 من Android والإصدارات الأحدث تعديل الرمز القابل للتنفيذ من الملفات التي تم فتحها باستخدام dlopen() في ذاكرتها. ويشمل ذلك أي ملفات عناصر مشترَكة (.so) التي تم نقل أماكن نصوصها.

معلومات ذات صلة: android:grantUriPermissions

تخزين البيانات بأمان

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

تخزين البيانات الخاصة في وحدة التخزين الداخلية

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

يوضِّح مقتطف الرمز التالي طريقة واحدة لكتابة البيانات في ملف التخزين الداخلي:

Kotlin

// Creates a file with this name, or replaces an existing file
// that has the same name. Note that the file name cannot contain
// path separators.
val FILE_NAME = "sensitive_info.txt"
val fileContents = "This is some top-secret information!"
File(filesDir, FILE_NAME).bufferedWriter().use { writer ->
    writer.write(fileContents)
}

Java

// Creates a file with this name, or replaces an existing file
// that has the same name. Note that the file name cannot contain
// path separators.
final String FILE_NAME = "sensitive_info.txt";
String fileContents = "This is some top-secret information!";
try (BufferedWriter writer =
             new BufferedWriter(new FileWriter(new File(getFilesDir(), FILE_NAME)))) {
    writer.write(fileContents);
} catch (IOException e) {
    // Handle exception.
}

يعرض مقتطف الرمز التالي العملية العكسية، وقراءة البيانات من وحدة التخزين الداخلية:

Kotlin

val FILE_NAME = "sensitive_info.txt"
val contents = File(filesDir, FILE_NAME).bufferedReader().useLines { lines ->
    lines.fold("") { working, line ->
        "$working\n$line"
    }
}

Java

final String FILE_NAME = "sensitive_info.txt";
StringBuffer stringBuffer = new StringBuffer();
try (BufferedReader reader =
             new BufferedReader(new FileReader(new File(getFilesDir(), FILE_NAME)))) {

    String line = reader.readLine();
    while (line != null) {
        stringBuffer.append(line).append('\n');
        line = reader.readLine();
    }
} catch (IOException e) {
    // Handle exception.
}

معلومات ذات صلة:

تخزين البيانات في وحدة تخزين خارجية بناءً على حالة الاستخدام

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

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

إذا كان تطبيقك يحتاج إلى الوصول إلى ملف يقدّم قيمة للتطبيقات الأخرى أو تخزينه، استخدِم إحدى واجهات برمجة التطبيقات التالية، استنادًا إلى حالة الاستخدام:

التحقّق من توفّر مساحة تخزين

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

التحقّق من صحة البيانات

إذا كان تطبيقك يستخدم بيانات من مساحة تخزين خارجية، تأكَّد من أنّ محتويات البيانات لم يتم تلفها أو تعديلها. يجب تضمين منطق لمعالجة الملفات التي لم تعُد بتنسيق ثابت.

يتضمّن المقتطف البرمجي التالي مثالاً على مدقّق التجزئة:

Kotlin

val hash = calculateHash(stream)
// Store "expectedHash" in a secure location.
if (hash == expectedHash) {
    // Work with the content.
}

// Calculating the hash code can take quite a bit of time, so it shouldn't
// be done on the main thread.
suspend fun calculateHash(stream: InputStream): String {
    return withContext(Dispatchers.IO) {
        val digest = MessageDigest.getInstance("SHA-512")
        val digestStream = DigestInputStream(stream, digest)
        while (digestStream.read() != -1) {
            // The DigestInputStream does the work; nothing for us to do.
        }
        digest.digest().joinToString(":") { "%02x".format(it) }
    }
}

Java

Executor threadPoolExecutor = Executors.newFixedThreadPool(4);
private interface HashCallback {
    void onHashCalculated(@Nullable String hash);
}

boolean hashRunning = calculateHash(inputStream, threadPoolExecutor, hash -> {
    if (Objects.equals(hash, expectedHash)) {
        // Work with the content.
    }
});

if (!hashRunning) {
    // There was an error setting up the hash function.
}

private boolean calculateHash(@NonNull InputStream stream,
                              @NonNull Executor executor,
                              @NonNull HashCallback hashCallback) {
    final MessageDigest digest;
    try {
        digest = MessageDigest.getInstance("SHA-512");
    } catch (NoSuchAlgorithmException nsa) {
        return false;
    }

    // Calculating the hash code can take quite a bit of time, so it shouldn't
    // be done on the main thread.
    executor.execute(() -> {
        String hash;
        try (DigestInputStream digestStream =
                new DigestInputStream(stream, digest)) {
            while (digestStream.read() != -1) {
                // The DigestInputStream does the work; nothing for us to do.
            }
            StringBuilder builder = new StringBuilder();
            for (byte aByte : digest.digest()) {
                builder.append(String.format("%02x", aByte)).append(':');
            }
            hash = builder.substring(0, builder.length() - 1);
        } catch (IOException e) {
            hash = null;
        }

        final String calculatedHash = hash;
        runOnUiThread(() -> hashCallback.onHashCalculated(calculatedHash));
    });
    return true;
}

تخزين البيانات غير الحساسة فقط في ملفات ذاكرة التخزين المؤقت

لتوفير إمكانية الوصول بشكل أسرع إلى بيانات التطبيقات غير الحسّاسة، يمكنك تخزينها في ملف التخزين المؤقت على الجهاز. بالنسبة إلى ذاكرات التخزين المؤقت الأكبر من 1 ميغابايت، استخدِم getExternalCacheDir(). بالنسبة إلى ذاكرات التخزين المؤقت التي تبلغ 1 ميغابايت أو أقل، استخدِم getCacheDir(). تقدّم لك كلتا الطريقتَين عنصر File الذي يحتوي على بيانات تطبيقك المخزّنة مؤقتًا.

يوضّح مقتطف الرمز البرمجي التالي كيفية تخزين ملف تم تنزيله مؤخرًا في ذاكرة التخزين المؤقت من خلال تطبيقك:

Kotlin

val cacheFile = File(myDownloadedFileUri).let { fileToCache ->
    File(cacheDir.path, fileToCache.name)
}

Java

File cacheDir = getCacheDir();
File fileToCache = new File(myDownloadedFileUri);
String fileToCacheName = fileToCache.getName();
File cacheFile = new File(cacheDir.getPath(), fileToCacheName);

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

تنبيه: لا يتم فرض أي إجراءات أمان على هذه الملفات. وبالتالي، يمكن لأي تطبيق يستهدف الإصدار 10 من نظام التشغيل Android (المستوى 29 لواجهة برمجة التطبيقات) أو إصدارًا أقل ويملك الإذن WRITE_EXTERNAL_STORAGE الوصول إلى محتوى ذاكرة التخزين المؤقت هذه.

معلومات ذات صلة: نظرة عامة على تخزين البيانات والملفات

استخدام SharedPreferences في الوضع الخاص

عند استخدام getSharedPreferences() لإنشاء كائنات SharedPreferences في تطبيقك أو الوصول إليها، استخدِم MODE_PRIVATE. بهذه الطريقة، لا يمكن إلا لتطبيقك الوصول إلى المعلومات في ملف الإعدادات المفضّلة المشتركة.

إذا كنت تريد مشاركة البيانات في جميع التطبيقات، لا تستخدِم SharedPreferences العناصر. بدلاً من ذلك، اتّبِع الخطوات لمشاركة data بشكل آمن على جميع التطبيقات.

وتوفر مكتبة الأمان أيضًا الفئة EncryptedSharedPreferences التي تضمّ فئة SharedPreferences وتشفّر المفاتيح والقيم تلقائيًا.

معلومات ذات صلة:

إبقاء الخدمات والتبعيات محدَّثة

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

التحقّق من مقدّم أمان "خدمات Google Play"

ملاحظة: لا ينطبق هذا القسم إلا على التطبيقات التي تستهدف الأجهزة التي تم تثبيت خدمات Google Play عليها.

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

لتحديد ما إذا كانت "خدمات Google Play" محدّثة على الجهاز الذي تم تثبيت تطبيقك عليه، اتّبِع الخطوات الواردة في الدليل حول تحديث مقدّم الأمان للحماية من عمليات اختراق بروتوكول SSL.

معلومات ذات صلة:

تعديل جميع متطلّبات التطبيق

قبل نشر تطبيقك، تأكَّد من تحديث جميع المكتبات وحِزم SDK وغيرها من التبعيات:

  • بالنسبة إلى التبعيات التابعة للطرف الأول، مثل حزمة تطوير البرامج (SDK) لنظام التشغيل Android، استخدِم أدوات التحديث المتوفّرة في "استوديو Android"، مثل مدير حزمة SDK.
  • بالنسبة إلى العناصر الخارجية التي يعتمد عليها تطبيقك، يُرجى التحقّق من المواقع الإلكترونية للمكتبات التي يستخدمها تطبيقك وتثبيت أي تحديثات ورموز تصحيح أمان متوفّرة.

معلومات ذات صلة: إضافة إنشاء التبعيات

مزيد من المعلومات

للتعرّف على مزيد من المعلومات عن كيفية تعزيز أمان تطبيقك، اطّلِع على المراجع التالية: