การทำให้แอปปลอดภัยยิ่งขึ้นช่วยรักษาความเชื่อมั่นของผู้ใช้และความสมบูรณ์ของอุปกรณ์
หน้านี้แสดงแนวทางปฏิบัติแนะนำหลายประการที่ส่งผลเชิงบวกอย่างมากต่อความปลอดภัยของแอป
บังคับใช้การสื่อสารที่ปลอดภัย
การรักษาความปลอดภัยของข้อมูลที่แลกเปลี่ยนระหว่างแอปกับแอปอื่นๆ หรือระหว่างแอปกับเว็บไซต์จะช่วยปรับปรุงความเสถียรของแอปและปกป้องข้อมูลที่ส่งและรับ
รักษาความปลอดภัยในการสื่อสารระหว่างแอป
หากต้องการสื่อสารระหว่างแอปอย่างปลอดภัยยิ่งขึ้น ให้ใช้ 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) ที่รู้จักและเชื่อถือ
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 ใหม่หรือที่กำหนดเอง คุณจะประกาศการตั้งค่าความปลอดภัยของเครือข่ายในไฟล์การกำหนดค่าได้ ขั้นตอนนี้ช่วยให้คุณสร้างการกำหนดค่าได้โดยไม่ต้องแก้ไขโค้ดแอป
หากต้องการเพิ่มไฟล์การกําหนดค่าความปลอดภัยของเครือข่ายลงในแอป ให้ทําตามขั้นตอนต่อไปนี้
- ประกาศการกําหนดค่าในไฟล์ Manifest ของแอป
-
เพิ่มไฟล์ทรัพยากร 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>
<manifest ... > <application android:networkSecurityConfig="@xml/network_security_config" ... > <!-- Place child elements of <application> element here. --> </application> </manifest>
ข้อมูลที่เกี่ยวข้อง: การกำหนดค่า ความปลอดภัยของเครือข่าย
สร้าง Trust Manager ของคุณเอง
เครื่องมือตรวจสอบ TLS ไม่ควรยอมรับใบรับรองทั้งหมด คุณอาจต้องตั้งค่าเครื่องมือจัดการความน่าเชื่อถือและจัดการคำเตือน TLS ทั้งหมดที่เกิดขึ้นหากเงื่อนไขข้อใดข้อหนึ่งต่อไปนี้ตรงกับกรณีการใช้งานของคุณ
- คุณกำลังสื่อสารกับเว็บเซิร์ฟเวอร์ที่มีใบรับรองที่ลงชื่อโดย 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_GRANT_READ_URI_PERMISSION
และFLAG_GRANT_WRITE_URI_PERMISSION
- เมื่อแชร์ข้อมูล ให้ใช้ URI
content://
ไม่ใช่ URIfile://
อินสแตนซ์ของ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 ของคุณ
- ไฟล์สื่อ: หากต้องการจัดเก็บและเข้าถึงรูปภาพ ไฟล์เสียง และวิดีโอที่แชร์ระหว่างแอป ให้ใช้ MediaStore API
- ไฟล์อื่นๆ: หากต้องการจัดเก็บและเข้าถึงไฟล์ที่แชร์ประเภทอื่นๆ ซึ่งรวมถึงไฟล์ที่ดาวน์โหลด โปรดใช้เฟรมเวิร์กการเข้าถึงพื้นที่เก็บข้อมูล
ตรวจสอบปริมาณพื้นที่เก็บข้อมูลที่เหลืออยู่
หากแอปของคุณโต้ตอบกับอุปกรณ์จัดเก็บข้อมูลภายนอกแบบถอดได้ โปรดทราบว่าผู้ใช้อาจนำอุปกรณ์จัดเก็บข้อมูลออกขณะที่แอปพยายามเข้าถึง ใส่ตรรกะเพื่อยืนยันว่าอุปกรณ์จัดเก็บข้อมูลพร้อมใช้งาน
ตรวจสอบความถูกต้องของข้อมูล
หากแอปใช้ข้อมูลจากพื้นที่เก็บข้อมูลภายนอก โปรดตรวจสอบว่าเนื้อหาของข้อมูลไม่มีความเสียหายหรือมีการแก้ไข รวมตรรกะเพื่อจัดการไฟล์ที่ไม่ได้อยู่ในรูปแบบที่เสถียรแล้ว
ข้อมูลโค้ดต่อไปนี้มีตัวอย่างโปรแกรมตรวจสอบแฮช
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 ให้เป็นปัจจุบันอยู่เสมอ
แอปส่วนใหญ่ใช้ไลบรารีภายนอกและข้อมูลระบบของอุปกรณ์เพื่อทำงานเฉพาะทาง การอัปเดตทรัพยากร Dependency ของแอปอยู่เสมอจะทำให้จุดสื่อสารเหล่านี้ปลอดภัยยิ่งขึ้น
ตรวจสอบผู้ให้บริการรักษาความปลอดภัยของบริการ Google Play
หมายเหตุ: ส่วนนี้ใช้กับแอปที่กำหนดเป้าหมายไปยังอุปกรณ์ที่ติดตั้งบริการ Google Play เท่านั้น
หากแอปใช้บริการ Google Play โปรดตรวจสอบว่าได้อัปเดตบริการดังกล่าวในอุปกรณ์ที่ติดตั้งแอปแล้ว ดำเนินการตรวจสอบแบบไม่พร้อมกันจากเธรด UI หากอุปกรณ์ไม่ใช่เวอร์ชันล่าสุด ให้ทริกเกอร์ข้อผิดพลาดในการให้สิทธิ์
หากต้องการตรวจสอบว่าบริการ Google Play ในอุปกรณ์ที่ติดตั้งแอปเป็นเวอร์ชันล่าสุดหรือไม่ ให้ทำตามขั้นตอนในคู่มือเกี่ยวกับการอัปเดตผู้ให้บริการรักษาความปลอดภัยเพื่อป้องกันการแสวงหาประโยชน์จาก SSL
ข้อมูลที่เกี่ยวข้อง:
อัปเดตทรัพยากร Dependency ทั้งหมดของแอป
ก่อนทำให้แอปใช้งานได้ ให้ตรวจสอบว่าไลบรารี, SDK และการอ้างอิงอื่นๆ ทั้งหมดเป็นเวอร์ชันล่าสุด โดยทำดังนี้
- สําหรับทรัพยากรของบุคคลที่หนึ่ง เช่น Android SDK ให้ใช้เครื่องมืออัปเดตที่มีอยู่ใน Android Studio เช่น SDK Manager
- สำหรับทรัพยากร Dependency ของบุคคลที่สาม ให้ตรวจสอบเว็บไซต์ของไลบรารีที่แอปใช้ จากนั้นติดตั้งการอัปเดตและแพตช์ความปลอดภัยที่มี
ข้อมูลที่เกี่ยวข้อง: เพิ่มข้อกําหนดของการสร้าง
ข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีทำให้แอปปลอดภัยยิ่งขึ้นได้ที่แหล่งข้อมูลต่อไปนี้
- รายการตรวจสอบความปลอดภัยเพื่อคุณภาพแอปหลัก
- โปรแกรมปรับปรุงความปลอดภัยของแอป
- ช่องนักพัฒนาแอป Android บน YouTube
- Codelab การกำหนดค่าความปลอดภัยของเครือข่าย Android
- การยืนยันการป้องกันของ Android: ยกระดับความปลอดภัยของธุรกรรมขึ้นไปอีกขั้น