ปรับปรุงความปลอดภัยของแอป

การทำให้แอปมีความปลอดภัยมากขึ้นจะช่วยรักษาความเชื่อมั่นของผู้ใช้และความสมบูรณ์ของอุปกรณ์

หน้านี้แสดงแนวทางปฏิบัติแนะนำหลายประการที่ส่งผลเชิงบวกอย่างมากต่อความปลอดภัยของแอป

บังคับใช้การสื่อสารที่ปลอดภัย

การรักษาความปลอดภัยของข้อมูลที่แลกเปลี่ยนระหว่างแอปกับแอปอื่นๆ หรือระหว่างแอปกับเว็บไซต์จะช่วยปรับปรุงความเสถียรของแอปและปกป้องข้อมูลที่ส่งและรับ

รักษาความปลอดภัยในการสื่อสารระหว่างแอป

หากต้องการสื่อสารระหว่างแอปอย่างปลอดภัยยิ่งขึ้น ให้ใช้ Intent ที่ไม่ชัดแจ้งกับเครื่องมือเลือกแอป สิทธิ์ตามลายเซ็น และผู้จัดหาเนื้อหาที่ไม่ได้ส่งออก

แสดงเครื่องมือเลือกแอป

หาก Intent ที่ไม่ชัดสามารถเปิดแอปที่เป็นไปได้อย่างน้อย 2 แอปในอุปกรณ์ของผู้ใช้ ให้แสดงเครื่องมือเลือกแอปอย่างชัดเจน กลยุทธ์การโต้ตอบนี้ช่วยให้ผู้ใช้สามารถโอนข้อมูลที่ละเอียดอ่อนไปยังแอปที่ตนเชื่อถือได้

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);
}

ข้อมูลที่เกี่ยวข้อง:

ใช้สิทธิ์ตามลายเซ็น

เมื่อแชร์ข้อมูลระหว่างแอป 2 แอปที่คุณควบคุมหรือเป็นเจ้าของ ให้ใช้สิทธิ์ตามลายเซ็น สิทธิ์เหล่านี้ไม่จําเป็นต้องได้รับการยืนยันจากผู้ใช้ แต่จะใช้การตรวจสอบว่าแอปที่เข้าถึงข้อมูลลงนามโดยใช้คีย์การรับรองเดียวกัน สิทธิ์เหล่านี้จึงมอบประสบการณ์การใช้งานที่ปลอดภัยและมีประสิทธิภาพยิ่งขึ้นแก่ผู้ใช้

<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 (API ระดับ 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>

ขอข้อมูลเข้าสู่ระบบก่อนแสดงข้อมูลที่ละเอียดอ่อน

เมื่อขอข้อมูลเข้าสู่ระบบจากผู้ใช้เพื่อให้เข้าถึงข้อมูลที่ละเอียดอ่อนหรือเนื้อหาพรีเมียมในแอปได้ ให้ขอ PIN/รหัสผ่าน/รูปแบบ หรือข้อมูลเข้าสู่ระบบแบบไบโอเมตริก เช่น การจดจำใบหน้าหรือการจดจำลายนิ้วมือ

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีขอข้อมูลเข้าสู่ระบบแบบไบโอเมตริกได้ที่คำแนะนำเกี่ยวกับการตรวจสอบสิทธิ์แบบไบโอเมตริก

ใช้มาตรการรักษาความปลอดภัยของเครือข่าย

ส่วนต่อไปนี้อธิบายวิธีปรับปรุงความปลอดภัยของเครือข่ายของแอป

ใช้การรับส่งข้อมูล TLS

หากแอปของคุณสื่อสารกับเว็บเซิร์ฟเวอร์ที่มีใบรับรองซึ่งออกโดยผู้ออกใบรับรอง (CA) ที่เชื่อถือได้และเป็นที่รู้จัก ให้ใช้คำขอ 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();

เพิ่มการกำหนดค่าการรักษาความปลอดภัยของเครือข่าย

หากแอปใช้ CA ใหม่หรือที่กำหนดเอง คุณสามารถประกาศการตั้งค่าความปลอดภัยของเครือข่ายในไฟล์กำหนดค่าได้ ขั้นตอนนี้ช่วยให้คุณสร้างการกำหนดค่าได้โดยไม่ต้องแก้ไขโค้ดแอป

หากต้องการเพิ่มไฟล์การกำหนดค่าความปลอดภัยของเครือข่ายลงในแอป ให้ทำตามขั้นตอนต่อไปนี้

  1. ประกาศการกําหนดค่าในไฟล์ Manifest ของแอป
  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 ใหม่หรือ CA ที่กําหนดเอง
  • อุปกรณ์ที่คุณใช้ไม่เชื่อถือ CA ดังกล่าว
  • คุณใช้การกำหนดค่าความปลอดภัยเครือข่ายไม่ได้

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีดำเนินการตามขั้นตอนเหล่านี้ได้ที่การสนทนาเกี่ยวกับการจัดการหน่วยงานที่รับรองที่ไม่รู้จัก

ข้อมูลที่เกี่ยวข้อง:

ใช้ออบเจ็กต์ WebView อย่างระมัดระวัง

WebView ออบเจ็กต์ในแอปไม่ควรอนุญาตให้ผู้ใช้ไปยังเว็บไซต์ที่อยู่นอกเหนือการควบคุมของคุณ ใช้รายการที่อนุญาตเพื่อจำกัดเนื้อหาที่โหลดโดยออบเจ็กต์ WebView ของแอปทุกครั้งที่ทำได้

นอกจากนี้ อย่าเปิดใช้การรองรับอินเทอร์เฟซ JavaScript เว้นแต่คุณจะควบคุมและเชื่อถือเนื้อหาในออบเจ็กต์ WebView ของแอปโดยสมบูรณ์

ใช้ช่องทางข้อความ HTML

หากแอปของคุณต้องใช้การรองรับอินเทอร์เฟซ JavaScript ในอุปกรณ์ที่ใช้ Android 6.0 (API ระดับ 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 เพื่อเลื่อนสิทธิ์

โปรดอย่าเพิ่มสิทธิ์ลงในแอปเพื่อดําเนินการซึ่งทําได้ในแอปอื่น หากเป็นไปได้ ให้ใช้ Intent เพื่อเลื่อนคําขอไปยังแอปอื่นที่มีสิทธิ์ที่จําเป็นอยู่แล้ว

ตัวอย่างต่อไปนี้แสดงวิธีใช้ 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);
}

นอกจากนี้ หากแอปต้องดำเนินการ I/O ตามไฟล์ เช่น การเข้าถึงพื้นที่เก็บข้อมูลหรือการเลือกไฟล์ แอปก็ไม่จำเป็นต้องมีสิทธิ์พิเศษ เนื่องจากระบบสามารถดำเนินการในนามของแอปให้เสร็จสมบูรณ์ได้ ที่สำคัญกว่านั้น หลังจากผู้ใช้เลือกเนื้อหาที่ URI หนึ่งๆ แล้ว แอปที่เรียกใช้จะได้รับสิทธิ์เข้าถึงทรัพยากรที่เลือก

ข้อมูลที่เกี่ยวข้อง:

แชร์ข้อมูลอย่างปลอดภัยในแอปต่างๆ

ทําตามแนวทางปฏิบัติแนะนําต่อไปนี้เพื่อแชร์เนื้อหาของแอปกับแอปอื่นๆ ในลักษณะที่ปลอดภัยยิ่งขึ้น

  • บังคับใช้สิทธิ์แบบอ่านอย่างเดียวหรือเขียนอย่างเดียวตามต้องการ
  • ให้สิทธิ์เข้าถึงข้อมูลแบบครั้งเดียวแก่ลูกค้าโดยใช้ Flag FLAG_GRANT_READ_URI_PERMISSION และ FLAG_GRANT_WRITE_URI_PERMISSION
  • เมื่อแชร์ข้อมูล ให้ใช้ URI content:// ไม่ใช่ URI file:// อินสแตนซ์ของ FileProvider จะดำเนินการนี้ให้คุณ

ข้อมูลโค้ดต่อไปนี้แสดงวิธีใช้ Flag การให้สิทธิ์ 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 ด้วยเหตุนี้ แอปที่ไม่น่าเชื่อถือซึ่งกำหนดเป้าหมายเป็น Android 10 (API ระดับ 29) ขึ้นไปจึงเรียกใช้ exec() ในไฟล์ภายในไดเรกทอรีหน้าแรกของแอปไม่ได้ ใช้ได้เฉพาะโค้ดไบนารีที่ฝังอยู่ในไฟล์ APK ของแอปเท่านั้น นอกจากนี้ แอปที่กําหนดเป้าหมายเป็น Android 10 ขึ้นไปจะแก้ไขโค้ดที่เรียกใช้งานได้จากไฟล์ที่เปิดด้วย 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.
}

ข้อมูลที่เกี่ยวข้อง:

จัดเก็บข้อมูลไว้ในที่จัดเก็บข้อมูลภายนอกตาม Use Case

ใช้พื้นที่เก็บข้อมูลภายนอกสำหรับไฟล์ขนาดใหญ่ที่ไม่ละเอียดอ่อนซึ่งเจาะจงสำหรับแอปของคุณ รวมถึงไฟล์ที่แอปของคุณแชร์กับแอปอื่นๆ API ที่เฉพาะเจาะจงที่คุณใช้จะขึ้นอยู่กับว่าแอปออกแบบมาเพื่อเข้าถึงไฟล์เฉพาะแอปหรือเข้าถึงไฟล์ที่แชร์

หากไฟล์ไม่มีข้อมูลส่วนตัวหรือข้อมูลที่ละเอียดอ่อน แต่ให้คุณค่าแก่ผู้ใช้ในแอปของคุณเท่านั้น ให้จัดเก็บไฟล์ในไดเรกทอรีเฉพาะแอปในที่จัดเก็บข้อมูลภายนอก

หากแอปของคุณจำเป็นต้องเข้าถึงหรือจัดเก็บไฟล์ที่ให้คุณค่าแก่แอปอื่นๆ ให้ใช้ API รายการใดรายการหนึ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับ Use Case ของคุณ

ตรวจสอบความพร้อมใช้งานของวอลุ่มพื้นที่เก็บข้อมูล

หากแอปของคุณโต้ตอบกับอุปกรณ์จัดเก็บข้อมูลภายนอกแบบถอดได้ โปรดทราบว่าผู้ใช้อาจนำอุปกรณ์จัดเก็บข้อมูลออกขณะที่แอปพยายามเข้าถึง ใส่ตรรกะเพื่อยืนยันว่าอุปกรณ์เก็บข้อมูลพร้อมใช้งาน

ตรวจสอบความถูกต้องของข้อมูล

หากแอปใช้ข้อมูลจากพื้นที่เก็บข้อมูลภายนอก ให้ตรวจสอบว่าเนื้อหาของข้อมูลไม่เสียหายหรือถูกแก้ไข รวมตรรกะเพื่อจัดการไฟล์ที่ไม่ได้อยู่ในรูปแบบที่เสถียรแล้ว

ข้อมูลโค้ดต่อไปนี้มีตัวอย่างโปรแกรมตรวจสอบแฮช

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 MB ให้ใช้ getExternalCacheDir() สำหรับแคชขนาดไม่เกิน 1 MB ให้ใช้ getCacheDir() ทั้ง 2 วิธีจะให้ออบเจ็กต์ 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 (API ระดับ 29) หรือต่ำกว่าและมีสิทธิ์ WRITE_EXTERNAL_STORAGE จึงเข้าถึงเนื้อหาในแคชนี้ได้

ข้อมูลที่เกี่ยวข้อง: ภาพรวมพื้นที่เก็บข้อมูลและไฟล์

ใช้ SharedPreferences ในโหมดส่วนตัว

เมื่อใช้ getSharedPreferences() เพื่อสร้างหรือเข้าถึงออบเจ็กต์ SharedPreferences ของแอป ให้ใช้ MODE_PRIVATE วิธีนี้จะทำให้มีเพียงแอปของคุณเท่านั้นที่เข้าถึงข้อมูลภายในไฟล์ค่ากำหนดที่แชร์ได้

หากต้องการแชร์ข้อมูลในแอปต่างๆ อย่าใช้ออบเจ็กต์ SharedPreferences แต่ให้ทำตามขั้นตอนเพื่อแชร์ข้อมูลอย่างปลอดภัยในแอปต่างๆ

ไลบรารีความปลอดภัยยังมีคลาส EncryptedSharedPreferences ซึ่งรวมคลาส SharedPreferences และเข้ารหัสคีย์และค่าโดยอัตโนมัติ

ข้อมูลที่เกี่ยวข้อง:

อัปเดตบริการและทรัพยากรที่เกี่ยวข้องอยู่เสมอ

แอปส่วนใหญ่ใช้ไลบรารีภายนอกและข้อมูลระบบของอุปกรณ์เพื่อทำงานเฉพาะทาง การอัปเดตทรัพยากร Dependency ของแอปให้เป็นเวอร์ชันล่าสุดอยู่เสมอจะทำให้จุดการสื่อสารเหล่านี้ปลอดภัยยิ่งขึ้น

ตรวจสอบผู้ให้บริการรักษาความปลอดภัยของบริการ Google Play

หมายเหตุ: ส่วนนี้ใช้กับแอปที่กำหนดเป้าหมายไปยังอุปกรณ์ที่ติดตั้งบริการ Google Play เท่านั้น

หากแอปใช้บริการ Google Play โปรดตรวจสอบว่าได้อัปเดตบริการดังกล่าวในอุปกรณ์ที่ติดตั้งแอปแล้ว ดำเนินการตรวจสอบแบบไม่พร้อมกันจากเธรด UI หากอุปกรณ์ไม่อัปเดต จะทำให้ระบบแสดงข้อผิดพลาดในการให้สิทธิ์

หากต้องการตรวจสอบว่าบริการ Google Play เป็นเวอร์ชันล่าสุดในอุปกรณ์ที่ติดตั้งแอปหรือไม่ ให้ทำตามขั้นตอนในคู่มือเกี่ยวกับการอัปเดตผู้ให้บริการรักษาความปลอดภัยเพื่อป้องกันการแสวงหาประโยชน์จาก SSL

ข้อมูลที่เกี่ยวข้อง:

อัปเดตทรัพยากร Dependency ทั้งหมดของแอป

ก่อนทำให้แอปใช้งานได้ ให้ตรวจสอบว่าไลบรารี, SDK และการอ้างอิงอื่นๆ ทั้งหมดเป็นเวอร์ชันล่าสุด โดยทำดังนี้

  • สําหรับทรัพยากรของบุคคลที่หนึ่ง เช่น Android SDK ให้ใช้เครื่องมืออัปเดตที่มีอยู่ใน Android Studio เช่น SDK Manager
  • สําหรับทรัพยากร Dependency ของบุคคลที่สาม ให้ตรวจสอบเว็บไซต์ของไลบรารีที่แอปของคุณใช้ และติดตั้งการอัปเดตและแพตช์ความปลอดภัยที่มีอยู่

ข้อมูลที่เกี่ยวข้อง: เพิ่มข้อกําหนดของบิลด์

ข้อมูลเพิ่มเติม

ดูแหล่งข้อมูลต่อไปนี้เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีทำให้แอปปลอดภัยยิ่งขึ้น