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

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

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

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

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

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

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

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

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

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 في تطبيقك. تكتسب هذه الإعدادات أهمية خاصة إذا كان يمكن تثبيت تطبيقك على الأجهزة التي تعمل بالإصدار 4.1.1 من نظام التشغيل Android (المستوى 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 على الأجهزة التي تعمل بالإصدار 6.0 (المستوى 23 من واجهة برمجة التطبيقات) والإصدارات الأحدث من نظام التشغيل Android، استخدِم قنوات رسائل 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"));

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

منح الأذونات المناسبة

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

استخدام الأهداف لتأجيل الأذونات

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

يوضّح المثال التالي كيفية استخدام هدف لتوجيه المستخدمين إلى تطبيق جهات اتصال بدلاً من طلب الإذنَين 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.
  • عند مشاركة البيانات، استخدِم معرّفات الموارد المنتظمة (URI) من النوع content:// وليس من النوع file://. تتضمّن هذه الحالات FileProvider ما يلي:

يوضّح مقتطف الرمز التالي كيفية استخدام علامات منح إذن عنوان URI وأذونات موفّر المحتوى لعرض ملف 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);
}

ملاحظة: يُعد تنفيذ الملفات من دليل التطبيق الرئيسي القابل للكتابة انتهاكًا لقاعدة 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() لوضع ذاكرة التخزين المؤقت لتطبيقك في مساحة التخزين المشتركة، قد يخرج المستخدم الوسائط التي تحتوي على مساحة التخزين هذه أثناء تشغيل تطبيقك. تضمين منطق للتعامل مع عدم توفّر البيانات في ذاكرة التخزين المؤقت بشكل سليم بسبب سلوك المستخدم هذا

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

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

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

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

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

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

تحديث الخدمات والتبعيات باستمرار

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

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

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

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

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

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

تعديل جميع العناصر التابعة للتطبيق

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

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

معلومات ذات صلة: إضافة تبعيات الإصدار

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

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