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

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

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

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

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

تأمين الاتصالات بين التطبيقات

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

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

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

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) التي تحدث إذا انطبق أحد الشروط التالية على حالة استخدامك:

  • أنت تتصل بخادم ويب يحتوي على شهادة موقعة من مرجع تصديق جديد أو مخصص.
  • الجهاز الذي تستخدمه غير يثق بهيئة إصدار الشهادات (CA).
  • لا يمكنك استخدام إعدادات أمان الشبكات.

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

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

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

يعرض مقتطف الرمز التالي كيفية استخدام علامات منح إذن معرّف الموارد المنتظم وأذونات موفّر المحتوى لعرض ملف 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.
  • بالنسبة إلى العناصر الخارجية التي يعتمد عليها تطبيقك، يُرجى التحقّق من المواقع الإلكترونية للمكتبات التي يستخدمها تطبيقك وتثبيت أي تحديثات ورموز تصحيح أمان متوفّرة.

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

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

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