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

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

تقدّم هذه الصفحة العديد من أفضل الممارسات التي تؤثر بشكلٍ إيجابي وملحوظ في أمان تطبيقك.

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

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

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

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

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

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

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 على الأجهزة التي تعمل بالإصدار 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"));

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

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

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

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

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

يوضِّح المثال التالي كيفية استخدام نية لتوجيه المستخدمين إلى تطبيق جهات اتصال بدلاً من طلب إذنَي 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 هذا الإجراء نيابةً عنك.

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

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

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

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