การทำให้แอปมีความปลอดภัยมากขึ้นจะช่วยรักษาความไว้วางใจของผู้ใช้และความสมบูรณ์ของอุปกรณ์
หน้านี้แสดงแนวทางปฏิบัติแนะนำหลายข้อที่จะส่งผลดีอย่างมากต่อความปลอดภัยของแอป
บังคับใช้การสื่อสารที่ปลอดภัย
เมื่อคุณปกป้องข้อมูลที่แลกเปลี่ยนระหว่างแอปกับแอปอื่นๆ หรือระหว่างแอปกับเว็บไซต์ คุณจะปรับปรุงความเสถียรของแอปและปกป้องข้อมูลที่คุณส่งและรับ
ปกป้องการสื่อสารระหว่างแอป
หากต้องการสื่อสารระหว่างแอปอย่างปลอดภัยมากขึ้น ให้ใช้ 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="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 หนึ่งๆ แล้ว แอปที่เรียกจะได้รับสิทธิ์เข้าถึงทรัพยากรที่เลือก
ข้อมูลที่เกี่ยวข้อง:
แชร์ข้อมูลระหว่างแอปอย่างปลอดภัย
ทำตามแนวทางปฏิบัติแนะนำต่อไปนี้เพื่อแชร์เนื้อหาของแอปกับแอปอื่นๆ อย่างปลอดภัยยิ่งขึ้น
- บังคับใช้สิทธิ์แบบอ่านอย่างเดียวหรือเขียนอย่างเดียวตามความจำเป็น
-
ให้สิทธิ์เข้าถึงข้อมูลแบบครั้งเดียวแก่ไคลเอ็นต์โดยใช้แฟล็ก
FLAG_GRANT_READ_URI_PERMISSIONและFLAG_GRANT_WRITE_URI_PERMISSION - เมื่อแชร์ข้อมูล ให้ใช้ URI
content://ไม่ใช่ URIfile://อินสแตนซ์ของ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 อย่างใดอย่างหนึ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับกรณีการใช้งาน
- ไฟล์สื่อ: หากต้องการจัดเก็บและเข้าถึงรูปภาพ ไฟล์เสียง และวิดีโอที่ แชร์ระหว่างแอป ให้ใช้ Media Store API
- ไฟล์อื่นๆ: หากต้องการจัดเก็บและเข้าถึงไฟล์ที่แชร์ประเภทอื่นๆ รวมถึง ไฟล์ที่ดาวน์โหลด ให้ใช้ Storage Access Framework
ตรวจสอบความพร้อมใช้งานของวอลุ่มที่จัดเก็บข้อมูล
หากแอปโต้ตอบกับอุปกรณ์พื้นที่เก็บข้อมูลภายนอกแบบถอดออกได้ โปรดทราบว่าผู้ใช้อาจนำอุปกรณ์พื้นที่เก็บข้อมูลออกขณะที่แอปพยายามเข้าถึง ดังนั้น ให้ใส่ตรรกะเพื่อยืนยันว่าอุปกรณ์พื้นที่เก็บข้อมูล พร้อมใช้งาน
ตรวจสอบความถูกต้องของข้อมูล
หากแอปใช้ข้อมูลจากพื้นที่เก็บข้อมูลภายนอก ให้ตรวจสอบว่าเนื้อหาของข้อมูลไม่เสียหายหรือมีการแก้ไข ใส่ตรรกะเพื่อจัดการไฟล์ที่ไม่ได้อยู่ในรูปแบบที่เสถียรอีกต่อไป
ข้อมูลโค้ดต่อไปนี้มีตัวอย่างเครื่องมือตรวจสอบแฮช
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 แต่ให้ทำตามขั้นตอนเพื่อแชร์
ข้อมูลระหว่างแอปอย่างปลอดภัยแทน
ข้อมูลที่เกี่ยวข้อง:
อัปเดตบริการและการอ้างอิงทั้งหมด
แอปส่วนใหญ่ใช้ไลบรารีภายนอกและข้อมูลระบบของอุปกรณ์เพื่อทำงานเฉพาะทางให้เสร็จสมบูรณ์ การอัปเดตการอ้างอิงของแอปจะทำให้จุดสื่อสารเหล่านี้มีความปลอดภัยมากขึ้น
ตรวจสอบผู้ให้บริการรักษาความปลอดภัยของบริการ Google Play
หมายเหตุ: ส่วนนี้ใช้ได้กับแอปที่กำหนดเป้าหมายเป็นอุปกรณ์ ที่มีการติดตั้งบริการ Google Play เท่านั้น
หากแอปใช้บริการ Google Play ให้ตรวจสอบว่าบริการดังกล่าวได้รับการอัปเดตในอุปกรณ์ที่ติดตั้งแอป ทำการตรวจสอบแบบไม่พร้อมกันในเธรดที่ไม่ใช่ UI หากอุปกรณ์ไม่ได้เป็นเวอร์ชันล่าสุด ให้ทริกเกอร์ข้อผิดพลาดการให้สิทธิ์
ข้อมูลที่เกี่ยวข้อง:
อัปเดตการอ้างอิงทั้งหมดของแอป
ก่อนที่จะติดตั้งใช้งานแอป โปรดตรวจสอบว่าไลบรารี, SDK และการอ้างอิงอื่นๆ ทั้งหมดเป็นเวอร์ชันล่าสุด
- สำหรับการอ้างอิงของบุคคลที่หนึ่ง เช่น Android SDK ให้ใช้เครื่องมืออัปเดตที่พบใน Android Studio เช่น เครื่องมือจัดการ SDK
- สำหรับการอ้างอิงของบุคคลที่สาม ให้ตรวจสอบเว็บไซต์ของไลบรารีที่แอปใช้ และติดตั้งการอัปเดตและแพตช์ความปลอดภัยที่มี
ข้อมูลที่เกี่ยวข้อง: เพิ่ม การอ้างอิงบิลด์
ข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีทำให้แอปมีความปลอดภัยมากขึ้นได้จากแหล่งข้อมูลต่อไปนี้
- รายการตรวจสอบความปลอดภัยของคุณภาพแอปที่สำคัญ
- โปรแกรมปรับปรุงความปลอดภัยของแอป
- ช่อง Android Developers บน YouTube
- การยืนยันที่ได้รับการปกป้องของ Android: ยกระดับความปลอดภัยของธุรกรรม