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

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

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

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

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

ปกป้องการสื่อสารระหว่างแอป

หากต้องการสื่อสารระหว่างแอปอย่างปลอดภัยยิ่งขึ้น ให้ใช้ 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 แต่ให้ทำตามขั้นตอนเพื่อแชร์ข้อมูลอย่างปลอดภัยในแอปต่างๆ

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

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

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

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

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

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

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

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

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

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

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

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

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

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