การทำให้แอปมีความปลอดภัยมากขึ้นจะช่วยรักษาความไว้วางใจของผู้ใช้และความสมบูรณ์ของอุปกรณ์
หน้านี้จะแสดงแนวทางปฏิบัติแนะนำหลายอย่างซึ่งมีผลกระทบเชิงบวกอย่างมากต่อความปลอดภัยของแอป
บังคับใช้การสื่อสารที่ปลอดภัย
เมื่อปกป้องข้อมูลที่คุณแลกเปลี่ยนระหว่างแอปกับแอปอื่นๆ หรือระหว่างแอปกับเว็บไซต์ คุณจะปรับปรุงความเสถียรของแอปและ ปกป้องข้อมูลที่คุณส่งและรับ
ปกป้องการสื่อสารระหว่างแอป
หากต้องการสื่อสารระหว่างแอปอย่างปลอดภัยยิ่งขึ้น ให้ใช้ 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 ใหม่หรือ 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>
ข้อมูลที่เกี่ยวข้อง: การกำหนดค่าความปลอดภัย ของเครือข่าย
สร้างผู้จัดการความน่าเชื่อถือของคุณเอง
เครื่องมือตรวจสอบ 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 ใด URI หนึ่งแล้ว แอปที่เรียกใช้จะได้รับสิทธิ์เข้าถึงทรัพยากรที่เลือก
ข้อมูลที่เกี่ยวข้อง:
แชร์ข้อมูลอย่างปลอดภัยในแอปต่างๆ
ทําตามแนวทางปฏิบัติแนะนําต่อไปนี้เพื่อแชร์เนื้อหาของแอปกับแอปอื่นๆ อย่างปลอดภัยยิ่งขึ้น
- บังคับใช้สิทธิ์แบบอ่านอย่างเดียวหรือเขียนอย่างเดียวตามต้องการ
-
ให้สิทธิ์เข้าถึงข้อมูลแก่ลูกค้าแบบครั้งเดียวโดยใช้
FLAG_GRANT_READ_URI_PERMISSION
และFLAG_GRANT_WRITE_URI_PERMISSION
- เมื่อแชร์ข้อมูล ให้ใช้ URI ของ
content://
ไม่ใช่ URI ของfile://
อินสแตนซ์ของFileProvider
จะดำเนินการนี้ให้คุณ
ข้อมูลโค้ดต่อไปนี้แสดงวิธีใช้แฟล็กการให้สิทธิ์ URI และสิทธิ์ของ Content Provider เพื่อแสดงไฟล์ 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. }
ข้อมูลที่เกี่ยวข้อง:
จัดเก็บข้อมูลในที่เก็บข้อมูลภายนอกตามกรณีการใช้งาน
ใช้พื้นที่เก็บข้อมูลภายนอกสำหรับไฟล์ขนาดใหญ่ที่ไม่ละเอียดอ่อนซึ่งเฉพาะเจาะจงสำหรับ แอปของคุณ รวมถึงไฟล์ที่แอปของคุณแชร์กับแอปอื่นๆ 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 ได้
หากต้องการแชร์ข้อมูลในแอปต่างๆ อย่าใช้
SharedPreferences
ออบเจ็กต์ แต่ให้ทำตามขั้นตอนเพื่อแชร์
ข้อมูลอย่างปลอดภัยในแอปแทน
ข้อมูลที่เกี่ยวข้อง:
อัปเดตบริการและการอ้างอิงอยู่เสมอ
แอปส่วนใหญ่ใช้ไลบรารีภายนอกและข้อมูลระบบของอุปกรณ์เพื่อทำงานเฉพาะทางให้เสร็จสมบูรณ์ การอัปเดตทรัพยากร Dependency ของแอปให้เป็นเวอร์ชันล่าสุดจะช่วยให้จุดสื่อสารเหล่านี้มีความปลอดภัยมากขึ้น
ตรวจสอบผู้ให้บริการด้านความปลอดภัยของบริการ Google Play
หมายเหตุ: ส่วนนี้ใช้กับแอปที่กำหนดเป้าหมายไปยังอุปกรณ์ที่ติดตั้งบริการ Google Play เท่านั้น
หากแอปใช้บริการ Google Play โปรดตรวจสอบว่าแอปได้รับการอัปเดตในอุปกรณ์ที่ติดตั้งแอป ทำการตรวจสอบแบบไม่พร้อมกัน นอกเธรด UI หากอุปกรณ์ไม่อัปเดต ให้ทริกเกอร์ ข้อผิดพลาดในการให้สิทธิ์
หากต้องการดูว่าบริการ Google Play ในอุปกรณ์ที่ติดตั้งแอปเป็นเวอร์ชันล่าสุดหรือไม่ ให้ทำตามขั้นตอนในคำแนะนำเกี่ยวกับการอัปเดตผู้ให้บริการรักษาความปลอดภัยเพื่อป้องกันการแสวงหาประโยชน์จาก SSL
ข้อมูลที่เกี่ยวข้อง:
อัปเดตทรัพยากร Dependency ของแอปทั้งหมด
ก่อนที่จะติดตั้งใช้งานแอป โปรดตรวจสอบว่าไลบรารี, SDK และ การอ้างอิงอื่นๆ ทั้งหมดเป็นเวอร์ชันล่าสุดโดยทำดังนี้
- สําหรับการขึ้นต่อกันของบุคคลที่หนึ่ง เช่น Android SDK ให้ใช้เครื่องมืออัปเดต ที่อยู่ใน Android Studio เช่น SDK Manager
- สำหรับทรัพยากร Dependency ของบุคคลที่สาม ให้ตรวจสอบเว็บไซต์ของไลบรารีที่แอปของคุณใช้ แล้วติดตั้งการอัปเดตและแพตช์ด้านความปลอดภัยที่มี
ข้อมูลที่เกี่ยวข้อง: เพิ่ม การอ้างอิงในการสร้าง
ข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีทําให้แอปปลอดภัยยิ่งขึ้นได้จากแหล่งข้อมูลต่อไปนี้
- รายการตรวจสอบความปลอดภัย คุณภาพแอปหลัก
- โปรแกรมปรับปรุงความปลอดภัยของแอป
- ช่อง Android Developers บน YouTube
- Android การยืนยันที่ได้รับการปกป้อง: ยกระดับความปลอดภัยในการทำธุรกรรม