การทำให้แอปมีความปลอดภัยมากขึ้นจะช่วยรักษาความเชื่อมั่นของผู้ใช้และความสมบูรณ์ของอุปกรณ์
หน้านี้แสดงแนวทางปฏิบัติแนะนำหลายประการซึ่งมีผลกระทบเชิงบวกอย่างมากต่อความปลอดภัยของแอป
บังคับใช้การสื่อสารที่ปลอดภัย
เมื่อปกป้องข้อมูลที่คุณแลกเปลี่ยนระหว่างแอปของคุณกับแอปอื่นๆ หรือระหว่างแอปของคุณกับเว็บไซต์ คุณจะปรับปรุงความเสถียรของแอปและ ปกป้องข้อมูลที่คุณส่งและรับ
ปกป้องการสื่อสารระหว่างแอป
หากต้องการสื่อสารระหว่างแอปอย่างปลอดภัยยิ่งขึ้น ให้ใช้ 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."
<b>val chooser = resources.getString(R.string.chooser_title).let { title ->
Intent.createChooser(intent, title)
}
startActivity(chooser)</b>
} 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."
<b>String title = getResources().getString(R.string.chooser_title);
Intent chooser = Intent.createChooser(intent, title);
startActivity(chooser);</b>
} 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="androidx.core.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 เพื่อนำผู้ใช้ไปยังแอปรายชื่อติดต่อแทนการขอสิทธิ์ 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 และสิทธิ์ของผู้ให้บริการเนื้อหาเพื่อแสดงไฟล์ 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("<b>content:</b>//com.example/personal-info.pdf")
// This flag gives the started app read access to the file.
<b>addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)</b>
}.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("<b>content://</b>com.example/personal-info.pdf"));
// This flag gives the started app read access to the file.
<b>viewPdfIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);</b>
// 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 อย่างใดอย่างหนึ่งต่อไปนี้ ขึ้นอยู่กับกรณีการใช้งาน
- ไฟล์สื่อ: หากต้องการจัดเก็บและเข้าถึงรูปภาพ ไฟล์เสียง และวิดีโอที่แชร์ระหว่างแอป ให้ใช้ MediaStore API
- ไฟล์อื่นๆ: หากต้องการจัดเก็บและเข้าถึงไฟล์ที่แชร์ประเภทอื่นๆ รวมถึงไฟล์ที่ดาวน์โหลด ให้ใช้เฟรมเวิร์กการเข้าถึงพื้นที่เก็บข้อมูล
ตรวจสอบความพร้อมใช้งานของวอลุ่มพื้นที่เก็บข้อมูล
หากแอปของคุณโต้ตอบกับอุปกรณ์จัดเก็บข้อมูลภายนอกแบบถอดออกได้ โปรดทราบว่า ผู้ใช้อาจนำอุปกรณ์จัดเก็บข้อมูลออกในขณะที่แอปพยายามเข้าถึง อุปกรณ์ดังกล่าว ใส่ตรรกะเพื่อยืนยันว่าอุปกรณ์จัดเก็บข้อมูล พร้อมใช้งาน
ตรวจสอบความถูกต้องของข้อมูล
หากแอปใช้ข้อมูลจากที่เก็บข้อมูลภายนอก โปรดตรวจสอบว่าเนื้อหาของข้อมูลไม่เสียหายหรือมีการแก้ไข รวมตรรกะ เพื่อจัดการไฟล์ที่ไม่ได้อยู่ในรูปแบบที่เสถียรอีกต่อไป
ข้อมูลโค้ดต่อไปนี้มีตัวอย่างเครื่องมือยืนยันแฮช
Kotlin
val hash = calculateHash(stream)
// Store "expectedHash" in a secure location.
if (hash == <var>expectedHash</var>) {
// 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, <var>expectedHash</var>)) {
// 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 ให้ใช้
getCacheDir()
สำหรับแคชที่มีขนาดใหญ่กว่า 1 MB ให้ใช้
getExternalCacheDir()
ทั้ง 2 วิธีจะให้
ออบเจ็กต์ File แก่คุณ ซึ่งมี
ข้อมูลที่แคชไว้ของแอป
แม้ว่าไดเรกทอรีแคชภายใน (getCacheDir())
จะเป็นแบบส่วนตัวสำหรับแอปของคุณ แต่ไดเรกทอรีแคชภายนอกจะไม่เป็นแบบส่วนตัว
ข้อมูลโค้ดต่อไปนี้แสดงวิธีแคชไฟล์ที่แอปดาวน์โหลดล่าสุด
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
- สำหรับทรัพยากร Dependency ของบุคคลที่สาม ให้ตรวจสอบเว็บไซต์ของไลบรารีที่แอปใช้ และติดตั้งการอัปเดตและแพตช์ด้านความปลอดภัยที่มี
ข้อมูลที่เกี่ยวข้อง: เพิ่ม ทรัพยากร Dependency ของบิลด์
ข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีทําให้แอปปลอดภัยยิ่งขึ้นได้ที่แหล่งข้อมูลต่อไปนี้
- รายการตรวจสอบความปลอดภัย คุณภาพแอปหลัก
- โปรแกรมปรับปรุงความปลอดภัยของแอป
- ช่อง Android Developers บน YouTube
- Android การยืนยันที่ได้รับการปกป้อง: ยกระดับความปลอดภัยในการทำธุรกรรม